这篇文章主要讲一下几个常见的后置处理器
bean后置处理器
首先要知道bean后置处理器的作用:为bean生命周期各个阶段提供扩展
然后来看看几个常见的bean后置处理器
先做一些准备工作,新建一个容器GenericApplicationContext,为什么用GenericApplicationContext呢?因为这是一个干净的容器,这里的干净是指他并没有帮我们添加一些额外的后置处理器,方便我们测试。
如果用开始介绍的那几种ApplicationContext,他已经把常用的后置处理器添加进去了。
创建容器之后还需要调用refresh方法,初始化容器,这个方法会帮我们执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例。
然后在准备3个bean用来测试
bean2和bean3里面没有什么内容
主要看bean1,里面用到了一些常用的注解@Autowired、@Resource、@Value、@PostConstruct、@PreDestroy,在里面打印了一些信息
然后把这3个bean注册到容器中,来看看是哪些后置处理器让他们里面的注解生效的。
启动容器看看效果
可以看到bean1中的日志信息都没有打印,说明这些注解都没有被解析,接下来就添加一下对应注解的后置处理器,让他们生效。
AutowiredAnnotationBeanPostProcessor
这个后置处理器主要是用来解析@Autowired和@Value注解的,我们来添加一下
再次运行可以看到控制台bean1里的日志打印,@Autowired和@Value已经生效了。
CommonAnnotationBeanPostProcessor
这个注解主要是解析@Resource @PostConstruct @PreDestroy
添加CommonAnnotationBeanPostProcessor之后在启动容器
可以看到@Resource @PostConstruct @PreDestroy这3个注解也生效了。
ConfigurationPropertiesBindingPostProcessor
这是一个springboot中的后置处理器,我们都知道springboot中有一个属性绑定的功能
比如这里指定一个prefix = “java”,他就会去解析我们的配置文件java.home和java.version的值绑定到bean4的对应属性上。
我们先看看不添加这个后置处理器的效果
可以看到bean4中的两个属性值都为空。然后我们在加上后置处理器试试
可以看到注解生效,并且成功赋值。
BeanFactory后置处理器
BeanFactory后置处理器的作用:为BeanFactory提供扩展功能。
我们还是使用GenericApplicationContext来演示。
新建一个容器,然后注册一个config类
config类里面我们有用注解注入了4个类,第一个是使用@ComponentScan注解扫描a05.component包下的类,也就是bean2类,后面3个是使用@bean注解注册的3个类。
运行一下容器,期望看到的是5个bean在容器里
但实际上只有config这一个bean,说明这些注解都没有被解析,接下来我们就介绍一下是哪个后置处理器来负责解析的这些注解。
ConfigurationClassPostProcessor
加入这个后置处理器之后我们在看看效果
可以看到期望的几个bean都成功注册到了容器里
他主要可以解析@ComponentScan @Bean @Import @ImportResource这几个注解。
MapperScannerConfigurer
这个注解的作用就是扫描mybatis那些mapper接口,吧他们作为bd补充到容器中,但是现在很少用到了,使用springboot都是通过自动配置间接的用到他。
使用他之后就会去扫描我们配置的路径a05.mapper包
可以看到mapper1 mapper2也注入到了容器里面。
接下来我们模拟一下这两个后置处理器的实现
@ComponentScan扫描自定义实现
先来看第一个ConfigurationClassPostProcessor
我先直接把实现类的代码贴出来,再来一步一步解释
第一步假设我们已经知道了一个类,比如config这个类,现在我们想要知道这个类上有没有加@ComponentScan注解,可以使用AnnotationUtils.findAnnotation方法来判断,当不为空时,在拿到basePackages属性上的值,也就是需要扫描的包的路径。
有了包路径之后拼接成一个完整的路径,就可以用到开始讲的ApplicationContext的扩展功能之一的getResources方法获取路径下所有的资源。这里我们用了另一个实现类PathMatchingResourcePatternResolver,也是同样的效果。
拿到resources之后循环处理,然后使用CachingMetadataReaderFactory的getMetadataReader方法,这是一个读取类的源信息的方法,读取到的结果放到了一个MetadataReader类中,在这里就能拿到类的一下注解,名字等信息。然后通过getAnnotationMetadata方法可以获取到类的注解信息。
当类上有@Component注解或者他的派生注解时,说明这个类是需要注入到容器中的,就会创建对应类的bd,然后注册到容器中。
循环完成后就会把扫描到的包下所有需要注册到容器中的类注册到容器中。
最后我们将自定义的后置处理器注册到容器中看看效果
可以看到跟ConfigurationClassPostProcessor实现了同样的效果。
@Bean扫描自定义实现
首先还是使用CachingMetadataReaderFactory的factory去读取类的源信息。
然后拿到被@bean注解标注了的方法,然后使用工厂方法来创建对应的bd。
我們可以看到这个@bean注解有个特殊逻辑,加了一个initMethod属性,指定了初始化方法,
所以这里多加了一个判断,获取注解上的initMethod的值,如果有值,会调用setInitMethodName方法指定初始化方法、
@Mapper注解扫描的实现
跟上面一样,还是根据mapper的路径获取所有资源,然后循环处理
读取类的源信息,然后判断是否是接口,最后生成对应的factorybean
来看看效果
可以看到@bean和@mapper注解都生效了。