【Spring源码】- 01 Spring IoC容器启动之this方法

216 阅读7分钟
原文链接: mp.weixin.qq.com

开始案例

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

AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerSpring中两个非常重要的类。

首先,我们来看下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<BeanDefinitionHolder> 
                                                                                                                        registerAnnotationConfigProcessors(   BeanDefinitionRegistry registry, @Nullable Object source) {        ...
                                                                                                                            //省略  Set<BeanDefinitionHolder> 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个BeanPostProcessorSpring就是通过这些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中相关注解的支持;
  • EventListenerMethodProcessorDefaultEventListenerFactory:这两个类主要完成对 Spring4.2之后引入的@EventListener等事件注解支持;

上面六个PostProcessor中,最重要的是前两个,一个负责完成从各个地方把需要纳入 IoC管理的Bean都收集到一起;另一个则完成对这些收集的 Bean进行依赖注入。Spring IoC基本工作就是管理 Bean以及依赖注入,所以IoC启动流程分析中,这两个类占有很大的比重。

Scanner

下面再来看下另一个非常重要的类:ClassPathBeanDefinitionScanner

Spring项目中配置 <context:component-scan base-package="com.example.demo"/>@ComponentScan(basePackages="a.b.c"),这背后的工作就是靠 ClassPathBeanDefinitionScanner完成,其主要就是完成对指定包路径下的 Bean进行扫描,把含有特定注解的 Bean生成BeanDefinition注册到 IoC容器中。

下面通过一个Demo了解下 ClassPathBeanDefinitionScanner基本使用:


                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                            @Test
                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                    public 
                                                                                                                                                                                                                                                                        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容器中。

ClassPathBeanDefinitionScannerSpring中非常重要的一个类,决定了哪些类需要被纳入 IoC容器。我们可以继承 ClassPathBeanDefinitionScanner实现框架定制化功能,比如 MyBatisMapper扫描就是一个典型应用案例, MyBatisMapperScannerConfigurer的内部就使用到一个 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类中。

MyBatisMapper类是一个接口,而依赖注入获取到的是一个对象,这是如何做到的?

这里主要运用了动态代理功能,具体可以参见后续 MyBatis Mapper实现原理分析。

注意: AnnotationConfigApplicationContext中定义的: this.scanner = new ClassPathBeanDefinitionScanner(this);,这个其实一般情况下是没有使用的,只有手工调用 AnnotationConfigApplicationContext#scan()才会使用到 scanner。大部分 Bean的采集工作是 AnnotatedBeanDefinitionReader中向 IoC注册的 ConfigurationClassPostProcessor这个 BeanFactory后置处理器完成的,它在处理 @ComponentScan注解时会重新创建一个 ClassPathBeanDefinitionScanner实例,而不是使用 AnnotationConfigApplicationContext.scanner这个,这里要特别注意下,避免修改却发现没有起作用的尴尬。