环球观热点:【Spring源码】- 01 Spring IoC容器启动之this方法
开始案例
1、定义两个Service Bean
package org.source.ioc.basic.demo02;public class TestService01 {}
import org.springframework.stereotype.Component;@Componentpublic class TestService02 {}
package org.source.ioc.basic.demo02;public class TestService03 {}
2、定义Configuration
package org.source.ioc.basic.demo02;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration@ComponentScan(basePackageClasses = {TestConfig.class})@Import(TestService03.class)public class TestConfig { @Bean public TestService01 testService01(){ return new TestService01(); }}
3、测试代码
(资料图片仅供参考)
public class MainDemo { @Test public void test01(){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); //Arrays.stream(context.getBeanDefinitionNames()).forEach(x -> System.out.println(x+"->"+context.getBean(x))); }}
输出结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.event.internalEventListenerProcessororg.springframework.context.event.internalEventListenerFactorytestConfigtestService02org.source.ioc.basic.demo02.TestService03testService01
通过上面的输出结果可以看出,三个TestService
都被纳入了Spring IoC
容器中,但却是通过三种不同方式实现的,@ComponentScan
、@Import
和@Bean
,从输入结果来看,IoC
容器中除了自定义的类外,还有几个非我们自定义的Bean
,它们又是从哪里引入的、引入进来又有什么用呢?下面我们就通过源码方式分析下IoC
的启动流程,看看IoC
容器启动的背后到底隐藏了哪些玄机。
Reader
new AnnotationConfigApplicationContext(TestConfig.class)
这一句测试代码就可以驱动IoC
启动,非常的简单,下面我们就来研究下隐藏在这一句代码的背后Spring
到底做了哪些工作。
public AnnotationConfigApplicationContext(Class>... componentClasses) { this(); register(componentClasses); refresh();}
这个构造方法代码非常简单,主要逻辑封装在三个方法中,首先我们来看下this()
这个方法。
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this);}
AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
是Spring
中两个非常重要的类。
首先,我们来看下AnnotatedBeanDefinitionReader
这个类,@Configuration
、@Import
、@Autowired
、@Bean
等等这些Spring
中常用注解可以很神奇的为我们实现各种功能,注解本身是没有任何意义的,核心在于隐藏在这些注解背后的处理逻辑,AnnotatedBeanDefinitionReader
就是这个隐藏在注解背后的处理逻辑,可以实现对Spring中常用注解的解析处理。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; //条件比较器,用于处理@Condition注解 this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}
AnnotatedBeanDefinitionReader
构造方法中最关键的是在最后一句代码,其源码核心见下:
public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { ...//省略 Set beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
其逻辑很简单,就是向IoC
中注册一个BeanFactoryPostProcessor
和5个BeanPostProcessor
,Spring
就是通过这些PostProcessor
扩展点实现对各种注解的解析、处理,让开发只需要简单的几个注解就可以实现很多复杂功能,屏蔽了注解背后处理的复杂逻辑,这也是目前Spring开发趋势:注解驱动开发。
这几个PostProcessor
大致作用:
ConfigurationClassPostProcessor
:完成@Configuration
、@Import
、@ComponentScan
、@Component
、@Bean
等注解支持,该类主要完成完成BeanDefinition
的采集工作,就是解析各种注解,把需要纳入Spring
管理的Bean
都采集到一起生成BeanDefinition
存储起来,供后续生成对象提供所需的材料;AutowiredAnnotationBeanPostProcessor
:完成@Autowired
、@Value
注解支持,实现Spring中依赖注入的核心逻辑;CommonAnnotationBeanPostProcessor
:支持JSR-250
的一些注解,如:@Resource
、@PostConstruct
、@PreDestroy
等;PersistenceAnnotationBeanPostProcessor
:支持JPA
中相关注解的支持;EventListenerMethodProcessor
和DefaultEventListenerFactory
:这两个类主要完成对Spring4.2
之后引入的@EventListener
等事件注解支持;上面六个PostProcessor
中,最重要的是前两个,一个负责完成从各个地方把需要纳入IoC
管理的Bean
都收集到一起;另一个则完成对这些收集的Bean
进行依赖注入。Spring IoC
基本工作就是管理Bean
以及依赖注入,所以IoC
启动流程分析中,这两个类占有很大的比重。
Scanner
下面再来看下另一个非常重要的类:ClassPathBeanDefinitionScanner
。
Spring
项目中配置
或@ComponentScan(basePackages="a.b.c")
,这背后的工作就是靠ClassPathBeanDefinitionScanner
完成,其主要就是完成对指定包路径下的Bean
进行扫描,把含有特定注解的Bean
生成BeanDefinition
注册到IoC
容器中。
下面通过一个Demo
了解下ClassPathBeanDefinitionScanner
基本使用:
@Testpublic void classPathBeanDefinitionScannerTest(){ String BASE_PACKAGE = "org.source.ioc.basic.demo02.scanner"; //1.创建一个IoC容器,用于装载ClassPathBeanDefinitionScanner扫描出的BeanDefinition SimpleBeanDefinitionRegistry registry= new SimpleBeanDefinitionRegistry(); /** * 2.创建一个Scanner扫描器,useDefaultFilters:是否使用默认过滤器,默认该值为true, * 即会把@Component注解的Bean都扫描出来,这里我们不需要这个功能,只需要扫描我们自定义注解的Bean */ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false); //3.这里注册一个注解类型过滤器,完成对自定义注解Bean过滤 scanner.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class)); /** * 4.是否向IoC中注册用于用于处理核心注解的6个PostProcessor,默认true * AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) */ scanner.setIncludeAnnotationConfig(false); //5.上面工作都准备完成,调用scan(String... basePackages)即可对指定的包路径下的Bean扫描过滤,返回值是扫描出的Bean数量 int beanCount = scanner.scan(BASE_PACKAGE); //6.scan()方法会把符合要求的Bean生成BeanDefinition并注册到IoC容器中,我们就可以从IoC容器中获取到这些BeanDefinition String[] beanDefinitionNames = registry.getBeanDefinitionNames(); System.out.println("bean count:"+beanCount); Arrays.stream(beanDefinitionNames).forEach(System.out::println);}
上面案例就可以完成扫描org.source.ioc.basic.demo02.scanner
包下的所有Bean
,将含有@MyComponent
注解的Bean
生成对应的BeanDefinition
,并注册到IoC
容器中。
ClassPathBeanDefinitionScanner
是Spring
中非常重要的一个类,决定了哪些类需要被纳入IoC
容器。我们可以继承ClassPathBeanDefinitionScanner
实现框架定制化功能,比如MyBatis
的Mapper
扫描就是一个典型应用案例,MyBatis
的MapperScannerConfigurer
的内部就使用到一个ClassPathBeanDefinitionScanner
的子类,实现将Mapper
接口文件注入到IoC
容器中。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } scanner.registerFilters(); scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}
ClassPathMapperScanner
继承ClassPathBeanDefinitionScanner
,完成@MapperScan
注解支持,将特定的Mapper
类生成BeanDefinition
注册到IoC
容器中,这样我们才能通过@Autowired
依赖注入Service
类中。
MyBatis
的Mapper
类是一个接口,而依赖注入获取到的是一个对象,这是如何做到的?这里主要运用了动态代理功能,具体可以参见后续MyBatis Mapper实现原理
分析。
注意:AnnotationConfigApplicationContext
中定义的:this.scanner = new ClassPathBeanDefinitionScanner(this);
,这个其实一般情况下是没有使用的,只有手工调用AnnotationConfigApplicationContext#scan()
才会使用到scanner
。大部分Bean
的采集工作是AnnotatedBeanDefinitionReader
中向IoC
注册的ConfigurationClassPostProcessor
这个BeanFactory
后置处理器完成的,它在处理@ComponentScan
注解时会重新创建一个ClassPathBeanDefinitionScanner
实例,而不是使用AnnotationConfigApplicationContext.scanner
这个,这里要特别注意下,避免修改却发现没有起作用的尴尬。
标签: