7.AnnotatedBeanDefinitionReader的作用

298 阅读13分钟
Appconfig.java
@ComponentScan("com.yuanma.beanDefinition")
@Configuration
public class Appconfig {
}
 
X.java
@Component
public class X {
    public X(){
        System.out.println("X Constructor");
    }
}
 
Y.java
public class Y {
}
 
Test.java
public class Test{
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new 
        							AnnotationConfigApplicationContext();
        ac.register(Appconfig.class);
        ac.refresh();
        }
    }
public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(Appconfig.class);
        applicationContext.refresh();
    }

首先通过一张图简单的理解一下spring容器启动的时候执行调用BeanFactoryPostProcessor后置处理器的大概的方法执行顺序:

image.png

上图大概分为④步(这里只是讨论spring如何调用BeanFactoryPostProcessor,在调用之前到底执行了那些方法,上图并不是spring容器启动的所有步骤)

步骤1:启动main方法

①启动main方法。

步骤2:实例化Context,注册内置处理器

②在main方法里面调用AnnotationConfigApplicationContext的无参构造方法。

public AnnotationConfigApplicationContext() {

        //调用构造方法时需要首先调用父类GenericApplicationContext的构造方法
        //在父类的构造方法里会初始化一个DefaultListableBeanFactory
        //this.beanFactory = new DefaultListableBeanFactory();
    
        //创建一个BeanDefinition读取器 可以根据注解读取BeanDefinition
        this.reader = new AnnotatedBeanDefinitionReader(this);
        
        //创建一个BeanDefinition扫描器 能够扫描一个类或者包 转换成bd
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

2.1实例化DefaultListableBeanFactory

AnnotationConfigApplicationContext继承自GenericApplicationContext,所以调用自己之前会先调用父类GenericApplicationContext的构造方法。

public GenericApplicationContext() {   
    this.beanFactory = new DefaultListableBeanFactory();
}

2.2实例化AnnotatedBeanDefinitionReader

this.reader = new AnnotatedBeanDefinitionReader(this);
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        //registry 代表的是AnnotationConfigApplicationContext
        this(registry, getOrCreateEnvironment(registry));
    }

2.2.1构造方法注册了2个BFPP2个BPP

这里很重要,因为ConfigurationClassPostProcessor就是在这里被注入的,当前只是先看一下,后面细讲!

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;
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
        
        /*
        这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
        这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
        这里很重要 因为ConfigurationClassPostProcessor就是在这里被注入的!
        //BeanFactoryPostProcessor
        RootBeanDefinition def = new                                    
                RootBeanDefinition(ConfigurationClassPostProcessor.class);

        //BeanPostProcessor
        RootBeanDefinition def = new 
                RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
                
        //BeanPostProcessor
        RootBeanDefinition def = new 
                RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
                
        //EventListenerMethodProcessor
        RootBeanDefinition def = new 
                    RootBeanDefinition(EventListenerMethodProcessor.class);
                    
        RootBeanDefinition def = new 
                    RootBeanDefinition(DefaultEventListenerFactory.class);
        */
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

2.3实例化了ClassPathBeanDefinitionScanner

这个对象顾名思义就是能够用来完成spring的classpath的扫描功能。

//创建一个BeanDefinition扫描器 能够扫描一个类或者包 转换成bd
this.scanner = new ClassPathBeanDefinitionScanner(this);

在这里创建了但是并没有用到,只有使用下面的构造方法才能用到。

public AnnotationConfigApplicationContext(String... basePackages) {
        this();
        scan(basePackages);
        refresh();
}

这里提一句:在扫描我们指定的包路径的时候用的是AnnotationConfigApplicationContext的构造方法中构造的ClassPathBeanDefinitionScanner。但是spring内部完成扫描功能并不是用的这个对象,而是在扫描的时候会new新的ClassPathBeanDefinitionScanner对象。

换言之,这里在AnnotationConfigApplicationContext的构造方法中new的对象我们假设他为a,但是spring在真正完成扫描的时候会new一个b,它们是同一个类都是ClassPathBeanDefinitionScanner,为什么需要两个?

1.每个scaner的includeFilter和excludeFilter属性不相同,扫描的包的路径也不同。所以需要不同的参数构造不同的ClassPathBeanDefinitionScanner

2.每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的 需要把每个@ConponentScan的包含过滤器和排除过滤器 复制到对应的ClassPathBeanDefinitionScanner扫描器中

步骤3:注册配置类为BeanDefintion

applicationContext.register(Appconfig.class);
@Override
public void register(Class<?>... componentClasses) {
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    //使用AnnotatedBeanDefinitionReader 读取器 注册bd
    this.reader.register(componentClasses);
}

可以看到会把Appconfig类解析成为一个beanDefintion对象。

这里其实涉及到AnnotatedBeanDefinitionReader对象的意义,给解析出来的beanDefinition对象设置一些默认属性,继而put到beanDefintionMap当中。

为什么需要put到beanDefintionMap呢?

在上一篇我们已经解释过这个map就是单纯用来存储beanDefinition的,spring后面会遍历这个map根据map当中的beanDefinition来实例化bean,如果Appconfig类的beanDefintion存在map当中那么他必然会被spring容器实例化成为一个bean。

为什么Appconfig会需要实例化呢?

因为Appconfig当中有很多加了@Bean的方法,这些方法需要被调用,故而需要实例化,但是Appconfig类的实例化很复杂比一般类实例化过程复杂很多,涉及到代理涉及到cglib等等,这个我们后面文章解释。

为什么Appconfig类是通过register(Appconfig.class);手动put到map当中呢?为什么不是扫描出来的呢?

其实也很简单,普通的类都是通过扫描出来的。但是因为Appconfig无法扫描自己,普通的类是spring通过解析Appconfig上的@ComponentScan注解然后被扫描到,但是配置类无法扫描自己,相当于是1个入口方法。

也就是Spring需要扫描哪些包是通过配置类来配置的,所以配置类相当于是1个入口。通过扫描配置类配置的扫描包扫描出来的类就是普通类。当然这些普通类上面也可能配置了扫描包或者导入了其他的xml配置。这个时候这些普通类也会被当做配置类处理,这是1个递归的过程。

步骤4:后面分析

接下来便是第④步,④-1到④-4以后分析。

步骤5:执行spring中的bean工厂后置处理器

④-5便是我们上篇文章说的执行spring当中的bean工厂后置处理器,也是本文重点讨论的;下图是对上述文字的一个说明——spring容器启动的执行顺序:

24117616-0ab4ad768d97945a.gif

AnnotatedBeanDefinitionReader作用

先看一下这个类的javadoc,看看作者怎么来解释这个类的,这个类作用分为以下两个。

作用一:动态注册带注解的bean

可用于编程式动态注册一个带注解的bean,什么意思呢?

比如我们有一个类A存在com.shadow包下面,并且是一个加注解的类。比如加了@Component,正常情况下这个类A是可以被spring扫描出来的,但是有不正常情况,比如spring并没有扫描到com.shadow包,那么类A就无法被容器实例化。

有人可能会问为什么没有扫描到com.shadow包?什么情况下不会扫描到?

1.假设这个类是动态生成,在容器实例化的时候不存在那么肯定不存在.

2.包下有N多类但是只有一个类加了注解,那么其实你不需要去扫描,只需要使用AnnotatedBeanDefinitionReader去注册这个类即可。

3.某个类是你和第三方系统交互后得到的。那么这个时候我们可以把这个类通过AnnotatedBeanDefinitionReader的register(Class clazz)方法把一个带注解的类注册给spring。

这里的注册其实就是上一篇文章中说的把一个类解析成BeanDefintion对象,然后把这个对象put到beanDefinitionMap当中。

作用二:注册配置类

AnnotatedBeanDefinitionReader可以代替ClassPathBeanDefinitionScanner这个类,具备相同的注解解析功能。

ClassPathBeanDefinitionScanner是spring完成扫描的核心类,这个我后面会分析。

简而言之,spring完成扫描主要是依靠ClassPathBeanDefinitionScanner这个类的对象,但是AnnotatedBeanDefinitionReader可以替代他完成相同的注解解析,意思就是通过ClassPathBeanDefinitionScanner扫描出来的类A和通过AnnotatedBeanDefinitionReader显式注册的类A在spring内部会一套相同的解析规则。

那么AnnotatedBeanDefinitionReader除了动态显示注册一些spring扫描不到的类之外还有什么功能?

在初始化spring容器的过程中他主要干了什么事情呢?

或者这么说:假设程序中没有需要动态显示注册的类他就没用了吗?

再或者AnnotatedBeanDefinitionReader这个类的对象除了注册一些自己的类还有什么应用场景呢?

答案是作为读取器,注册我们的配置类,所谓的配置类就是那个加了@Configuration和@ComponentScan的那个类。也就是Appconfig.java。

那么问题来了,为什么配置类需要手动注册呢?

很简单,因为配置类无法扫描出来,所以需要我们手动注册。为什么无法扫描呢?

比如spring完成扫描是需要解析Appconfig.java当中的@ComponentScan注解的值(一般是一个包名),得到这个值之后去扫描这个值所代表的包下面的所有bean。

简单的说就是spring如果想要完成扫描。必须先提供Appconfig.java,所以Appconfig.java要在一开始就手动注册给spring,spring得到Appconfig.class之后把他解析成BeanDefintion对象,继而去获取@ComponentScan的值然后才能开始扫描其他bean。

public static void main(String[] args) {
     AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
}

//可以发现构造方法里已经帮我们做了register和refresh和文章开头的并无区别。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    this.register(componentClasses);
    this.refresh();
}

除了使用上面这种方式,也可以通过以下方式指定扫描的包名。

    @Configuration
    public class AppConfig {
        public AppConfig() {
            System.out.println("AppConfig开始被构造");
        }
    
        @Bean
        public Object getObject(){
            return new Object();
        }
    }
    AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext("指定扫描的包名");
                
     //我们可以看到也可以使用扫描器扫描到AppConfig
    public AnnotationConfigApplicationContext(String... basePackages) {
            this();
            scan(basePackages);
            refresh();
        }

如何注册

针对AnnotatedBeanDefinitionReader应用场景的第2点,我在啰嗦几句,一般程序员在初始化spring容器的时候代码有很多种写法,但都是换汤不换药的,我这里举2个例子:

第一种写法

AnnotationConfigApplicationContext ac =
                                new AnnotationConfigApplicationContext();
        //动态注册一个配置类 通过配置类来进行扫描包配置
        ac.register(Appconfig.class);
        //调用refresh方法
        //这里很多资料都叫做刷新spring容器
        //但是我觉得不合适,这是硬核翻译,比较生硬
        //笔者觉得理解为初始化spring容器更加精准
        //至于为什么以后慢慢更新再说
        ac.refresh();

第二种写法

    AnnotationConfigApplicationContext ac =
            new AnnotationConfigApplicationContext(Appconfig.class);

这两种写法都初始化spring容器,代码上的区别无非就是:

第一种写法是调用AnnotationConfigApplicationContext()的无参构造方法。

第二种写法是调用了AnnotationConfigApplicationContext(Class<?>... annotatedClasses)有参构造方法。

但是你如果翻阅源码,第二种写法的内部也是首先调用无参构造方法的,内部继续调用register(Appconfig.class)方

法,最后调用refresh()方法。和第一种写法的区别是register和refresh是程序员调用的。

笔者更推荐第一种写法,因为第一种写法可以在初始化spring容器之前得到AnnotationConfigApplicationContext的对象也就是代码里面的ApplicationContext对象。

其实第一种方法和第二种方法都可以得到ApplicationContext对象,那么为什么第一种写法笔者推荐呢?

首先第二种方法是在spring容器完成初始化之后的到的ac对象,容器已经初始化了,这个时候得到这个对象能干了事情少了很多,第一种方法在初始化之前得到的,那么能干的事情可多了。

①比如我们可以在容器初始化之前动态注册一个自己的bean,就是上文提到的 AnnotatedBeanDefinitionReader的应用场景,

②再比如可以利用ApplicationContext对象来关闭或者开启spring的循环依赖,

③还比如程序员可以在容器初始化之前注册自己实例化的BeanDefinition对象。如果你精通spring源码你会发觉提前得到这个ApplicationContext对象可以做的事情太多了,笔者这里说的三个比如都必须在spring容器初始化之前做才有意义,简而言之就是需要在spring调用refresh方法之间做才有意义。

AnnotatedBeanDefinitionReader源码

ConfigurationClassPostProcessor对应的BeanDefinition就是在AnnotatedBeanDefinitionReader构造的。

我们看下AnnotatedBeanDefinitionReader具体做了什么,看这个构造方法,传入的是一个BeanDefinitionRegistry类型,就是可以注册BeanDefinition的,内部要注册就需要用到:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    //registry 代表的是AnnotationConfigApplicationContext
    this(registry, getOrCreateEnvironment(registry));
}

getOrCreateEnvironment获得或者创建环境,如果BeanDefinitionRegistry是EnvironmentCapable的话就可以直接获取,否则就创建一个标准环境,其实就是获取一些系统的变量。

比如可以配置dev环境,test环境,pro环境等等。

private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry){
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    if (registry instanceof EnvironmentCapable) {
        return ((EnvironmentCapable) registry).getEnvironment();
    }
    return new StandardEnvironment();
}

接下来执行的是这一句代码:this(registry, getOrCreateEnvironment(registry));

继续看AnnotatedBeanDefinitionReader另一个构造方法:

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;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    //关键代码
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

前面4句代码是在断言判空和赋值。对BeanDefinitionRegistry进行了保存和把environment封装进了ConditionEvaluator,ConditionEvaluator可以理解成一个条件过滤器,与@Conditional有关,如果有了这个注解,就先判断条件成不成立,不成立的话有些操作就不做了。

registerAnnotationConfigProcessors

最重要的操作是registerAnnotationConfigProcessors。registerAnnotationConfigProcessors一看方法就知道要加一些处理器,来处理我们的注解配置,也就是说,spring有些内部的处理器需要注册进来,这里可以想到spring应该是为了统一处理处理器,所以也是按注册,处理这样的流程来,无论是自己内部的,还是用户自定义的,我们来看看里面是怎么做的,我们跟进这个方法的源码:

    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
                BeanDefinitionRegistry registry, @Nullable Object source) {
                
            // 获取beanFactory也就是DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = 
                                    unwrapDefaultListableBeanFactory(registry);
        
            if (beanFactory != null) {
                if (!(beanFactory.getDependencyComparator() 
                                    instanceof AnnotationAwareOrderComparator)) {
                    /**
                     * AnnotationAwareOrderComparator主要能解析@Order@Priority
                     */
                    beanFactory.setDependencyComparator
                                (AnnotationAwareOrderComparator.INSTANCE);
                }
                if (!(beanFactory.getAutowireCandidateResolver() 
                            instanceof ContextAnnotationAutowireCandidateResolver)) {
                    /**
                     * ContextAnnotationAutowireCandidateResolver提供处理延迟加载的功能
                     */
                    beanFactory.setAutowireCandidateResolver
                                    (new ContextAnnotationAutowireCandidateResolver());
                }
            }
    ​
            Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
            /**
             *  下面非常非常重要
             *  spring默认的BeanDefinition的注册,很重要,需要理解每个bean的类型
            */
        
            /*  
             *  1.注册ConfigurationClassPostProcessor对应的bd
             *  注册的bd的名字是internalConfigurationAnnotationProcessor
             *  ConfigurationClassPostProcessor是一个工厂后置处理器
             *  这个后置处理器非常重要,基本上类上面的注解都在这里面判断并解析
             *  spring的包扫描也在里面完成
             */
       if(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)){
                RootBeanDefinition def = new 
                            RootBeanDefinition(ConfigurationClassPostProcessor.class);
                def.setSource(source);
                //添加ConfigurationClassPostProcessor对应的bd
                beanDefs.add(registerPostProcessor(
                        registry,                 
                        def,                                   
                        CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));                         }
            // 2.注册AutowiredAnnotationBeanPostProcessor
            //顾名思义就是处理@Autowired的,它是一个bean的后置处理器,在bean的属性注入的时候会用到
           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));
            }
    
    
            //3.注册CommonAnnotationBeanPostProcessor
            //顾名思义就是处理一些公共注解的,它是一个bean的后置处理器
            //可以处理@PostConstruct和@PreDestroy还有@Resource等
            //提示: 这里有一个jsr250Present校验
            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));
            }
    
            // 4.注册PersistenceAnnotationProcessor
            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: ");
                }
                def.setSource(source);
                beanDefs.add(registerPostProcessor(registry, def,
                                                PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
            }
            // 5.注册EventListenerMethodProcessor
            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));
            }
            // 6.注册DefaultEventListenerFactory
            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;
        }

主要流程就是获取DefaultListableBeanFactory。

1.注册AnnotationAwareOrderComparator顺序比较器,排序用的。

2.注册ContextAnnotationAutowireCandidateResolver自动装配解析器,解析自动装配相关配置用。

3.注册CommonAnnotationBeanPostProcessor 顾名思义就是处理一些公共注解的,它是一个bean的后置处理器 可以处理@PostConstruct和@PreDestroy还有@Resource等

3.注册ConfigurationClassPostProcessor,他是前面配置类处理的关键,所以这里先添加bean定义。

4.AutowiredAnnotationBeanPostProcessor,处理自动装配的。

5.EventListenerMethodProcessor和DefaultEventListenerFactory。

6.注册CommonAnnotationBeanPostProcessor。

我们再来看看AnnotatedBeanDefinitionReader的重要属性:

    private final BeanDefinitionRegistry registry;
    //类名生成器
    private BeanNameGenerator beanNameGenerator = 
                                        AnnotationBeanNameGenerator.INSTANCE;
    //socpe注解解析器
    private ScopeMetadataResolver scopeMetadataResolver = new 
                                        AnnotationScopeMetadataResolver();
    //条件评估器
    private ConditionEvaluator conditionEvaluator;

可以看到,内部的bean定义全部都是用RootBeanDefinition。

至此读取器创建完成,其实就是注册了需要处理注解的处理器bean定义,此时还没有创建bean哦。后面ConfigurationClassPostProcessor发挥着很大的作用,用来解析配置类。

ClassPathBeanDefinitionScanner为什么要2个?

因为每个@ConponentScan的包含过滤器和排除过滤器不一样,是独一无二的 需要把每个@ConponentScan的包含过滤器和排除过滤器 复制到ClassPathBeanDefinitionScanner扫描器中