Spring Framework#@Enable*详解

来源:互联网 发布:ios小游戏源码 编辑:程序博客网 时间:2024/06/07 19:11

发博词

@Enable*的原理其实是根据此注解的各个配置往Spring IOC容器中注册一系列的Bean。在IOC中注册Bean,Spring为我们提供了两种方式,一个是@Configuration注解某个类,一个是实现ImportBeanDefinitionRegistrar接口,直接在容器中添加、删除某个Bean。Spring还为我们提供了一个ImportSelector接口,用于同时应用多个@Configuration注解的类或者实现了ImportBeanDefinitionRegistrar接口的类。

@Import

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Import {    /**     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}     * or regular component classes to import.     */    Class<?>[] value();}

查看JavaDoc可以知道,@Import接受一个Class类型的参数,这个Class可以是一个@Configuration注解的配置用的Class,可以是一个ImportBeanDefinitionRegistrar的接口的实现,可以是ImportSelector接口的实现。
通过查看Spring生态中已有的EnableXXX的实现可以发现,@Import注解引入的类的最终目的都是引导Spring在IOC容器中创建一系列的bean。

@Configuration

@Configuration注解过的Class里会使用@Bean注解直接定义各种Bean。例子:@EnableLoadTimeWeaving

ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口的实现中可以在registerBeanDefinitions方法中往容器中注入Bean。例子:@EnableAspectJAutoProxy。在ImportBeanDefinitionRegistrar接口中,可以使用

public interface ImportBeanDefinitionRegistrar {    /**     * Register bean definitions as necessary based on the given annotation metadata of     * the importing {@code @Configuration} class.     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be     * registered here, due to lifecycle constraints related to {@code @Configuration}     * class processing.     * @param importingClassMetadata annotation metadata of the importing class     * @param registry current bean definition registry     */    public void registerBeanDefinitions(            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);}

各个Spring的ApplicationContext都实现了BeanDefinitionRegistry接口用于管理IOC容器的Bean的注册移除等操作。
@Configuration和ImportBeanDefinitionRegistrar相比发现的一点区别是ImportBeanDefinitionRegistrar不仅可以增量添加Bean,还可以动态删除某个Bean,ImportBeanDefinitionRegistrar这种方式应该是更灵活,功能更强大一些。

ImportSelector

public interface ImportSelector {    /**     * Select and return the names of which class(es) should be imported based on     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.     */    String[] selectImports(AnnotationMetadata importingClassMetadata);}

ImportSelector接口的selectImports接口用于返回一个@Configuration注解的类的数组或者一个实现了ImportBeanDefinitionRegistrar接口的数组,暂时还没发现Spring的EnableXXX的实现中出现混用的情况;由于返回的数组是具体的类的全路径字符串,所以猜测混用也是可以的。有时间在测试下。感兴趣的同学可以先测试下,看看混用行不行,欢迎测试结果留言。
ImportSelector接口的selectImports方法返回ImportBeanDefinitionRegistrar的例子:@EnableConfigurationProperties
ImportSelector接口的selectImports方法返回@Configuration注解的类的例子:@EnableAsync

总结

刚开始的时候,@Enable* 风格的注解和@Configuration注解过的类,一直分不清他们的关系。其实他们是互相依赖的,@Enable* 的注解实现上需要有一个@Import注解,这个@Import注解可以引入一个或者多个@Configuration注解过的类。而一个Configuration可以被@Enable*注解继续注解。

@Configuration@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)public class AopAutoConfiguration {    @Configuration    @EnableAspectJAutoProxy(proxyTargetClass = false)    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)    public static class JdkDynamicAutoProxyConfiguration {    }    @Configuration    @EnableAspectJAutoProxy(proxyTargetClass = true)    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)    public static class CglibAutoProxyConfiguration {    }}
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy {    /**     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed     * to standard Java interface-based proxies. The default is {@code false}.     */    boolean proxyTargetClass() default false;    /**     * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}     * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.     * Off by default, i.e. no guarantees that {@code AopContext} access will work.     * @since 4.3.1     */    boolean exposeProxy() default false;}

而且@Enable*注解本身也可以被另一个@Enable*注解。

@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)@Target(value={java.lang.annotation.ElementType.TYPE})@Documented@Import({WebSecurityConfiguration.class,ObjectPostProcessorConfiguration.class})@EnableGlobalAuthenticationpublic @interface EnableWebSecurity {    /**     * Controls debugging support for Spring Security. Default is false.     * @return if true, enables debug support with Spring Security     */    boolean debug() default false;}

Spring 在处理这块时已经想到了各种有意义的可能的组合,使用起来还是相当灵活方便的。

原创粉丝点击