Spring Enable 是什么?

本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

简述

Spring 提供了一系列名称以 *Enable * 开头的注释,这些注释本质上激活对应的 Spring 管理的功能。

一个很好的例子是 EnableWebMvc,它引入了在基于 Spring 的应用程序中支持 MVC 流所需的所有 Bean。另一个很好的例子是 EnableAsync ,用于激活 Bean 以支持基于 Spring 的应用程序中的异步功能。

分类

下面表格列举部分 Enable 开头的注解

序号 注解名 作用
0 EnableScheduling 开启计划任务的支持
1 EnableAsync 开启异步方法的支持
2 EnableAspectJAutoProxy 开启对 AspectJ 代理的支持
3 EnableTransactionManagement 开启对事务的支持
4 EnableCaching 开启对注解式缓存的支持
5 EnableWebMvc 开启对Spring MVC的支持
6 EnableWebSocket 开启网络套接字请求的处理

核心类

  • @Import
  • @Configuration
  • ImportSelector
  • ImportBeanDefinitionRegistrar

实现

目前 Spring 框架实现 Enable 模式主要有三种:

  • 直接导入确定配置类:@Import + @Configuration 组合
    • 明确知道引入哪个配置类
  • 依据条件选择配置类: @Import + ImportSelector 组合
    • 不明确知道引入哪个配置类,或者需要一定逻辑进行判断引入哪个配置类
  • 自定义注册选择配置类:@Import + ImportBeanDefinitionRegistrar 组合
    • 不明确知道引入哪个配置类,或者以后会增加配置类

当然,还可以进行混合组合:

@Import + ImportSelector + ImportBeanDefinitionRegistrar 组合

原理

@Import 的作用

definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.

导入的 @Configuration 类中声明的定义应使用 @Autowired 注入进行访问。 Bean 本身可以自动装配,也可以配置类实例
声明可以自动装配 bean 。后一种方法允许显式,IDE友好的通过 @Configuration 类方法之间的注解。

@Import 注解用来导入一个或多个 class,这些类会注入到 Spring 容器中,或者配置类,配置类里面定义的 bean 都会被 Spring 容器托管,成为 Spring 上下文的一部分 。

@Configuration和@Import
@EnableScheduling

@EnableScheduling 是这两个组合实现的实例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {

}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}
}
ImportSelector和@Import
@EnableAsync

@EnableAsync 是这两个组合实现的实例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
...
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

	/**
	 * 
	 * 返回 ProxyAsyncConfiguration 或者 AspectJAsyncConfiguration 作为 EnableAsync#mode() 的值
	 * 根据 adviceMode 参数的值进行判断
	 */
	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] { ProxyAsyncConfiguration.class.getName() };
			case ASPECTJ:
				return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
			default:
				return null;
		}
	}

}
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {

	public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";

	protected String getAdviceModeAttributeName() {
		return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
	}

	@Override
	public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
		Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
		Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");

		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
		if (attributes == null) {
			throw new IllegalArgumentException(String.format(
				"@%s is not present on importing class '%s' as expected",
				annType.getSimpleName(), importingClassMetadata.getClassName()));
		}

		AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
		String[] imports = selectImports(adviceMode);
		if (imports == null) {
			throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode));
		}
		return imports;
	}

	@Nullable
	protected abstract String[] selectImports(AdviceMode adviceMode);

}

这里可以看到 AsyncConfigurationSelector 继承 AdviceModeImportSelectorAdviceModeImportSelector 实现 ImportSelector

public interface ImportSelector {
	/**
	  * 根据导入的 Configuration 类的 AnnotationMetadata 选择并返回应导入的类的名称
	  * @param AnnotationMetadata:用来获得当前配置类上的注解
	  * @return class的全类名的字符串数组
	  * /
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

Spring 会把实现 ImportSelector 接口的类中的 SelectImport 方法返回的值注入到 Spring 容器中。
例如,在 @EnableAsync 注解的 Bean 中 ,将会把 ProxyAsyncConfiguration 或者 AspectJAsyncConfiguration 注入到 Spring 容器中。

ImportBeanDefinitionRegistrar和@Import
@EnableAspectJAutoProxy

@EnableAspectJAutoProxy 是这两个组合实现的实例

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	boolean proxyTargetClass() default false;
	
	boolean exposeProxy() default false;

}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * 根据导入时启用AspectJ自动代理属性的值注册,升级和配置AspectJ自动代理创建器配置类。
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}

这里可以看到 AspectJAutoProxyRegistrar 实现 ImportBeanDefinitionRegistrar 接口。

public interface ImportBeanDefinitionRegistrar {

	/**
	 * 根据导入的Configuration类的给定配置类上的注解,注册Bean。请注意,由于与Configuration类相关的生命周期限             			
	 * 制,BeanDefinitionRegistryPostProcessor 在运行时自动添加Bean到已有的配置类。
	 * @param importingClassMetadata 获得当前配置类上的注解
	 * @param registry 注册Bean
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

下面是 AspectJAutoProxyRegistrar 实现通过 @EnableAspectJAutoProxy 注解中的信息进行判断的逻辑,这里可以参照进行相对应的业务改写。

if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
	AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
	AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
@Import和ImportSelector和ImportBeanDefinitionRegistrar
@EnableConfigurationProperties

@EnableAspectJAutoProxy 是这三个组合实现的实例

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EnableConfigurationPropertiesImportSelector.class})
public @interface EnableConfigurationProperties {
    Class<?>[] value() default {};
}
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
    private static final String[] IMPORTS = new String[]{EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};

    EnableConfigurationPropertiesImportSelector() {
    }

    public String[] selectImports(AnnotationMetadata metadata) {
        return IMPORTS;
    }

    public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
        public ConfigurationPropertiesBeanRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            this.getTypes(metadata).forEach((type) -> {
                this.register(registry, (ConfigurableListableBeanFactory)registry, type);
            });
        }
		
		...
    }
}

这里可以看到 EnableConfigurationPropertiesImportSelector 实现 ImportSelector 接口,同时有个内部静态类 ConfigurationPropertiesBeanRegistrar 实现 ImportBeanDefinitionRegistrar 接口,然后通过 ImportSelector#selectImports 方法将 ConfigurationPropertiesBeanRegistrar 注入。

例子

@Import和@Configuration

EnableSomeBeans 注解

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 开启自动注入一些Beans注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfiguration.class)
public @interface EnableSomeBeans {
}

SomeBeanConfiguration.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 注入一些Bean的配置
 */
@Configuration
public class SomeBeanConfiguration {

    @Bean
    public String beanA() {
        return "beanA";
    }

    @Bean
    public String beanB() {
        return "beanA";
    }
}
ImportSelector和@Import

定义 EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationSelector.class)
public @interface EnableSomeBeansSelector {
	String criteria() default "default";
}

实现 SomeBeanConfigurationSelector 类,此类实现了 ImportSelector 接口

blic class SomeBeanConfigurationSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        AnnotationAttributes attributes =
                AnnotationAttributes.fromMap(
                        importingClassMetadata.getAnnotationAttributes
                                (EnableSomeBeansSelector.class.getName(), false));
        String criteria = attributes.getString("criteria");
        if (criteria.equals("default")) {
            return new String[]{"com.enable.selectorDemo.SomeBeanConfigurationDefault"};
        }else {
            return new String[]{"com.enable.selectorDemo.SomeBeanConfigurationTypeA"};
        }
    }
}

@Configuration
class SomeBeanConfigurationTypeA {

    @Bean
    public String aBean() {
        return "TypeA";
    }

}

@Configuration
class SomeBeanConfigurationDefault {

    @Bean
    public String aBean() {
        return "Default";
    }
}
ImportBeanDefinitionRegistrar和@Import

定义 EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationRegistrar.class)
public @interface EnableSomeBeansSelector {
    String criteria() default "default";
}

实现 SomeBeanConfigurationRegistrar

public class SomeBeanConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取EnableSomeBeansSelector注解的所有属性的value
        AnnotationAttributes attributes =
                AnnotationAttributes.fromMap(
                        importingClassMetadata.getAnnotationAttributes
                                (EnableSomeBeansSelector.class.getName(), false));
        //获取criteria属性的value
        String criteria = attributes.getString("criteria");
        if (criteria.equals("default")) {
            this.registerConfigurationDefault(registry);
        }else {
            this.registerConfigurationTypeA(registry);
        }
    }

    private void registerConfigurationDefault(BeanDefinitionRegistry registry) {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(SomeBeanConfigurationDefault.class);
        definition.setRole(2);
        registry.registerBeanDefinition(SomeBeanConfigurationDefault.class.getName(), definition);
    }

    private void registerConfigurationTypeA(BeanDefinitionRegistry registry) {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(SomeBeanConfigurationTypeA.class);
        definition.setRole(2);
        registry.registerBeanDefinition(SomeBeanConfigurationTypeA.class.getName(), definition);
    }
}

class SomeBeanConfigurationTypeA {
}

class SomeBeanConfigurationDefault {
}

这里通过 GenericBeanDefinition 实现类的定义,BeanDefinitionRegistry#registerBeanDefinition 完成对类的注册。

@Import和ImportSelector和ImportBeanDefinitionRegistrar

EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationSelector.class)
public @interface EnableSomeBeansSelector {
    String criteria() default "default";
}

SomeBeanConfigurationSelector 混合模式

public class SomeBeanConfigurationSelector implements ImportSelector {
    private static final String[] IMPORTS = new String[]{com.enable.selectorAndRegistarDemo.SomeBeanConfigurationSelector.SomeBeanConfigurationBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};

    SomeBeanConfigurationSelector() {
    }

    public String[] selectImports(AnnotationMetadata metadata) {
        return IMPORTS;
    }

    public static class SomeBeanConfigurationBeanRegistrar implements ImportBeanDefinitionRegistrar {
        public SomeBeanConfigurationBeanRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            this.getTypes(metadata).forEach((type) -> {
                this.register(registry, (ConfigurableListableBeanFactory)registry, type);
            });
        }

        private List<Class<?>> getTypes(AnnotationMetadata metadata) {
            MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableSomeBeansSelector.class.getName(), false);
            return this.collectClasses(attributes != null ? (List)attributes.get("criteria") : Collections.emptyList());
        }

        private List<Class<?>> collectClasses(List<?> values) {
            return (List)values.stream().flatMap((value) -> {
                return Arrays.stream((Object[])((Object[])value));
            }).map((o) -> {
                return (Class)o;
            }).filter((type) -> {
                return Void.TYPE != type;
            }).collect(Collectors.toList());
        }

        private void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory, Class<?> type) {
            String name = this.getName(type);
            if (!this.containsBeanDefinition(beanFactory, name)) {
                this.registerBeanDefinition(registry, name, type);
            }

        }

        private String getName(Class<?> type) {
            ConfigurationProperties annotation = (ConfigurationProperties)AnnotationUtils.findAnnotation(type, ConfigurationProperties.class);
            String prefix = annotation != null ? annotation.prefix() : "";
            return StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName();
        }

        private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String name) {
            if (beanFactory.containsBeanDefinition(name)) {
                return true;
            } else {
                BeanFactory parent = beanFactory.getParentBeanFactory();
                return parent instanceof ConfigurableListableBeanFactory ? this.containsBeanDefinition((ConfigurableListableBeanFactory)parent, name) : false;
            }
        }

        private void registerBeanDefinition(BeanDefinitionRegistry registry, String name, Class<?> type) {
            this.assertHasAnnotation(type);
            GenericBeanDefinition definition = new GenericBeanDefinition();
            definition.setBeanClass(type);
            registry.registerBeanDefinition(name, definition);
        }

        private void assertHasAnnotation(Class<?> type) {
            Assert.notNull(AnnotationUtils.findAnnotation(type, ConfigurationProperties.class), () -> {
                return "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on  '" + type.getName() + "'.";
            });
        }
    }
}
自定义注解使用方式
@EnableSomeBeansSelector(criteria = "TypeA")
public class Application {
}

注解在对应的 bean 上就可以了。

结论

通过本文内容,研究了一些 @Enable Spring 注解原理,和如何手动创建自定义的 @Enable 注解, 开发人员在实际开发过程中,不需要手动创建 Enable 注解,而是使用 @Configuration 注解和 Spring Bean 配置文件搭建应用程序,这种简单的机制。

代码地址:github

参考资料

Spring-Boot之@Enable*注解的工作原理

Spring Boot 自动配置之@Enable* 与@Import注解

Quick Guide to the Spring @Enable Annotations(Spring @Enable注解快速指南)

Spring Enable annotation – writing a custom Enable annotation(Spring Enable注解–编写自定义的Enable注解

今天室友AJ拆箱::)))

发布了56 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/feng_xiaoshi/article/details/103825162