Springboot启动流程分析(三):刷新IOC容器之执行beanFactory后置处理器原理

5 篇文章 1 订阅

目录

1 准备刷新容器prepareRefresh

1.1 设置属性,打印日志

1.2 根据容器中environment初始化servlet参数

1.3 校验environment参数

1.4 初始化监听器和监听事件

2 获取beanFactory

3 准备beanFactory参数

4 设置servlet相关beanFactory参数

5 执行beanFactory后置处理器

5.1 获取beanFactory后置处理器

5.2 执行beanFactory后置处理器方法 

5.2.1 第一次执行BeanDefinitionRegistry的后置处理器

5.2.2 第二次执行BeanDefinitionRegistry的后置处理器

5.2.3 第三次执行BeanDefinitionRegistry的后置处理器

5.2.4 第四次执行BeanDefinitionRegistry的后置处理器

5.2.5 第一次执行BeanFactory的后置处理器

5.2.6 第二次执行BeanFactory的后置处理器

5.2.7 上半部分代码总结

5.2.8 第三次执行BeanFactory的后置处理器

5.2.9 第四次执行BeanFactory的后置处理器

5.2.10 第五次执行BeanFactory的后置处理器

5.2.11 清理beanFatcory缓存

6 总结


本章最重要的方法就是关于beanFactory的后置处理器执行原理,具体为类PostProcessorRegistrationDelegate中的方法invokeBeanFactoryPostProcessors,初看上去,完全不知所云,但是通过详细的代码解析,我们可以通过它对Spring框架有更深的理解。

如果没有耐心的同学,可以直接去看最后一节的总结内容,也能有一个大致的了解。

下面正式开始:

在SpringApplication的run方法中,执行完成

createApplicationContext、prepareContext

分别完成IOC容器实例化和environment环境准备以后,我们正式进入容器刷新阶段,也就是refreshContext方法:

context = this.createApplicationContext();
            ......
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);

 而refreshContext方法最终会进入AbstractApplicationContext类中的refresh方法。这里要说明,其实refreshContext这个方法最终调用的是其参数context的refresh方法,也就是IOC容器自身的刷新方法。通过下面代码可以很清晰的看出来:

protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
    }

清楚了这一点,对我们后文的一些方法属性理解会更有帮助。下面我们进入真正的刷新方法: 

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //准备刷新容器
            this.prepareRefresh();
            //获取bean工厂
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //准备bean工厂
            this.prepareBeanFactory(beanFactory);
            try {
                //准备bean工厂后置处理器
                this.postProcessBeanFactory(beanFactory);
                //执行bean工厂后置处理器
                this.invokeBeanFactoryPostProcessors(beanFactory);

 通过同步方法块进入后,我们来看一下每一步都做了什么。

1 准备刷新容器prepareRefresh

为了方便理解,我们实行分步、分段讲解。

1.1 设置属性,打印日志

第一步,会设置一些当前实例,其实也就是IOC容器,它本身是以

AnnotationConfigServletWebServerApplicationContext

的形式实例化,在SpringApplication中被强转为其父类AbstractApplicationContext。所以在实例化的过程中,其实父类的属性也会被初始化。所以我们在这里可以继续设置其基本属性。

之后如果当前配置文件设置的日志级别是debug及以下,会在控制台打印日志,代表当前容器进入刷新阶段,具体代码如下:

this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);
        if (this.logger.isDebugEnabled()) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Refreshing " + this);
            } else {
                this.logger.debug("Refreshing " + this.getDisplayName());
            }
        }

1.2 根据容器中environment初始化servlet参数

this.initPropertySources()

这一句代码就是初始化environment的部分参数属性,主要与servlet相关。

在IOC容器中参数environment初始化的过程中,会设置2个标准的servlet参数:

servletContextInitParams、servletConfigInitParams

其初始化代码示例为:

protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));

而在我们这一步的初始化过程中, 也是先获取到IOC容器的environment参数,之后执行其servlet专用的初始化方法。如果传入的servlet参数有值,则对上面的初始化参数值进行替换。

但是根据这一步中initPropertySources方法的具体实现,其传入的servlet参数均为空,所以我们认为这一步保持environment自己的初始值不变:

protected void initPropertySources() {
        ConfigurableEnvironment env = this.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, (ServletConfig)null);
        }

1.3 校验environment参数

this.getEnvironment().validateRequiredProperties();

主要作用就是校验environment中key-value形式的参数配置中,有些必输参数的value是否为空,如果为空会抛出运行时异常。

1.4 初始化监听器和监听事件

在前文执行prepareContext方法的过程中,在加载初始化器和prepareContext最后一步执行监听器方法的时候,会分别把SpringApplication实例化过程中通过spring.factories配置文件加载和代码直接加载的监听器都添加到IOC自己的监听器中,也就是其参数

private final Set<ApplicationListener<?>> applicationListeners

在这里,会把监听器和监听事件进一步处理:

if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
        } else {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        this.earlyApplicationEvents = new LinkedHashSet();

2 获取beanFactory

ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

其实质是调用GenericApplicationContext中的方法执行了两步操作:

第一步,通过原子类AtomicBoolean中的cas方法,控制IOC容器只能刷新一次,否则就抛出异常。

protected final void refreshBeanFactory() throws IllegalStateException {
        //第一步
        if (!this.refreshed.compareAndSet(false, true)) {
            throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
        } else {
        //第二步
            this.beanFactory.setSerializationId(this.getId());
        }
    }

第二步,GenericApplicationContext就是我们实例化的IOC容器的父类,所以其beanFactory就是前面在IOC容器实例化过程中新建的bean工厂对象。

在这里,拿到当前beanFactory后,设置其序列id:serializationId,为当前容器名称,也就是yml文件中配置的spring.application.name参数,如果没有配置取默认值application。

之后把serializationId和通过弱引用构建的当前beanFactory通过key-value的形式放入bean工厂类的一个map属性serializableFactories中:

public void setSerializationId(@Nullable String serializationId) {
        if (serializationId != null) {
            serializableFactories.put(serializationId, new WeakReference(this));
        ......
        this.serializationId = serializationId;
    }

3 准备beanFactory参数

this.prepareBeanFactory(beanFactory);

 主要是设置一些beanFactory的属性,例如当前默认的类加载器、参数解析器、资源编辑相关属性、忽略的依赖类型、能够解析的依赖类型,还有注册了environment相关的实例到IOC容器中,例如beanName为environment、systemProperties、systemEnvironment及其实例。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        beanFactory.setBeanClassLoader(this.getClassLoader());
   ......
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   ......
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
   ......
        if (!beanFactory.containsLocalBean("environment")) {
            beanFactory.registerSingleton("environment", this.getEnvironment());
        }

总的来说就是一些beanFacotry的set或者Map属性的put方法。

4 设置servlet相关beanFactory参数

this.postProcessBeanFactory(beanFactory)

这其实是一个抽象方法,可以在子类中有不同的实现,可以灵活以多种方式的实现,来为beanFactory设置一些属性。具体在当前的启动方式,则是通过属性设置的方式,来把servlet的一些基本属性request、session通过scope属性的方式,设置到beanFactory的Map形式的 scopes参数中。

同时也会设置一些servlet相关的bean的后置处理器、忽略的依赖类型、可以解析的依赖类型,以List或者Map的形式储存到beanFactory的对应属性中:

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(ServletContextAware.class);
        this.registerWebApplicationScopes();
    }

可以理解,这一步依然是一些准备工作,但是它和上面的准备工作不同,不是通用的,需要根据不同的子类来实现。

5 执行beanFactory后置处理器

this.invokeBeanFactoryPostProcessors(beanFactory);

 这一步,真正开始执行,在环境准备阶段加载的beanFactory后置处理器。

核心功能便是下面这一个:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());

5.1 获取beanFactory后置处理器

在执行之前,首先会通过getBeanFactoryPostProcessors方法,获取容器中已有的bean的后置处理器(其实就是beanFactory的一个List属性),那么这些后置处理器哪来的呢?

举个例子,在前面准备容器刷新阶段的方法中,当监听器ConfigFileApplicationListener执行到ApplicationPreparedEvent这一步的时候,会直接把当前监听器中的实现了BeanFactoryPostProcessor的内部类加载到IOC容器中:

private void onApplicationPreparedEvent(ApplicationEvent event) {
        this.logger.switchTo(ConfigFileApplicationListener.class);
        this.addPostProcessors(((ApplicationPreparedEvent)event).getApplicationContext());
    }
......
    protected void addPostProcessors(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(new ConfigFileApplicationListener.PropertySourceOrderingPostProcessor(context));
    }

这一步,获取到的beanFactory后置处理器如下:

[org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor]

可以看到,传入的beanFactory后置处理器一共有3个,分别是在环境准备阶段,初始化器加载的Initializer,和监听器加载的Listener。 

5.2 执行beanFactory后置处理器方法 

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());

下面我们介绍具体的invokeBeanFactoryPostProcessors方法,由于这个方法非常长,先大概介绍下。

先说明一下,上文传入的beanFactory的后置处理器参数其实包含两部分:bean定义信息注册器BeanDefinitionRegistry的后置处理器,和真正的beanFactory的后置处理器。

看方法名,说的是执行beanFactory的后置处理器,其实在这之前,还需要先执行bean定义信息注册器BeanDefinitionRegistry的后置处理器方法,而且会执行多次。

刚看到代码,如果不仔细看,会觉得很莫名其妙,感觉就是同样的代码执行了3次,但是如果仔细比较,还是能比较出不同的。

具体的不同,主要是在收集用户执行的BeanDefinitionRegistry的后置处理器和beanFactory的后置处理器的时候。

以BeanDefinitionRegistry的后置处理器为例,一边执行,一边会往beanFactory中加入新的信息,这样下一次需要执行的后置处理器会变多,因为归根结底,后置处理器还是需要从beanFactory中去加载。

同时也会过滤掉已经执行过的后置处理器,避免重复执行。

具体的表现形式为,例如BeanDefinitionRegistry的后置处理器在执行到某一阶段,会借助环境准备阶段加载到IOC容器中的著启动类(main方法类),以启动类为基准,通过其全类名获取到其所在的包名,然后扫描包下所有的类,构建一个项目中所有类的集合。

从其中筛选合适的后置处理器,供下一步的BeanDefinitionRegistry的后置处理器调用。

总而言之,就是每一次BeanDefinitionRegistry的后置处理器的调用都是为其下一次调用做准备。

而所有的BeanDefinitionRegistry的后置处理器的调用,又都是为beanFactory的后置处理器调用做准备。

而每一次的beanFactory的后置处理器调用调用,也是为下一次做准备。并不是无意义的代码重复。

以上两点,就是执行这段方法的主线,要把握主线,才不至于被代码绕到云山雾罩。

下面我们详细解读代码,为了便于理解,依旧分段解析。

5.2.1 第一次执行BeanDefinitionRegistry的后置处理器

先介绍2个重要参数:beanFactory后置处理器集合regularPostProcessors和BeanDefinitionRegistry的后置处理器集合registryProcessors。

根据上一节,传入的三个beanFactory的后置处理器,需要进行分类,如果是BeanDefinitionRegistryPostProcessor类型的BeanDefinitionRegistry后置处理器,先执行此处理器的默认方法postProcessBeanDefinitionRegistry(),执行完成后把处理器加入集合registryProcessors中。

如果beanFactory后置处理器的类型不是BeanDefinitionRegistryPostProcessor,则认为是普通的beanFactory后置处理器,加入集合regularPostProcessors:

BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                } else {
                    regularPostProcessors.add(postProcessor);
                }

这一段的重点就是BeanDefinitionRegistry后置处理器执行postProcessBeanDefinitionRegistry()方法做了什么?

下面分别介绍:

CachingMetadataReaderFactoryPostProcessor作用

属于SharedMetadataReaderFactoryContextInitializer的静态内部类,其执行后置处理器特定方法的作用有两个。

首先,在IOC容器中,注册了一个特殊的beanName的bean的定义信息。

其次,对IOC容器中已有的一个特殊beanName为internalConfigurationAnnotationProcessor(简写)的bean的定义信息进行了增强。

和IOC容器实例化过程中,调用的AnnotationConfigUtils方法有点类似。

这些初始化定义的beanName基本都以internal开头,代表IOC容器内具体有特殊意义的bean。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            this.register(registry);
            this.configureConfigurationClassPostProcessor(registry);
        }

ConfigurationWarningsPostProcessor作用

属于ConfigurationWarningsApplicationContextInitializer中的静态final类,也是SpringApplication类实例化过程中加载的初始化器initializers,在执行其initializer方法加载进来的beanFactory后置处理器。

在其特定的处理器执行方法中,这个作用只有一个,那就是打印告警信息。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            ConfigurationWarningsApplicationContextInitializer.Check[] var2 = this.checks;
            int var3 = var2.length;
            for(int var4 = 0; var4 < var3; ++var4) {
                ConfigurationWarningsApplicationContextInitializer.Check check = var2[var4];
                String message = check.getWarning(registry);
                if (StringUtils.hasLength(message)) {
                    this.warn(message);
......

打印告警信息的步骤,主要分为两步:

第一步,通过注入IOC容器中主启动类的注解信息,获取需要扫描的包名。

第二步,检查包名是否有问题,主要确认包名不包含org和org.springframework,因为这是Spring框架专用的包名。

public String getWarning(BeanDefinitionRegistry registry) {
            Set<String> scannedPackages = this.getComponentScanningPackages(registry);
            List<String> problematicPackages = this.getProblematicPackages(scannedPackages);
            return problematicPackages.isEmpty() ? null : "Your ApplicationContext is unlikely to start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
        }

其具体实现为:

第一步,因为目前容器中加载的bean定义信息不多,主要就是以internal开头的IOC容器内部的一些bean定义信息和启动类BookstoreApplication的bean定义信息。

首先,筛选出bean的定义信息是注解类型——AnnotatedBeanDefinition的,只有主启动类满足要求,这样就获取了主启动类的所有bean定义信息。

之后,把主启动类的注解信息进行层层解析,得到复合注解和元注解的集合,从中筛选出主启动类上标记为@ComponentScan的所有信息,如果没有标记@ComponentScan注解,取拆解后集合中@ComponentScan的默认属性。

在获取@ComponentScan的过程中,其实和在前文容器环境准备阶段使用load方法时,我们选择使用注解还是xml来解析启动类的方法会有关联,在前文由于我们是注解方式启动,所以会用isComponent来校验主启动类是否有@Component注解。

前文我们介绍了,如何通过解析启动类的复合注解,其实在解析完成后,会以key-value的形式,把启动类的注解信息,存放在key为"Packages annotation filter: java.lang.,org.springframework.lang."的Map缓存中,其实就是AnnotationTypeMappings中一个属性为final的静态Map,其名称为standardRepeatablesCache。

那么在完成isComponent校验后,还会执行一个register方法,会把一个key为"No annotation filtering",value为启动类所有复合注解信息,注意还没有解析,放在具有同样属性的缓存noRepeatablesCache中。

那么到了当前获取@ComponentScan的过程,就可以通过key"No annotation filtering",直接从缓存获取启动类复合注解,不过还需要重复isComponent校验的部分方法,就是通过AnnotationTypeMappings的实例化方法来解析复合注解,得到复合注解和元注解的集合返回。

之后通过反射获取@ComponentScan注解的所有信息,就可以获取到启动类配置了哪些需要扫描的包,注意这里如果没有配置@ComponentScan注解,返回默认值,即启动类的包名,通过getComponentScanningPackages方法调用如下方法:

private void addComponentScanningPackages(Set<String> packages, AnnotationMetadata metadata) {
            //获取ComponentScan注解所有属性
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true));
            if (attributes != null) {
                this.addPackages(packages, attributes.getStringArray("value"));
                this.addPackages(packages, attributes.getStringArray("basePackages"));
                this.addClasses(packages, attributes.getStringArray("basePackageClasses"));
                if (packages.isEmpty()) {
                    packages.add(ClassUtils.getPackageName(metadata.getClassName()));
                ......

第二步,根据第一步获取的包名,校验是否满足包名不包含org和org.springframework的规范。

5.2.2 第二次执行BeanDefinitionRegistry的后置处理器

currentRegistryProcessors = new ArrayList();
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            String[] var16 = postProcessorNames;
            var9 = postProcessorNames.length;
            int var10;
            String ppName;
            for(var10 = 0; var10 < var9; ++var10) {
                ppName = var16[var10];
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

有个大概印象就行,重要步骤,下面会一步步解释: 

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

postProcessorNames是在方法进入后初始化的一个String数组,其实根据当前方法名称getBeanNamesForType就可以看出来,就是从beanFactory中根据给定的参数类型获取beanName的集合。

这里beanName需要满足的条件就是其对应的class信息要是属于BeanDefinitionRegistryPostProcessor这个接口类型的,也就是beanName对应的class信息要实现这个接口,即可满足要求。

知道了原理,就知道该怎么做了,那如果我们自己做会怎么做呢?

其实最简单的可以从beanFactory的beanDefinitionMap属性中,获取所有的value,也就是bean的定义信息——RootBeanDefinition,可以从RootBeanDefinition中获取beanName对应的class信息,如果class是实现了BeanDefinitionRegistryPostProcessor接口的,就直接返回。

通过比对,我们最终会获取到postProcessorNames的值为:[org.springframework.context.annotation.internalConfigurationAnnotationProcessor]。

因为在beanDefinitionMap映射中,它对应的class为ConfigurationClassPostProcessor,实现了接口BeanDefinitionRegistryPostProcessor。

Spring实现

上面我们说的只是一个大概的做法,那么Spring框架是如何实现这个方法的呢?

第一步:

Spring会根据getBeanNamesForType的参数设定,进入指定的方法,先获取到容器中所有的beanName的注册信息,然后遍历处理。

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
        List<String> result = new ArrayList();
        Iterator var5 = this.beanDefinitionNames.iterator();

第二步:

在遍历过程中,首先会对bean的定义信息进行合并,可能同一个bean在多处进行了信息定义,会放入一个ConcurrentHashMap中,需要的时候去获取信息然后合并。

RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);

第三步:

完成bean信息合并后,会继续判断这个beanName对应的class是否属于FactoryBean接口类型,也就是是否实现了这个接口。

boolean isFactoryBean = this.isFactoryBean(beanName, mbd);

注意FactoryBean和beanFactory完全不一样,前者重点是一个bean,后者的重点是一个工厂Factory。FactoryBean的主要作用就是对bean中的实例进行定制化的管理,beanFactory则在于标准化的生产bean实例,二者是相辅相成的。

由于还在容器的初始化阶段,容器内bean的实例极少,容器中定义的信息基本还是关于beanFactory的,所以beanName对应的class不属于FactoryBean类型

第四步:

根据beanName,bean定义信息,还有BeanDefinitionRegistryPostProcessor类型,判断是否满足要求。首先会从bean定义信息RootBeanDefinition中,根据beanName信息获取其对应的class类型,再判断其是否继承了接口BeanDefinitionRegistryPostProcessor。

matchFound = this.isTypeMatch(beanName, type, allowFactoryBeanInit);

满足要求,则返回beanName到集合postProcessorNames中,如果不满足,继续遍历获取下一个beanName。

if (matchFound) {
      result.add(beanName);
}

Spring的实现方式和我们预想的大差不差,但是作为框架,它做了很多扩展、兼容方面的考虑,所以真正的方法还是会比较复杂。

在获取到postProcessorNames后,会对其执行遍历,如果其中beanName对应的class实现了PriorityOrdered接口,则会把beanName对应的class进行实例化:

for(var10 = 0; var10 < var9; ++var10) {
      ppName = var16[var10];
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
       currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
           processedBeans.add(ppName);
      }
}

这里,会把beanFactory生成的实例加入到currentRegistryProcessors集合中,为下一步执行beanDefinitionRegistry后置处理器做准备。

beanName加入processedBeans集合,为下文防止重复执行做准备。

在这里beanFactory.getBean方法也是很重要,但是不是这一章的重点,我们后面会重点讲到。

但是我们可以说一下这个方法的作用,大概就是看这个beanName是否被实例化了,如果实例化过且需要获取单例实例,从缓存获取,否则生成新的单例实例。

下面继续执行:

sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);

对currentRegistryProcessors进行排序,然后加入registryProcessors集合中,registryProcessors是用于后面执行beanFactory的后置处理器。

下面再进入到一个重要方法:

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

执行currentRegistryProcessors中,也就是实例ConfigurationClassPostProcessor的beanDefinitionRegistry后置处理器的指定方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);
        ......
        } else {
            this.registriesPostProcessed.add(registryId);
            this.processConfigBeanDefinitions(registry);
        }
    }

这里主要做了两件事,把registryId加入registriesPostProcessed集合,执行processConfigBeanDefinitions方法。

重点在第二步其主要作用是根据主启动类的全类名,获取到当前项目中所有需要注册到IOC容器中的类,以及主启动类的注解@EnableAutoConfiguration,来加载一系列Spring框架提供的默认类,来注册到IOC容器中

关于这一点,会开专门的章节详细讲解,我们需要知道的就是,它加载了一系列bean定义信息到容器后,为下面继续执行的方法做了准备。

5.2.3 第三次执行BeanDefinitionRegistry的后置处理器

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            var16 = postProcessorNames;
            var9 = postProcessorNames.length;

            for(var10 = 0; var10 < var9; ++var10) {
                ppName = var16[var10];
  //和第二次执行BeanDefinitionRegistry后置处理器唯一不同,需要实现Ordered类
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }

            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();

有了前一次执行BeanDefinitionRegistry后置处理器方法讲解的铺垫,这一次执行就比较好理解了。

首先会从容器中获取bean的类型为BeanDefinitionRegistryPostProcessor.class的beanName,然后遍历这些beanName,进入遍历循环后,通过processedBeans集合判断当前beanName的后置处理器是否已被执行过,只有未被执行过且实现了Ordered接口的后置处理器才会被加入currentRegistryProcessors集合中。

注意currentRegistryProcessors在上一步已被清空。

同时这个满足条件的处理器的beanName也会被加入processedBeans集合,防止下次重复执行。

之后就是排序,再把新获取到的BeanDefinitionRegistry后置处理器集合currentRegistryProcessors加入到registryProcessors,为beanFactory的后置处理器执行做准备。

之后便是最重要的,第三次执行BeanDefinitionRegistry后置处理器指定方法。

通过第二次的执行BeanDefinitionRegistry后置处理器——ConfigurationClassPostProcessor类中的方法,我们在IOC容器中注册了当前工程中,主启动类下所有的自定义class文件,以及Spring框架自带的默认的自动装配类。

所以这一次需要执行的BeanDefinitionRegistry后置处理器,理论上会比上一次多,又由于processedBeans集合的过滤作用,只会执行新增的BeanDefinitionRegistry后置处理器的指定方法。

所以关键便在于新增了哪些后置处理器,可以先看下这次加载的postProcessorNames:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration$TokenKeyEndpointRegistrar

可以看到,由于internalConfigurationAnnotationProcessor(IOC容器实例化过程中,注册的一批以internal开头的内部beanName)是ConfigurationClassPostProcessor类的beanName,已经被执行过。

新增了AuthorizationServerEndpointsConfiguration中的静态内部类TokenKeyEndpointRegistrar,但是其只是实现了BeanDefinitionRegistryPostProcessor接口,并不满足同时还要实现Ordered接口的条件。

所以这次的需要执行的currentRegistryProcessors集合为空,跳过。

5.2.4 第四次执行BeanDefinitionRegistry的后置处理器

boolean reiterate = true;

while(reiterate) {
      reiterate = false;
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      String[] var19 = postProcessorNames;
      var10 = postProcessorNames.length;

      for(int var26 = 0; var26 < var10; ++var26) {
      String ppName = var19[var26];
      if (!processedBeans.contains(ppName)) {
          currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
          reiterate = true;
          }
      }

      sortPostProcessors(currentRegistryProcessors, beanFactory);
      registryProcessors.addAll(currentRegistryProcessors);
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      currentRegistryProcessors.clear();
}

可以看到,其实除了第一次执行BeanDefinitionRegistry后置处理器,是边判断边执行,后三次执行BeanDefinitionRegistry后置处理器的方法很类似,只不过第二次执行的的需要继承PriorityOrdered接口,第三次需要继承Ordered接口。

等到第四次的时候,由于又while循环的存在,只要是BeanDefinitionRegistry后置处理器,且没有被执行过,就可以一直执行下去。

了解了这些,其实我们就可以完全自定义我们需要的任何BeanDefinitionRegistry后置处理器,同时还能指定它的执行顺序。

其实Springboot的所有设计思想,都藏在它的源码里,所以说学习Spring最好的方法,还是看源码。

由于第四次只是新增了一个TokenKeyEndpointRegistrar的BeanDefinitionRegistry后置处理器实现类,所以当前while循环只会执行一次,其执行方法为:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            this.registry = registry;
        }

简单来说,就是把IOC容器注入到了TokenKeyEndpointRegistrar这个类中,供后续处理。

5.2.5 第一次执行BeanFactory的后置处理器

这这一部分代码的最后,我们终于可以执行BeanFactory的后置处理器了:

invokeBeanFactoryPostProcessors((Collection)registryProcessors, (ConfigurableListableBeanFactory)beanFactory);
invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

可以看到,其实是同样的方法,就是参数不同,也就是说指向的beanFactory后置处理器有优先级之分,先执行registry类型的,再执行regular类型的。

先看看registtry类型的后置处理器有哪些:

[org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, org.springframework.context.annotation.ConfigurationClassPostProcessor, org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration$TokenKeyEndpointRegistrar]

前两个beanFactory后置处理器的方法为空,所以重要的在第三、四个后置处理器的方法:

ConfigurationClassPostProcessor作用

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //获取容器id
        int factoryId = System.identityHashCode(beanFactory);
        if (this.factoriesPostProcessed.contains(factoryId)) {
            //如果容器id加入过集合factoriesPostProcessed报错
            throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);
        } else {
            //只有没有被加入过集合factoriesPostProcessed的容器id,才能加入集合
            this.factoriesPostProcessed.add(factoryId);
            if (!this.registriesPostProcessed.contains(factoryId)) {
            //如果没有被加入过BeanDefinitionRegistry后置处理器,需要重新扫描当前工程主启动类下所有class文件,及Spring的默认加载class文件,并把bean信息注册到beanFactory中
                this.processConfigBeanDefinitions((BeanDefinitionRegistry)beanFactory);
            }
            //使用cglib代理增强beanFactory,可以使用类似AOP的拦截器
            this.enhanceConfigurationClasses(beanFactory);
            //添加一个新的beanFactory后置处理器
            beanFactory.addBeanPostProcessor(new ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor(beanFactory));
        }
    }

 可以看到其主要作用就是在方法enhanceConfigurationClasses中,使用cglib代理增强了beanFactory,和添加了一个beanFactory的后置处理器。

如果仔细追踪cglib代理增强,可以看到其实是对beanFactory中,满足一定条件的bean进行增强,满足什么条件呢?如果bean定义信息中的如下属性值为“full”,则放入一个新的map中,在后面进行集中增强:

org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass

且不能加有@Configuration注解,也不能是一个静态方法:

if ("full".equals(configClassAttr)) {
                if (!(beanDef instanceof AbstractBeanDefinition)) {
                    throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
                }

                if (this.logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
                    this.logger.info("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.");
                }

                configBeanDefs.put(beanName, (AbstractBeanDefinition)beanDef);
            }

之后,便是循环遍历map,来对满足条件的bean进行增强:

if (!configBeanDefs.isEmpty()) {
            ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
            Iterator var14 = configBeanDefs.entrySet().iterator();

            while(var14.hasNext()) {
                Entry<String, AbstractBeanDefinition> entry = (Entry)var14.next();
                AbstractBeanDefinition beanDef = (AbstractBeanDefinition)entry.getValue();
                beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
                Class<?> configClass = beanDef.getBeanClass();
                Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

 TokenKeyEndpointRegistrar作用

主要是利用传入的beanFactory参数,来获取JwtAccessTokenConverter的继承类,并把获取到的类的定义信息注册到IOC容器中。

注意这里的registry是在前文中,TokenKeyEndpointRegistrar的BeanDefinitionRegistry后置处理器方法中被注入的。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, JwtAccessTokenConverter.class, false, false);
            if (names.length > 0) {
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
                builder.addConstructorArgReference(names[0]);
                this.registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
            }
        }

5.2.6 第二次执行BeanFactory的后置处理器

invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

执行regularPostProcessors集合中的beanFactory后置处理器。

此时regularPostProcessors集合数据为:

org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

 PropertySourceOrderingPostProcessor作用

主要是对容器中environment参数中存在的defaultProperties属性值进行重排序,排到最后。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            this.reorderSources(this.context.getEnvironment());
        }

        private void reorderSources(ConfigurableEnvironment environment) {
            PropertySource<?> defaultProperties = environment.getPropertySources().remove("defaultProperties");
            if (defaultProperties != null) {
                environment.getPropertySources().addLast(defaultProperties);
            }

        }

5.2.7 上半部分代码总结

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
        Set<String> processedBeans = new HashSet();
        ArrayList regularPostProcessors;
        ArrayList registryProcessors;
        int var9;
        ArrayList currentRegistryProcessors;
        String[] postProcessorNames;
        if (beanFactory instanceof BeanDefinitionRegistry) {
            //5.2节前面所有流程
            .......
        } else {
            invokeBeanFactoryPostProcessors((Collection)beanFactoryPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        }

可以看到,在类PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法中,我们在前面讲到的所有5.2节中的内容,都是if条件满足后执行的方法。

也就是说,前面方法执行的基础就是当前的beanFactory是BeanDefinitionRegistry类型的,才会先执行BeanDefinitionRegistry类型的后置处理器,再执行beanFactory的后置处理器。

否则,会直接执行所有的beanFactory的后置处理器方法。

5.2.8 第三次执行BeanFactory的后置处理器

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
        regularPostProcessors = new ArrayList();
        registryProcessors = new ArrayList();
        currentRegistryProcessors = new ArrayList();
        postProcessorNames = postProcessorNames;
        int var20 = postProcessorNames.length;

        String ppName;
        for(var9 = 0; var9 < var20; ++var9) {
            ppName = postProcessorNames[var9];
            if (!processedBeans.contains(ppName)) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    regularPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    registryProcessors.add(ppName);
                } else {
                    currentRegistryProcessors.add(ppName);
                }
            }
        }

        sortPostProcessors(regularPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

执行完所有的BeanDefinitionRegistry类型的后置处理器方法,再执行了两次beanFactory的后置处理器方法。

我们终于进入invokeBeanFactoryPostProcessors这个方法的下半部分,又要开始执行beanFactory的后置处理器方法。

注意这里的beanFactory的后置处理器获取方式和前面的获取方式是不同的:

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

这里是按类型BeanFactoryPostProcessor类型去获取的,而前面的beanFactory的后置处理器是按BeanDefinitionRegistryPostProcessor类型去获取的。

这也是为什么前面的beanFactory的后置处理器BeanDefinitionRegistry类型的后置处理器有的都归属一个类,前面先执行了BeanDefinitionRegistry类型的方法,为下一次执行beanFactory的后置处理器做了准备。

设计的也是很巧妙。

进入到这里呢,真正执行第三次beanFactory的后置处理器的前面,也和前面的BeanDefinitionRegistry后置处理器一样,区分成了三类:PriorityOrdered、Ordered、current,按优先级做了分类。那显然它们也是依次按顺序执行了。

在这里我们可以看到满足PriorityOrdered条件的后置处理器没有,略过这一段。

5.2.9 第四次执行BeanFactory的后置处理器

List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList(registryProcessors.size());
        Iterator var21 = registryProcessors.iterator();

        while(var21.hasNext()) {
            String postProcessorName = (String)var21.next();
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }

        sortPostProcessors(orderedPostProcessors, beanFactory);
        invokeBeanFactoryPostProcessors((Collection)orderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

执行继承了Ordered接口的后置处理器方法,由于本项目使用的是JPA持久层框架,这一步会把JPA相关的类注册到容器中,获取到的后置处理器为:

org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor
org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator

其后置处理器方法作用分别为:

EntityManagerBeanDefinitionRegistrarPostProcessor作用

通过JPA框架中指定的工具类BeanDefinitionUtils,获取预先定义好的一系列类信息,结合传入的IOC容器的factoryBean信息,依次构建EntityManagerFactoryBeanDefinition对象,生成bean的定义信息,最后注册到IOC容器中。

这样在后续大规模的生成bean实例后,可以直接从容器中获取JPA的相关信息,此时注册的bean有:

static {
        List<Class<?>> types = new ArrayList();
        types.add(EntityManagerFactory.class);
        types.add(AbstractEntityManagerFactoryBean.class);
        if (ClassUtils.isPresent("org.springframework.jndi.JndiObjectFactoryBean", ClassUtils.getDefaultClassLoader())) {
            types.add(JndiObjectFactoryBean.class);
        }
        EMF_TYPES = Collections.unmodifiableList(types);
    }

ConfigurationPropertiesBeanDefinitionValidator作用

主要做yml配置方面的校验:

private void validate(ConfigurableListableBeanFactory beanFactory, String beanName) {
        try {
            Class<?> beanClass = beanFactory.getType(beanName, false);
            if (beanClass != null && BindMethod.forType(beanClass) == BindMethod.VALUE_OBJECT) {
                throw new BeanCreationException(beanName, "@EnableConfigurationProperties or @ConfigurationPropertiesScan must be used to add @ConstructorBinding type " + beanClass.getName());
            }

5.2.10 第五次执行BeanFactory的后置处理器

List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList(currentRegistryProcessors.size());
        Iterator var24 = currentRegistryProcessors.iterator();

        while(var24.hasNext()) {
            ppName = (String)var24.next();
            nonOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }

        invokeBeanFactoryPostProcessors((Collection)nonOrderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

执行优先级最低的后置处理器方法:

[org.springframework.context.event.EventListenerMethodProcessor, org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor]

EventListenerMethodProcessor作用

主要是从beanFactory中,获取EventListenerFactory的实现类,进行排序后,在放入当前类的集合eventListenerFactories中,为后续创建监听器做准备。注意这里的实现类应该是Spring利用默认加载路径自动装配进去的:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
        List<EventListenerFactory> factories = new ArrayList(beans.values());
        AnnotationAwareOrderComparator.sort(factories);
        this.eventListenerFactories = factories;
    }

RsaKeyConversionServicePostProcessor作用

用于ssl公钥私钥的解析,一般来说,知道就行。

PreserveErrorControllerTargetClassPostProcessor作用

给beanFactory中ErrorController实现类的属性preserveTargetClass,设置固定值。

5.2.11 清理beanFatcory缓存

beanFactory.clearMetadataCache();

主要就是设置一些beanFactory的属性,和清空一些map信息。

6 总结

本章的重点在beanFactory的后置处理器执行原理,其实所谓的原理就是分别执行了4次BeanDefinitionRegistry后置处理器的指定方法,和5次beanFactory后置处理器指定方法。

通过上文的源码解析,可以看到大致来说就是把所有的后置处理器分成了三类:PriorityOrdered最高优先级、Ordered高优先级和普通优先级,依次执行,如果需要自定义后置处理器,且要指定其执行顺序,实现对应的优先级接口即可。

其中有几个需要注意的点:包括BeanDefinitionRegistry后置处理器第一次的执行和第二次执行,以及如何设置beanFactory后置处理器优先级高于PriorityOrdered。

BeanDefinitionRegistry后置处理器第一次的执行

可以认为主要是做一些准备工作,为后续需要用到的BeanDefinitionRegistry后置处理器ConfigurationClassPostProcessor设置属性,以及校验当前工程包名是否合法。

BeanDefinitionRegistry后置处理器第二次的执行

这里也是最重要的一步,其执行了ConfigurationClassPostProcessor处理器的指定方法,把当前工程中符合条件的类信息、Spring框架默认的类信息,全部注册进了IOC容器。为下一步执行做好了必须的信息准备。

设置beanFactory后置处理器优先级高于PriorityOrdered

通过源码可以知道,如果一个类同时实现了BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口,它是可以在BeanFactoryPostProcessor的PriorityOrdered优先级之前执行的,这对我们了解其执行时机有重要意义。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值