Annotation的解析机制
我们在使用spring中,经常会在xml中加上<context:component-scan/>标签,然后在需要配置在spring中的类上加上@Component/@Service/@Repository等注解,我们先简单的看看@Service或者@Repository注解,会发现这两个注解都被@Component注解所标识:

想要了解这些注解是如何工作的,就需要先知道component-scan标签做了什么。前文中描述了spring的xml解析机制以及其扩展方式,既然component-scan标签声明在context这个命名空间中,我们可以从ContextNamespaceHandler类入手,此类中有如下代码:

可以看到,component-scan标签使用了ComponentScanBeanDefinitionParser类做bean的解析,此类中的核心代码比较容易理解,先扫描类路径,识别出@Component注解标识的类或以@Component作为元注解的注解标识的类,将扫描到的bean注册到BeanDefinitionRegistry中:

从ClassPathBeanDefinitionScanner的doScan方法中进入,很容易发现component-scan的类路径扫描策略,它并没有逐个加载class,而是通过读取class文件得到类的元数据的方式做的类识别,关键代码段如下:

registerComponents方法的逻辑前半部分是在处理scanner扫描出来的bean,而后半部分则略有不同,后半部分向spring中注册注解配置处理相关的PostProcessor,主要逻辑是用于处理@Configuration/@ComponentScan注解标识的类以及@Autowired等注解的注入:

进入到AnnotationConfigUtils.registerAnnotationConfigProcessors方法的调用中,可以看到spring注册了多个与注解处理相关的类,其中包括用于处理@Configuration或@ComponentScan注解配置的类ConfigurationClassPostProcessor,注解注入处理类AutowiredAnnotationBeanPostProcessor等,详情可见具体代码:

注册的这些为都是前文描述过的PostProcessor的实现,其中注解注入相关的类是AutowiredAnnotationBeanPostProcessor,前文已经提到过,这里再重点描述一下ConfigurationClassPostProcessor类,此类的定义如下:

ConfigurationClassPostProcessor类是BeanFactoryPostProcessor的实现类(BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口),从ConfigurationClassPostProcessor的postProcessorBeanFactory方法或者postProcessorBeanDefinitionRegistry方法中可以看到注解配置处理的逻辑:

这里涉及到另一个注解@Conditional,被此注解标识的配置类,在不满足此注解指定的条件时在处理注解配置时会被跳过。
@Import
在使用springboot时,经常会用到@EnableXxx这样的注解,比如开户aop可以使用@EnableAspectJAutoProxy,这里也是使用到了spring为注解配置留的扩展,打开此类可以看到@Import注解的使用:

回到ConfigurationClassParser类中,可以找到处理@Import注解的代码:

首先是collectImports方法,它里面有一个递归调用,用于处理@Import作为元注解的情况,由于有这段逻辑的处理,@EnableAspectJAutoProxy上的元注解@Import才会被识别;
通过processImports方法可以看到@Import的具体处理逻辑,它通过@Import注解中的value属性指定的class获取BeanDefinition对象,逻辑相对较容易理解,这里不做过多解释:

Spring注解的扩展
这里给一个Spring注解扩展的简单示例,在初始化前通过日志打印出spring中配置了哪些bean,通过@Import实现:

在使用时可将@LogBean标识在配置类或者springboot的启动类上:

网友评论