博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring4-@Enable** 注解的实现原理
阅读量:6515 次
发布时间:2019-06-24

本文共 4212 字,大约阅读时间需要 14 分钟。

背景

在前面的工作中使用SpringBoot的时候,我碰到了很多的使用@Enable***注解的地方,使用上也都是加在@Configuration 类注解的类上面,比如: 

(1)@EnableAutoConfiguration 开启自动扫描装配Bean

(2)@EnableScheduling 开启计划任务的支持

(3)@EnableTransactionManagement 开启注解式事务的支持。

(4)@EnableCaching开启注解式的缓存支持。

(5)@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持,

还有一些我没用过,但是听过的,比如:

(6) @EnableAsync 开启异步方法的支持

(7) @EnableWebMvc 开启Web MVC的配置支持。

(8) @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持。

(9)@EnableJpaRepositories 开启对Spring Data JPA Repository的支持。

通过简单的加上这些@Enable*注解就可以开启一些功能,避免了在XML时代, 需要自己手动的配置很多的XML代码,大大提升效率降低使用难度。 那么这种简单的操作带来的巨大的便利性是怎么实现的呢?

我研究了一下上面的这些自动开启某些功能的注解,发现了一些公共的特性,下面先举出一个例子:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(CachingConfigurationSelector.class)public @interface EnableCaching { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }

在这些所有的@Enable*注解的定义里面都包含一个 @Import 注解。 @Import注解在4.2之前只支持导入配置类,在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean。这也说明了,自动开启的实现,其实是导入了一些配置类。

在上面各种@Enable**注解的实现中,导入配置类的形式,主要有三种类型:

1. 根据条件选择配置类(用的最多的)

以@EnableTransactionManagement 的定义为例:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }

 

TransactionManagementConfigurationSelector通过条件选择需要导入的配置类, TransactionManagementConfigurationSelector最根接口是ImportSelector, 这个接口重写了selectImports方法,在此方法里面先进行判断。比如在此例中,若AdviceMode为PROXY(默认), 则返回AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 的配置类。

源码如下:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector
{ @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }

 

2. 动态注册的Bean

以@EnableAspectJAutoProxy 为例:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }

 

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,并在Spring容器启动时解析生成bean,通过重写方法:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

来实现扫描注册逻辑。其中,AnnotationMetadata参数用来获得当前配置类上的注解,BeanDefinitionRegistry 参数用来注册Bean。源码如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {    @Override    public void registerBeanDefinitions(            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);        AnnotationAttributes enableAspectJAutoProxy =                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }

 

3. 直接导入配置类

以@EnableScheduling 注解为例

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

 

从上面可以看到,直接导入了入配置类SchedulingConfiguration, 这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean,SchedulingConfiguration的源码如下:

@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(); } }

 

总结: 其实上面的三种方法中,第一类是用的最多的。

转载地址:http://eoofo.baihongyu.com/

你可能感兴趣的文章
IBATIS 写BLOB字段遇到的问题
查看>>
Java集合--Map
查看>>
Dev gridControl 按回车增加一行
查看>>
Reapte控件的使用
查看>>
模拟手指或者鼠标单击和双击
查看>>
生成器与迭代器
查看>>
方法(method)和函数(function)区别
查看>>
mysql数据库学习——3,表的创建,删除和变更
查看>>
express
查看>>
渐进式的脚本加载
查看>>
Python参数传递(传值&传引用)
查看>>
在windows上如何安装python web引擎jinja2
查看>>
移动短信网关模拟器
查看>>
Python爬虫学习笔记之微信宫格验证码的识别(存在问题)
查看>>
Linux下的进程间通信(三)
查看>>
dva框架的下拉菜单的父子关系
查看>>
mysql 创建用户,授权
查看>>
基于Redis实现分布式锁
查看>>
ubuntu14.04+FSL5.0 安装
查看>>
maven mirror
查看>>