本篇文章给大家谈谈SpringBoot测试类可以重用应用程序上下文以更快地运行测试吗?,以及springboottest怎么用的知识点,同时本文还将给你拓展java–使用Web应用程序上下文的Spri
本篇文章给大家谈谈Spring Boot测试类可以重用应用程序上下文以更快地运行测试吗?,以及springboot test怎么用的知识点,同时本文还将给你拓展java – 使用Web应用程序上下文的Spring上下文层次结构、java-Spring Boot:在类路径更改时刷新应用程序上下文、pringboot2.0.6启动解析(九)刷新应用程序上下文、pringboot2.0.6启动解析(十)刷新应用程序上下文-IoC容器的初始化等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:- Spring Boot测试类可以重用应用程序上下文以更快地运行测试吗?(springboot test怎么用)
- java – 使用Web应用程序上下文的Spring上下文层次结构
- java-Spring Boot:在类路径更改时刷新应用程序上下文
- pringboot2.0.6启动解析(九)刷新应用程序上下文
- pringboot2.0.6启动解析(十)刷新应用程序上下文-IoC容器的初始化
Spring Boot测试类可以重用应用程序上下文以更快地运行测试吗?(springboot test怎么用)
@ContextConfiguration
对于Spring
Boot集成测试,location属性没有意义。还有其他方法可以在多个带有注解的测试类中重用应用程序上下文@SpringBootTest
吗?
答案1
小编典典是。实际上这是默认行为。链接指向Spring Framework文档,由Spring
Boot在后台使用。
顺便说一句,默认情况下,上下文也会在@ContextConfiguration
使用时重用。
java – 使用Web应用程序上下文的Spring上下文层次结构
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
现在有一些模块应该使用ContextSingletonbeanfactoryLocator在运行时加载.因此每个模块都有自己的ClasspathXmlApplicationContext.所以一个模块可以引用XmlWebApplicationContext中的bean,它应该被附加到XmlWebApplicationContext以形成一个上下文层次结构,其中XmlWebApplicationContext应该扮演父类的角色,并且该模块的ClasspathXmlApplicationContext是子上下文的角色.不幸的是我无法连接他们使用
<beans> <bean id="moduleContext"https://www.jb51.cc/tag/ssp/" target="_blank">sspathXmlApplicationContext"> <constructor-arg> ... </constructor-arg> <constructor-arg ref="parentContext" /> </bean> </beans>
因为我没有找到没有办法给WebApplicationContext的名称为parentContext.我忽略了一些东西,还是有一种更好/更容易的方式来实现不同的方式?
解决方法
@Inject private XmlWebApplicationContext context; @Inject private List<ClasspathXmlApplicationContext> childs; @postconstruct public void refreshContext() { for(ClasspathXmlApplicationContext appContext : childs) { appContext.setParent(context); } context.refresh(); }
您也可以通过使用接口InitializingBean和ApplicationContextAware来进行注释.
编辑:childs是按类型自动连线的,所以Spring将注入作为ClasspathXmlApplicationContext实例的所有bean.
java-Spring Boot:在类路径更改时刷新应用程序上下文
我使用Spring Boot的PropertiesLauncher和Loader-Path:插件清单条目构建了一个小型应用程序.在plugins文件夹中有几个jar文件,其中包含meta-inf / spring.factories.
加载我的外部插件jar的配置可以正常工作.
现在,我想知道如果类路径上的某些内容发生更改,是否以及如何刷新应用程序上下文.例如,删除或添加外部插件jar.
我已经看过SpringApplicationRunListener了,但是我想我不能通过此类来实现.
有没有想法,以及如何实现?
在2009/2010年,有Spring Dynamics Modules个项目,但有it seems abandoned now个项目.
您很可能必须自己重新实现.也许您应该研究现有的插件系统实现,例如实现OSGi的Apache Felix:
Apache Felix is a community effort to implement the Osgi Framework and Service platform and other interesting Osgi-related technologies under the Apache license. The Osgi specifications originally targeted embedded devices and home services gateways,but they are ideally suited for any project interested in the principles of modularity,component-orientation,and/or service-orientation. Osgi technology combines aspects of these aforementioned principles to define a dynamic service deployment framework that is amenable to remote management.
pringboot2.0.6启动解析(九)刷新应用程序上下文
解析SpringApplication的run方法中applicationContext相关分析
public ConfigurableApplicationContext run(String... args) {
try {
......//省略
// 刷新Context容器实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作
refreshContext(context);
// 刷新Context容器之后处理
afterRefresh(context, applicationArguments);
stopWatch.stop();// 执行结束,记录执行时间
......//省略
}
......//省略
} catch (Throwable ex) {
// 启动错误报告处理
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
......//省略
}
刷新上下文
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
1. refresh 刷新
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
---------AbstractApplicationContext--------
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//刷新上下文环境
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//获取beanFactory,还给其设置了refreshed标识
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备bean工厂,在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 设置beanFactory 后置处理器
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用 beanFactory 后置处理器 ,向bean工厂中注册bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.postProcessBeanFactory
// 注册beanPostProcessor(会确保spring的internalPostProcessors)一定是排在beanPostProcessor的集合最后
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 给spring的容器和其父容器设置messageSource
initMessageSource();
// Initialize event multicaster for this context.
// 给spring的容器注册一个事件广播器applicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 初始化tomcat
onRefresh();
// Check for listener beans and register them.
// 注册所有的ApplicationListener,并且执行之前积攒的earlyApplicationEvents
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//实例化所有非懒加载的单例bean、bean封装、属性注入、注解注入(主要使用BeanPostProcessor或子类实现)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//清理resource的缓存,初始化LifecycleProcessor用来执行finishBeanFactoryInitialization,
//发布ContextRefreshedEvent将该Spring容器注册到LiveBeansView,启动tomcat发布tomcat启动事件
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset ''active'' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring''s core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
1. prepareRefresh
清除ClassPathBeanDefinitionScanner的缓存。设置servletContext和servletConfig,如果这个两个属性不为空则替换environment的source中关于这个两个的属性,校验必须存在的properties是否存在(我们可以设置哪些properties)。设置earlyApplicationEvents,存储早期的ApplicationEvents,当所有的ApplicationContextListener都注册进入容器之后在执行这些事件
// Reactiv类型中调用
该方法第一步调用子类的 prepareRefresh()方法子类的prepareRefresh方法清除CachingMetadataReaderFactory中的缓存然后继续调用父类的prepareRefresh方法
protected void prepareRefresh() {
// 清除扫描仪缓存(清除ClassPathBeanDefinitionScanner的缓存)
this.scanner.clearCache();
// 准备刷新环境
super.prepareRefresh();
}
// 设置相关属性 检验environment的properties是否正确
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment
initPropertySources();
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
initPropertySources会调用GenericWebApplicationContext的实现 就是设置容器的环境初始化容器的属性 earlyApplicationEvents 在 multicaster 可用的时候进行发布
2. obtainFreshBeanFactory
前面文章我们讲过创建应用的上下文时会并触发了GenericApplicationContext类的构造方法,创建了beanFactory。也就是创建了DefaultListableBeanFactory类。GenericApplicationContext方法如下所示
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
obtainFreshBeanFactory方法其实就是拿到我们之前创建的 beanFactory 对象,ConfigurableListableBeanFactory 是在初始化容器的时候其父类容器也被初始化时才被赋值的,spring容器是通过 ConfigurableListableBeanFactory 这个bean工厂进行真正的bean加载。其主要做了三个动作,刷新 beanFactory,获取 beanFactory,返回 beanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新BeanFactory
refreshBeanFactory();
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractApplicationContext的两个子类AbstractRefreshableApplicationContext和GenericApplicationContext实现了refreshBeanFactory()方法,由于我们在创建上下文的时候实例化了GenericApplicationContext类,所以这里进入了GenericApplicationContext类的refreshBeanFactory()。如下所示
protected final void refreshBeanFactory() throws IllegalStateException {
//this.refreshed.compareAndSet(false, true)-->刷新GenericApplicationContext且只刷新一次
//注意:this.refreshed属性: private final AtomicBoolean refreshed = new AtomicBoolean();
//java J.U.C并发包中很重要的一个原子类AtomicBoolean。通过该类的compareAndSet()方法可以实现一段代码绝对只实现一次的功能。
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call ''refresh'' once");
}
//指定序列化的id,当需要反序列化这个BeanFactory时,则可以直接根据这个id来进行反序列化
this.beanFactory.setSerializationId(getId());
}
3. prepareBeanFactory
在注册bean之前先给beanFactory配置一些属性,它们可以帮助beanFactory后期去解析、注册其他的bean,比如忽略依赖,指定依赖等等
- ignoreDependencyInterface:当我们spring指定自动注入的时候会忽略对这个指定参数的EnvironmentAware的注入
- registerResolvableDependency:当我们spring指定自动注入的时候会只注入我们指定的参数属性
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context''s class loader etc.
// 配置类加载器:默认使用当前上下文的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 配置EL表达式:在Bean初始化完成,填充属性的时候会用到
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 添加属性编辑器 PropertyEditor
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 添加Bean的后置处理器,使用上下文回调配置beanFactory
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// 将以下类注册到 beanFactory(DefaultListableBeanFactory) 的resolvableDependencies属性中
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
// 将后处理器注册为application监听器,用于检测内部bean
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 检测是否存在代码织入,如果当前BeanFactory包含loadTimeWeaver Bean,说明存在类加载期织入AspectJ,则把当前BeanFactory交给类加载期BeanPostProcessor实现类LoadTimeWeaverAwareProcessor来处理,从而实现类加载期织入AspectJ的目的。
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// 将当前环境变量(environment) 注册为单例bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// 将当前系统配置(systemProperties) 注册为单例Bean
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
// 将当前系统环境 (systemEnvironment) 注册为单例Bean
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
上述代码的作用如下:
- 设置beanFactory的classLoader,一般都是线程上下文类加载器
- 设置beanFactory的BeanExpressionResolver,用来根据bean的表达式来解析bean
- 增加ResourceEditorRegistrar(属性编译器),主要是注册用户或者系统定义的属性编辑器
- 添加BeanPostProcessor(后置处理器)--ApplicationContextAwareProcessor
- BeanPostProcessor 提供了2个方法
- postProcessBeforeInitialization(该方法在调用bean的初始化方法(注意是初始化方法此时bean早已经实例化好了)前调用
- postProcessAfterInitialization(该方法是在调用bean的初始化方法后进行调用)
- BeanPostProcessor 提供了2个方法
注意@service 、@component 都没有初始化方法 但是@Bean注解有,还有需要注意的是若是上述2中方法对 bean 进行修改那么原始的bean就会被替换,而ApplicationContextAwareProcessor则是只修改postProcessBeforeInitialization,方法内部就是判断 bean 是否属于EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware不同的aware调用不同的方法细节看invokeAwareInterfaces方法
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
上述代码中可以看出ignoreDependencyInterface 则是正好对应着上述的EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware进行处理。其真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖。
如果想忽略指定类的自动注入使用ignoreDependencyType,autowiring(自动装配)特定指的是通过beans标签default-autowire属性来依赖注入的方式 即@Bean注解。而不是指使用@Autowired注解进行的依赖注入。区别在于,使用default-autowire会自动给所有的类从容器中查找匹配的依赖并注入,而使用@Autowired注解只会给这些注解的对象从容器查找依赖并注入。
所谓的自动装配是指我们配置bean的xml 或者@Bean的时候 会自动帮我们注入属性(比如我们配置A类的B属性=8),那么容器就会给我们生成一个属性为8的A的实例对象
如果上述几个Aware不使用ignoreDependencyInterface会有什么问题尼?很简单 如果我们写一个实现类,然后给他默认配置ApplicationContext属性,而这个ApplicationContext是我们自己new的,这就导致了我们使用的不是spring自己生成的ApplicationContext,所以spring为了避免我们手误自己注册下这个属性,而帮我们忽略。
registerResolvableDependency意思更简单了
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
上述代码的意思就是当我们使用自动注入比如@AutoWired注入BeanFactory 类型是那么他会帮我们注入我们目前输入的实例对象,而不会去注入他的其他实现类,这是为了避免我们使用其他的自定义的实例对象
4. postProcessBeanFactory
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(
new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (!ObjectUtils.isEmpty(this.basePackages)) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
设置ignoreDependencyInterface(ServletContextAware.class),标识对于ServletContextAware的实现类中的ServletContextAware属性禁止自动注入,如果存在basePackages和annotatedClasses,则扫描basePackages,并把扫描到的class和annotatedClasses注册到spring容器中
postProcessBeanFactory方法其实就是向上下文中添加了一系列的Bean的后置处理器,后置处理器工作的时机是在所有的beanDenifition加载完成之后,bean实例化之前执行。简单来说Bean的后置处理器是用来修改BeanDefinition的属性信息的。
5. invokeBeanFactoryPostProcessors
该方法中完成IoC容器的初始化。该方法比较重放到后面文章单独分析
6. registerBeanPostProcessors
registerBeanPostProcessors方法是在注册bean过程中调用的,那我们先来看看BeanPostProcessor接口
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
该接口有两个方法,其作用如下
- postProcessBeforeInitialization作用:
- 在bean实例化、依赖注入之后,初始化之前执行。什么叫初始化?我们知道在bean实例化之前,已经初始化对象了,这里初始化指的是,给bean的某些属性赋值或去执行的某些方法。比如init-method方法,其就是在实例化之后调用的。
- postProcessAfterInitialization作用:
- 初始化之后执行。我们知道BeanFactoryPostProcessor是用来处理bean工厂或者是bean定义的,那么BeanPostProcessor就是用来处理bean实例的。
下面来看看registerBeanPostProcessors
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
PostProcessorRegistrationDelegate类
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
//取得所有的bean后置处理器名称
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// 注册 BeanPostProcessorChecker
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
//缓存实现priorityOrdered接口的bean
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 排序、注册
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// 缓存实现Ordered接口的bean
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
// 排序、注册
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// 缓存没有实现两个接口的bean
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
// 注册
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// 排序注册所有的实现了MergedBeanDefinitionPostProcessor接口的bean
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// 注册ApplicationListenerDetector,用来检查所有得ApplicationListener
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
- 首先在 IoC 容器中获取所有的 bean 后置处理器名称集合postProcessorNames。然后在容器中注册一个BeanPostProcessorChecker,其在beanPostBean创建bean的时候打印信息或用来对在bean不适合所有的BeanPostProcessor调用的情况下,打印一些日志信息。
- 遍历处理postProcessorNames集合,先注册实现了PriorityOrdered接口的BeanPostProcessor,再注册实现了Ordered接口的的BeanPostProcessor,然后注册什么接口都没实现的BeanPostProcessor,最后注册实现了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor。
- 在容器中注册 ApplicationListenerDetector 用来检测bean是否是ApplicationListener,判断是否是单例,不是单例则删除singtonNames中对应的key。
注意,这里只是注册,相当于整理所有的BeanPostProcessor,把其提出来,按执行循序放到list中,供后续调用。原来我们只知道所有的BeanPostProcessor都在IoC容器中以bean的形式存在着,但是我们不知道应该如何去执行,以什么顺序去执行。经过该方法解决了该问题。
7. initMessageSource
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name ''" + MESSAGE_SOURCE_BEAN_NAME +
"'': using default [" + this.messageSource + "]");
}
}
}
主要做国际化处理,其首先检测是否存在messageSource的bean,若是存在获取这个bean。 若其父容器存在且messageSource实现HierarchicalMessageSource接口,当父容器存在,设置该父容器的messageSource为当前容器的父messageSource,不存在则设置父容器本身。设置父messageSource的意义是当messageSource无法解析就交给父messageSource。若不存在则设一个emtpymessageSource(DelegatingMessageSource ),继续按照上述逻辑设置父MessageSource ,然后注册该bean
8. initApplicationEventMulticaster
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
// 获取IoC容器
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断IoC容器否包含"applicationEventMulticaster",如果包含则获取该bean,设置到applicationEventMulticaster属性中
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}else {// 如果不包含"applicationEventMulticaster",则获取该bean注册该bean,设置到applicationEventMulticaster属性中
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name ''" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"'': using default [" + this.applicationEventMulticaster + "]");
}
}
}
初始化容器的组播组件,该方法主要是查看当前beanName是否存在对应的ApplicationEventMulticaster ,存在就注册该bean 不存在就设置SimpleApplicationEventMulticaster为组播组件。其用意是先看看是否有自定义的广播器,有就直接用,没有则用默认的SimpleApplicationEventMulticaster广播器,并且把其设置到应用上下文的applicationEventMulticaster属性中。
9. onRefresh
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don''t consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : "
+ StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
}
}
创建WebServer
- 获取webServer和servletcontext,如果它们都为null 则去获取ServletWebServerFactory的bean
- ServletContextInitializer 存在的作用主要是让ServletContextInitializer 可以被spring容器管理 而不被servlet容器管理,且与WebApplicationInitializer不同的是实现 ServletContextInitializer 的实现类未实现 WebApplicationInitializer ,因此不会被SpringServletContainerInitializer检测到,所以不会有servlet容器自动引导。其onStartup方法 主要是配置ServletContext,比如any servlets, filters, listeners context-params and attributes
- WebApplicationInitializer 可以看做是Web.xml的替代,它是一个接口。通过实现WebApplicationInitializer,在其中可以添加servlet,listener等,在加载Web项目的时候会加载这个接口实现类,从而起到web.xml相同的作用
- SpringServletContainerInitializer作为ServletContainerInitializer的实现类,通过SPI机制,在web容器加载的时候会自动的被调用。(这个类上还有一个注解@HandlesTypes,它的作用是将感兴趣的一些类注入到ServletContainerInitializerde), 而这个类的方法又会扫描找到WebApplicationInitializer的实现类,调用它的onStartup方法,从而起到启动web.xml相同的作用
- getWebServer 主要是设置设置和启动内置tomcat 设置Connector (默认Nio协议)customizeConnector调整Connector
- prepareContext 就是设置host host内部持有的是spring的TomcatEmbeddedContext
- 最后获取TomcatWebServer并初始化,即设置LifecycleListener并且启动tomcat
10. registerListeners
protected void registerListeners() {
// 和BeanPostProcess一样,可以通过set手动注册监听器.
for (ApplicationListener<?> listener : getApplicationListeners()) {
//绑定监听器到广播器
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 获取监听器的名称集合
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
// 遍历监听器集合,将其设置到广播器
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 获取早期应用事件集合
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
// 早期应用事件存在,则遍历发布
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
注册监听器与广播器是同时存在的。之前只是初始化广播器,但是并没有为广播器绑定监听器。该方法就是对其进行了绑定。对于spring配置的监听器,在这里并没有做处理只是设置的名字。那么怎么进行绑定呢?还记得之前注册的ApplicationListenerDetector的后置处理器吧,这个后置处理器就是用来向广播器绑定监听器的。只是只在初始化bean的时候用到的。
11. finishBeanFactoryInitialization
实例化所有的非懒加载的bean对象,即实现IoC容器的依赖注入。主要步骤如下
- 设置转换器,即设置beanFactory 的conversion service(其实作为JavaBeans PropertyEditors.的替代者 用来转换属性值)
- 注册一个默认的属性值解析器,为嵌入值(例如注释属性)添加String解析器。
- 初始化LoadTimeWeaverAware bean以允许尽早注册其变换器
- 冻结所有的bean定义,该步之后注册的bean定义将不能被修改或进一步的处理
- 实例化剩余的非懒加载的bean,即实例化非延迟初始化加载的bean // 实例化所有非延迟初始化(加载)的bean
12. finishRefresh
发布容器事件,结束Refresh过程主要做一下操作
- 清除上下文级资源缓存(来自扫描的ASM元数据)。
- 初始化容器的生命周期处理器DefaultLifecycleProcessor,其有start方法和stop方法两个方法,调用start方法开始生命周期,调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如设置一直轮询kafka
- 按照phase的大小依次启动lifecycle processor
- 通过spring的事件发布机制发布ContextRefreshedEvent事件,保证对应的监听器做进一步的处理,如一些需要在启动后处理的类,即实现了ApplicationListener<ContextRefreshedEvent>接口的类。发布ContextRefreshedEvent事件就是触发这些类的执行(执行onApplicationEvent方法)另外。
- spring的内置事件有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent等。
完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
- spring的内置事件有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent等。
13. destroyBeans
摧毁所有单例
14. cancelRefresh
设置active表示为false
15. resetCommonCaches
- 清除反射的缓存
- 清除注解的相关缓存
- Clear the internal {@code ResolvableType}/{@code SerializableTypeWrapper} cache.
- 清除classloader缓存
pringboot2.0.6启动解析(十)刷新应用程序上下文-IoC容器的初始化
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
......//省略
// 调用 beanFactory 后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
......//省略
}
}
invokeBeanFactoryPostProcessors
该方法中完成IoC容器的初始化。具体步骤如下
- Resource定位
- 我们知道在SpringBoot中包扫描默认是从主类所在的包开始扫描的,在准备应用上下文时,即在prepareContext()方法中会先将主类解析成BeanDefinition,然后在refresh方法中调用invokeBeanFactoryPostProcessors方法,其解析主类的BeanDefinition获取basePackage的路径,完成扫描路径的定位。其次SpringBoot的starter是通过SPI扩展机制实现的自动装配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors方法中触发实现的。还另外在SpringBoot中还有很多的@EnableXXX注解,查看得知其底层是@Import注解,在invokeBeanFactoryPostProcessors方法中也实现了对该注解指定的配置类的定位加载。
- 常规的在SpringBoot中有四种实现定位
- 主类所在包的
- @ComponentScan指定扫描目录
- SPI扩展机制实现的自动装配(比如各种starter)
- @Import注解指定的类。(对于非常规的不说了)
- BeanDefinition的载入
- 找到了扫描包的位置,定位后紧接着要做的就是将BeanDefinition的分别载入。所谓的载入就是通过上面的定位得到的basePackage的路径拼接成“classpath*:org/springframework/boot/demo/**/*.class”这样的形式,PathMatchingResourcePatternResolver类会将该路径下所有的 .class 文件都加载进来,然后遍历判断是否有@Component注解,如果有的话其就是我们要装载的BeanDefinition。
- 注意@Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了
- 注册BeanDefinition
- 这个过程是通过调用前文中提到的BeanDefinitionRegister接口的实现来完成。其把载入过程中解析得到的BeanDefinition向IoC容器进行注册,即在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
进入PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
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();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
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();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
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();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
invokeBeanFactoryPostProcessors看着挺长其实里面的内容还是比较好理解的。其主要就是对BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的bean进行处理。
- BeanDefinitionRegistryPostProcessor
- 首先判断传入的beanFactory是不是BeanDefinitionRegistry的实例。
-
beanFactory是BeanDefinitionRegistry,我们开始处理BeanFactoryPostProcessor,即遍历每一个对象,验证是不是BeanDefinitionRegistryPostProcessor(BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口)的实例。是将其强制类型转换,然后执行BeanDefinitionRegistryPostProcessor的生命周期方法postProcessBeanDefinitionRegistry,并且将这个BeanFactoryPostProcessor放入到registryPostProcessors集合中。不是则直接放入到regularPostProcessors集合中。它们都放入集合中之后我们就可以处理集合中的属于BeanDefinitionRegistryPostProcessor类型的Bean了。
获取BeanDefinitionRegistryPostProcessor类型的Bean的集合,处理这些bean,即遍历每一个对象,验证是不是PriorityOrdered类型的,是放入currentRegistryProcessors集合中并把bean名称放入processedBeans(已经处理过的Bean的集合)集合中,防止重复执行,然后排序,把该集合加入registryProcessors集合中。在invokeBeanDefinitionRegistryPostProcessors方法中,处理currentRegistryProcessors集合,即遍历每一个对象,执行BeanDefinitionRegistryPostProcessor的生命周期方法postProcessBeanDefinitionRegistry。在用用同样的方法处理rdered类型的Bean。在用同样的方法处理剩余的bean直到所有的bean都处理完。然后在执行BeanFactoryPostProcessor的生命周期方法postProcessBeanFactory处理 registryProcessors 和 regularPostProcessors两个集合。
-
beanFactory不是BeanDefinitionRegistry我们则直接BeanFactoryPostProcessor的生命周期方法postProcessBeanFactory将BeanFactoryPostProcessor处理掉。
- BeanFactoryPostProcessor
其处理的方式与BeanDefinitionRegistryPostProcessor相同,即先处理完PriorityOrdered,再处理Ordered,最后统一处理普通的,这里要注意的是,因为BeanDefinitionRegistryPostProcessor也是BeanFactoryPostProcessor,所以在这里需要过滤掉不处理了。
下面我们来分析一些其核心方法invokeBeanDefinitionRegistryPostProcessors看看他是如何实现的
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
在PostProcessorRegistrationDelegate类的invokeBeanDefinitionRegistryPostProcessors方法进入ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
do {
parser.parse(candidates);
parser.validate();
...
}
...
}
在processConfigBeanDefinitions中进入ConfigurationClassParser类的parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 在SpringBoot项目中,bd其实就是前面主类封装成的 AnnotatedGenericBeanDefinition(AnnotatedBeanDefinition接口的实现类)
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 加载默认的配置(这里就是自动装配的入口了)
processDeferredImportSelectors();
}
我们已经在前面文章《springboot2.0.6启动解析(八)准备上下文》中详细介绍了SpringBoot项目的主类是如何一步步的封装成AnnotatedGenericBeanDefinition,并注册进IoC容器的beanDefinitionMap中的。也在前面文章《springboot2.0.6启动解析(一)自动配置》中对processDeferredImportSelectors进行了详细讲解。
跟进parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName())方法,进入ConfigurationClassParser类的processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
return;
} else {
// Explicit bean definition found, probably replacing an import.
// Let''s remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 递归处理配置类及其弗雷层次结构
SourceClass sourceClass = asSourceClass(configClass);
do {
// 递归处理Bean,直到顶层父类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
进入asSourceClass方法
private SourceClass asSourceClass(ConfigurationClass configurationClass) throws IOException {
AnnotationMetadata metadata = configurationClass.getMetadata();
if (metadata instanceof StandardAnnotationMetadata) {
return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
}
return asSourceClass(metadata.getClassName());
}
进入doProcessConfigurationClass方法
/* SpringBoot的包扫描的入口方法(重点)*/
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
//递归处理内部类,(主类一般没有内部类)
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// ① 处理 @PropertySource 注解的属性配置
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
} else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// ② 根据 @ComponentScan 注解,扫描项目中的Bean(SpringBoot 主类上可能有该注解)
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// ③ 执行扫描,(从此处可以了解到为什么从主类所在的包扫描开始扫描)
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 判断是否是ConfigurationClass(有configuration、component两个注解),是则递归查找该类相关联的配置类(@Configuration中的@Bean定义的bean。或者在有@Component注解的类上继续存在@Import注解等都是相关的配置类)。
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
//眼熟把,眼熟就对了这进行了对贵调用(因为当Spring扫描到需要加载的类会进一步判断每一个类是否满足是@Component、@Configuration注解的类,满足则会递归调用parse()方法,查找其相关的类即其外层的if判断语句)
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// ④ 递归处理@Import注解(SpringBoot中的各种@Enable***注解底层基本都是封装的@Import)
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
① 获取主类上的@PropertySource注解进行解析并将该注解指定的properties配置文件中的值存储到Spring的 Environment 中,Environment 接口提供方法去读取配置文件中的值,参数是 properties 文件中定义的key值。
② 解析主类上的@ComponentScan注解,获取注解值,扫描解析详情请看③中分析。
③ 遍历处理componentScans,解析注解并进行包扫描
④ 解析主类上的@Import注解,并加载该注解指定的配置类。
1. 定位扫描路径进行 IoC 初始化
关键具体处理在ComponentScanAnnotationParser类的parse方法,我们进入该方法
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//设置BeanNameGenerator
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
//设置ScopedProxyMode
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
//设置ResourcePattern
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
//设置IncludeFilter
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
//设置ExcludeFilter
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
//设置LazyInit
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
Set<String> basePackages = new LinkedHashSet<>();
//获取@ComponentScan注解设置的扫描路径集合
String[] basePackagesArray = componentScan.getStringArray("basePackages");
//遍历处理把扫描路径添加到basePackages集合中
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
// 根据 clazz 获取扫描路径(SpringBoot项目则参数为主类的全路径名)
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 当basePackages为控值时,根据 declaringClass 获取扫描路径(SpringBoot项目则参数为主类的全路径名)添加到basePackages集合中
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//设置AbstractTypeHierarchyTraversingFilter类型的ExcludeFilter
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 根据basePackages扫描类
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
到这里我们就找到了装配的扫描路径,即完成了Resource定位。经过分析我们得到在主类上有@ComponentScan注解的话扫描路径为其指定的路径。没有的化话就是主类所在的包。
完成了扫描路径的定位,下面让进入ClassPathBeanDefinitionScanner类的都Scan方法看看其是如何进行扫描的
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// 从指定的包中扫描需要装载的Bean
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将该 Bean 注册进 IoC容器(beanDefinitionMap)
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
分析该类得知findCandidateComponents方法从basePackage中扫描类并解析成BeanDefinition,拿到所有符合条件的类后registerBeanDefinition方法将该类注册进IoC容器。也就是说在方法doScan中完成了IoC容器初始化过程的第载入和注册,即BeanDefinition的载入,和BeanDefinition的注册
进入ClassPathScanningCandidateComponentProvider类的findCandidateComponents方法
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//拼接扫描路径,比如:classpath*:com/xds/test/demo/controller/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + ''/'' + this.resourcePattern;
//从 packageSearchPath 路径中扫描所有的类
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断metadataReader类是不是 被@Component 注解标注且不是需要排除掉的类
if (isCandidateComponent(metadataReader)) {
//将该类封装成 ScannedGenericBeanDefinition(BeanDefinition接口的实现类)
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
在scanCandidateComponents方法中先将basePackage拼接成classpath类型的扫描路径packageSearchPath。再调用getResources(packageSearchPath)方法,扫描到了该路径下的所有的类。然后遍历扫描获取到的类的集合,判断其是不是 被@Component 注解标注且不是需要排除掉的类。满足添加则将扫描到的类,解析成ScannedGenericBeanDefinition,该类是BeanDefinition接口的实现类。到这里IoC容器的BeanDefinition载入就结束了。换句话scanCandidateComponents方法完成了BeanDefinition载入。
下面我们进入registerBeanDefinition方法看看其实如何完成BeanDefinition注册的
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
查看registerBeanDefinition方法。BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry)这段代码很眼熟,我们在在前面介绍prepareContext()方法时,已经详细介绍了主类的BeanDefinition是怎么一步一步的注册进DefaultListableBeanFactory的beanDefinitionMap中的。而完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableFactory中已经建立了整个Bean的配置信息,而这些BeanDefinition已经可以被容器使用了。因为它们都在BeanbefinitionMap这个集合里被检索和使用。容器的作用就是对这些信息进行处理和维护。当然这些信息是容器建立依赖反转的基础。
2. @Import注解解析过程
前面说到过SpringBoot中的各种@EnableXXX注解,很大一部分都是对@Import的二次封装(目的其实就是为了解耦)。
首先我们看看getImports方法,看看如何递归遍历解析@Import,获取其引用的类
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
由于是@EnableAutoConfiguration注解中引用了@AutoConfigurationPackage注解而@AutoConfigurationPackage注解有使用@Import注解指定了“AutoConfigurationPackages.Registrar.class”类,并且@EnableAutoConfiguration本身使用@Import注解指定了“AutoConfigurationImportSelector.class”类。所以递归处理获取@import引用获取到的imports集合有两个值。
接下来让我们来看看ConfigurationClassParser类的doProcessConfigurationClass方法的中的processImports(configClass, sourceClass, getImports(sourceClass), true)方法,注意中configClass和sourceClass参数都是主类相对应的值
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 进行循环依赖的检查
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}else {
this.importStack.push(configClass);
try {
//@Import的value可以指定三种类型:带有@Configuration的类、ImportSelector实现、ImportBeanDefinitionRegistrar实现
for (SourceClass candidate : importCandidates) {
// 判断candidate是不是ImportSelector.class类型
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 获取ImportSelector对象
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 当前类是DeferredImportSelector 的实现,则加入到deferredImportSelectors,到所有配置类都解析后处理
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}else {
// 不是则调用processImports 进行处理
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}else {// 加入到importStack后调用processConfigurationClass 进行处理
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}catch (BeanDefinitionStoreException ex) {
throw ex;
}catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}finally {
this.importStack.pop();
}
}
}
该方法解析@Import注解,并加载该注解指定的配置类,有好多注解都是一层一层封装的,比如@EnableXXX就是对@Import注解的二次封装。
自动配置AutoConfigurationImportSelector类的selectImports方法在处启用。
关于Spring Boot测试类可以重用应用程序上下文以更快地运行测试吗?和springboot test怎么用的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于java – 使用Web应用程序上下文的Spring上下文层次结构、java-Spring Boot:在类路径更改时刷新应用程序上下文、pringboot2.0.6启动解析(九)刷新应用程序上下文、pringboot2.0.6启动解析(十)刷新应用程序上下文-IoC容器的初始化等相关知识的信息别忘了在本站进行查找喔。
本文标签: