Spring注解实现解析

本文深入探讨Spring框架中应用上下文ApplicationContext及其注解(如@Controller, @Component等)的工作原理,详细解析注解配置与上下文注册之间的关联,并深入分析Spring如何通过AnnotationConfigWebApplicationContext与AnnotationConfigApplicationContext实现注解解析与Bean注入。

自Spring2.5以后引入的注解方法,为我们的开发带来了更多的便捷,至于使用,这里不打算说明,因为太多了……,本次主要想讲讲Spring提供给我们使用的注解(@Controller,@Component等等)其背后的细节,也讨论讨论中间贯穿的一些Spring知识。

开始讨论之前,你可能需要对Spring IOC有一些比较深的理解,我觉得,Spring IOC基本上是其它东西的基础,也是核心所在,有必要深入再深入。

Spring中的应用上下文ApplicationContext我们都不陌生,无论是Web还是非Web环境都需要使用到,只是使用的具体实现可能不同,在ApplicationContext的继承体系中,有一条分支是跟注解有关的,AnnotationConfigWebApplicationContext与AnnotationConfigApplicationContext,从名字就可以看出,前者用于Web应用,后者用于桌面应用,如果你只是一直在使用Spring,没有纠结与它是怎么做的,那么你可能不太注意到它们的存在,因为对于在Web中应用Spring注解的人,大抵是应用这两个注解,<context:annotation-config>与<context:component-scan>,很久以前论坛上还很多了问,配置了<context:component-scan>还需要配<context:annotation-config>吗?  

在接下来的解析中,你会发现这两个标签加上前面的注解ApplicationContext背后的机制都是大抵一致的:

(1). 扫描配置的base-package下的所有bean(<context:annotation-config>没有做)

(2). 通过AnnotationConfigUtils注册相关的BeanPostProcessor。(核心,这些BeanPostProcessor负责对应的注解的解析与注入)

其中(2)将是注解实施的核心,先来分析各个应用方法都是如何进入到(2)的,ApplicationContext只以Web类型的AnnotationConfigWebApplicationContext为例,在解析Bean的过程中,系统会调用其loadBeanDefinitions()加载Bean定义信息(详见IOC的解析):

  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
  2. AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory); //负责注册bean
  3. reader.setEnvironment(this.getEnvironment());
  4.  
  5. ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory); //扫描包中的bean
  6. scanner.setEnvironment(this.getEnvironment());
  7.  
  8. if (!ObjectUtils.isEmpty(this.annotatedClasses)) {
  9. ……
  10. reader.register(this.annotatedClasses); //注册
  11. }
  12.  
  13. if (!ObjectUtils.isEmpty(this.basePackages)) {
  14. ……
  15. scanner.scan(this.basePackages); //扫描包中的bean
  16. }
  17.  
  18. String[] configLocations = getConfigLocations();
  19. if (configLocations != null) {
  20. for (String configLocation : configLocations) {
  21. try {
  22. Class&lt;?&gt; clazz = getClassLoader().loadClass(configLocation);
  23. if (logger.isInfoEnabled()) {
  24. logger.info("Successfully resolved class for [" + configLocation + "]");
  25. }
  26. reader.register(clazz); //注册
  27. }
  28. catch (ClassNotFoundException ex) {
  29. if (logger.isDebugEnabled()) {
  30. logger.debug("Could not load class for config location [" + configLocation +
  31. "] - trying package scan. " + ex);
  32. }
  33. int count = scanner.scan(configLocation); //扫描
  34. ……
  35. }
  36. }
  37. }
  38. }

明显看到了(1)的影子,不断的扫描包中的Bean,注册Bean,但是明显不够,还需要第二部注册BeanPostProcessor来处理注解,实现的位置便是在scan中:

  1. public int scan(String... basePackages) {
  2. int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
  3.  
  4. doScan(basePackages); //(1)扫描包中的类
  5.  
  6. // Register annotation config processors, if necessary.
  7. if (this.includeAnnotationConfig) {
  8. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); //注册各种注解类型对应的BeanPostProcessor
  9. }
  10.  
  11. return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
  12. }

 

到了这里我们已经清楚了ApplicationContext中是如何处理的了,可惜在Web应用,我们一般使用的是XmlWebApplicationContext,之后再在配置文件中使用前面提到的两个标签配置注解,因此我们来看看其实现的原理,xml配置文件中<context>类型的配置由ContextNamespaceHandler处理:

  1. public class ContextNamespaceHandler extends NamespaceHandlerSupport {
  2.  
  3. public void init() {
  4. registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
  5. registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
  6. registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
  7. registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
  8. registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
  9. registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  10. registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
  11. registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
  12. }
  13.  
  14. }

看到对应我们要分析的配置标签annotation-config与component-scan的Parser,首先看annotation-config的:

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {
  2. Object source = parserContext.extractSource(element);
  3.  
  4. //注册所有的相关BeanPostProcessor,(2)中的内容,后续分解
  5. Set processorDefinitions =
  6. AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
  7.  
  8. //元素注册一个复合的Component,包含底下的PostProcessor Bean
  9. CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
  10. parserContext.pushContainingComponent(compDefinition);
  11.  
  12. // 将processor都作为复合Component的嵌入Component
  13. for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
  14. parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
  15. }
  16.  
  17. // 注册复合Component
  18. parserContext.popAndRegisterContainingComponent();
  19.  
  20. return null;
  21. }

它把做任务都交给了BeanPostProcessor,具体每个BeanPostProcessor做了啥,后面分析,先继续来看看component-scan的Parser:

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {
  2. String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
  3. ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  4.  
  5. // 扫描Bean定义并注册.
  6. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
  7. Set beanDefinitions = scanner.doScan(basePackages);
  8. registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
  9.  
  10. return null;
  11. }

看看我标注出来的三行代码,<1><2>便是创建scanner,通过它来扫描basepackage包下的Bean定义,这个在前面已经见识过了,<3>则是注册Bean,里面会有我们熟悉的注册BeanPostProcessor:

  1. protected void registerComponents(
  2. XmlReaderContext readerContext, Set beanDefinitions, Element element) {
  3. //老规矩,创建复合Component
  4. Object source = readerContext.extractSource(element);
  5. CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
  6.  
  7. for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
  8. compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder)); //将前面扫描的Bean添加进入
  9. }
  10.  
  11. // 如果必要,注册BeanPostProcessor.
  12. boolean annotationConfig = true;
  13. if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
  14. annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
  15. }
  16. if (annotationConfig) {
  17. //注册BeanPostProcessor
  18. Set processorDefinitions =
  19. AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
  20. for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
  21. compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
  22. }
  23. }
  24.  
  25. readerContext.fireComponentRegistered(compositeDef);
  26. }

函数中先注册扫描的bean,之后根据需要决定是否添加BeanPostProcessor,这里就回到一开始的问题“配置了<context:component-scan>还需要配<context:annotation-config>吗?  ”,在这里你会发现<context:component-scan>的Parser中会判断你是否配置了annotation-config属性,如果你配置了且设置为false,则它不会注册注解相关的BeanPostProcessor,相反则注册。之前说过BeanPostProcessor是具体处理Spring注解的地方,因此如果你使用了<context:component-scan>,只要你不主动将其annotation-config属性配置为false,则不需要再配置<context:annotation-config>了。

好了,最后的最后,我们终于来到了做实事的地方,看看Spring究竟注册了哪些BeanPostProcessor:

  1. public static Set registerAnnotationConfigProcessors(
  2. BeanDefinitionRegistry registry, Object source) {
  3.  
  4. Set beanDefs = new LinkedHashSet(4);
  5. //处理@Configuration注解
  6. if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  7. RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
  8. def.setSource(source);
  9. beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
  10. }
  11. //处理自动注入的
  12. if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  13. RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
  14. def.setSource(source);
  15. beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
  16. }
  17.  
  18. if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  19. RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
  20. def.setSource(source);
  21. beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
  22. }
  23.  
  24. // 处理JSR-250相关注解的,如@Resource
  25. if (jsr250Present &amp;&amp; !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  26. RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
  27. def.setSource(source);
  28. beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
  29. }
  30.  
  31. //持久化相关,JPA支持
  32. if (jpaPresent &amp;&amp; !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  33. RootBeanDefinition def = new RootBeanDefinition();
  34. try {
  35. ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
  36. def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
  37. }
  38. catch (ClassNotFoundException ex) {
  39. throw new IllegalStateException(
  40. "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
  41. }
  42. def.setSource(source);
  43. beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
  44. }
  45.  
  46. return beanDefs;
  47. }

这里注册了好几个相关注解类型的BeanPostProcessor,其中可能最用的便是CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor了,有兴趣可以一个一个看看具体怎么操作。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值