目录
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);
通过同步方法块进入后,我们来看一下每一步都做了什么。
为了方便理解,我们实行分步、分段讲解。
第一步,会设置一些当前实例,其实也就是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());
}
}
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);
}
this.getEnvironment().validateRequiredProperties();
主要作用就是校验environment中key-value形式的参数配置中,有些必输参数的value是否为空,如果为空会抛出运行时异常。
在前文执行prepareContext方法的过程中,在加载初始化器和prepareContext最后一步执行监听器方法的时候,会分别把SpringApplication实例化过程中通过spring.factories配置文件加载和代码直接加载的监听器都添加到IOC自己的监听器中,也就是其参数
private final Set> applicationListeners
在这里,会把监听器和监听事件进一步处理:
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
} else {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.earlyApplicationEvents = new LinkedHashSet();
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;
}
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方法。
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();
}
可以理解,这一步依然是一些准备工作,但是它和上面的准备工作不同,不是通用的,需要根据不同的子类来实现。
this.invokeBeanFactoryPostProcessors(beanFactory);
这一步,真正开始执行,在环境准备阶段加载的beanFactory后置处理器。
核心功能便是下面这一个:
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
在执行之前,首先会通过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。
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的后置处理器调用调用,也是为下一次做准备。并不是无意义的代码重复。
以上两点,就是执行这段方法的主线,要把握主线,才不至于被代码绕到云山雾罩。
下面我们详细解读代码,为了便于理解,依旧分段解析。
先介绍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 scannedPackages = this.getComponentScanningPackages(registry);
List 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 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的规范。
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 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定义信息到容器后,为下面继续执行的方法做了准备。
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集合为空,跳过。
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这个类中,供后续处理。
这这一部分代码的最后,我们终于可以执行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 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());
}
}
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);
}
}
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
Set 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的后置处理器方法。
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条件的后置处理器没有,略过这一段。
List 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> 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());
}
List 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 beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
List factories = new ArrayList(beans.values());
AnnotationAwareOrderComparator.sort(factories);
this.eventListenerFactories = factories;
}
RsaKeyConversionServicePostProcessor作用
用于ssl公钥私钥的解析,一般来说,知道就行。
PreserveErrorControllerTargetClassPostProcessor作用
给beanFactory中ErrorController实现类的属性preserveTargetClass,设置固定值。
beanFactory.clearMetadataCache();
主要就是设置一些beanFactory的属性,和清空一些map信息。
本章的重点在beanFactory的后置处理器执行原理,其实所谓的原理就是分别执行了4次BeanDefinitionRegistry后置处理器的指定方法,和5次beanFactory后置处理器指定方法。
通过上文的源码解析,可以看到大致来说就是把所有的后置处理器分成了三类:PriorityOrdered最高优先级、Ordered高优先级和普通优先级,依次执行,如果需要自定义后置处理器,且要指定其执行顺序,实现对应的优先级接口即可。
其中有几个需要注意的点:包括BeanDefinitionRegistry后置处理器第一次的执行和第二次执行,以及如何设置beanFactory后置处理器优先级高于PriorityOrdered。
BeanDefinitionRegistry后置处理器第一次的执行
可以认为主要是做一些准备工作,为后续需要用到的BeanDefinitionRegistry后置处理器ConfigurationClassPostProcessor设置属性,以及校验当前工程包名是否合法。
BeanDefinitionRegistry后置处理器第二次的执行
这里也是最重要的一步,其执行了ConfigurationClassPostProcessor处理器的指定方法,把当前工程中符合条件的类信息、Spring框架默认的类信息,全部注册进了IOC容器。为下一步执行做好了必须的信息准备。
设置beanFactory后置处理器优先级高于PriorityOrdered
通过源码可以知道,如果一个类同时实现了BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口,它是可以在BeanFactoryPostProcessor的PriorityOrdered优先级之前执行的,这对我们了解其执行时机有重要意义。