這是我參與8月更文挑戰的第7天,活動詳情查看:8月更文挑戰java
上一篇講了spring容器初始化流程中的beanFactory預處理。那這一篇將對beanFactory的後置處理進行整理。web
其實spring的啓動過程是有不少流程的,也是很複雜的,我只是根據本身的思路和理解來進行整理的,確定就會忽略一些自認爲不重要的或者是本身沒有發現的地方。也但願xdjmm可以多多指正。spring
依賴的pom:springboot
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
複製代碼
本篇文章將要說的一個流程就是:markdown
//所在類及方法: AbstractApplicationContext#refresh
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
複製代碼
進入postProcessBeanFactory方法,發現該方法是一個空方法,什麼也沒作,不過根據該方法的註釋翻譯:session
/** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for registering special * BeanPostProcessors etc in certain ApplicationContext implementations. * @param beanFactory the bean factory used by the application context */
// 在beanFactory完成標準的初始化後修改應用上下文的內部bean factory,全部的bean定義都會被加載,可是並不會實例化bean。這裏容許註冊特殊的
// BeanPostProcessors 等在某些應用上下文中(ApplicationContext的實現)
複製代碼
從註釋能夠知道,postProcessBeanFactory就是對BeanFactory進行後置處理,也就是在BeanFactory完成初始化後能夠對其進行一些修改,具體要作什麼,就看子類怎麼去實現的了。 因爲我只導入了spring-context包,裏面對該方法的實現是沒有的,不過能夠在springboot項目中去查看該方法在子類中是如何去實現的。 這裏須要注意的是,我使用的springboot的版本是2.5.2,不一樣版本的代碼多是不同的。 下面對其中的一個子類代碼進行講解:app
// 所在類及方法:AnnotationConfigServletWebServerApplicationContext#postProcessBeanFactory
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 調用父類的postProcessBeanFactory方法
super.postProcessBeanFactory(beanFactory);
// 執行scanner和reader 在此地方打斷點,發現第二步並無執行。
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
複製代碼
AnnotationConfigServletWebServerApplicationContext的父類是ServletWebServerApplicationContext, 下面看看父類中的postProcessBeanFactory方法是怎麼樣的。ide
// 所在類及方法:ServletWebServerApplicationContext#postProcessBeanFactory
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 向BeanFactory中註冊一個PostProcessor
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
// 調用registerWebApplicationScopes
registerWebApplicationScopes();
}
複製代碼
在父類的方法中主要就是作了兩件事:源碼分析
類的註釋:post
/**
* Variant of {@link ServletContextAwareProcessor} for use with a
* {@link ConfigurableWebApplicationContext}. Can be used when registering the processor
* can occur before the {@link ServletContext} or {@link ServletConfig} have been
* initialized.
*/
// 用於ConfigurableWebApplicationContext的一個ServletContextAwareProcessor擴展,
// 能夠在初始化ServletContext或ServletConfig以前進行處理器的註冊。
複製代碼
註解中說明了是ServletContextAwareProcessor,那麼看看ServletContextAwareProcessor類的註釋:
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor}
* implementation that passes the ServletContext to beans that implement
* the {@link ServletContextAware} interface.
*
* <p>Web application contexts will automatically register this with their
* underlying bean factory. Applications do not use this directly.
*/
// 將ServletContext傳遞給實現ServletContextAware接口的bean
//Web應用程序上下文將自動將其註冊到底層bean工廠。應用程序不會直接使用它
複製代碼
ServletContextAwareProcessor實現了BeanPostProcessor接口, 那麼這裏就定位至他的postProcessBeforeInitialization方法和postProcessAfterInitialization方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (getServletContext() != null && bean instanceof ServletContextAware) {
((ServletContextAware) bean).setServletContext(getServletContext());
}
if (getServletConfig() != null && bean instanceof ServletConfigAware) {
((ServletConfigAware) bean).setServletConfig(getServletConfig());
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
複製代碼
代碼也比較簡單,就是判斷當前bean是ServletContextAware的實現仍是ServletConfigAware的實現,而後分別轉換後再調用各自接口的實現方法。 ServletContextAware的實現類就是調用setServletContext方法。 ServletConfigAware的實現類就是調用setServletConfig方法。
擴展: 這裏涉及到了BeanPostProcessor和XXXAware接口,這兩種接口的基本使用這裏就不細說了; 這裏的大概流程就是:向容器註冊BeanPostProcessor的一個實現,而後該實現類是處理XXXAware接口的實現類實現的方法。 其實本身也能夠根據這個模板來進行自定義XXXAware接口的擴展,固然也能夠進行其餘的一些處理,總之就是BeanPostProcessor提供了很好的擴展性,這裏只是其中的一種方式。
這個方法主要就是向beanFactory中註冊做用域:("request", "session", "globalSession", "application"),在源碼分析:
該方法的源碼以下:
// 所在類及方法:ServletWebServerApplicationContext#registerWebApplicationScopes
private void registerWebApplicationScopes() {
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
existingScopes.restore();
}
複製代碼
ExistingWebApplicationScopes是ServletWebServerApplicationContext類中的一個靜態類。源碼以下:
public static class ExistingWebApplicationScopes {
static {
Set<String> scopes = new LinkedHashSet<>();
scopes.add(WebApplicationContext.SCOPE_REQUEST);
scopes.add(WebApplicationContext.SCOPE_SESSION);
SCOPES = Collections.unmodifiableSet(scopes);
}
// 這是構造方法,大概就是根據SCOPES獲取beanFactory中已經註冊的scope,而後放入scopes
// 須要注意的是,在上面的方法中,第二行纔在向beanFactory中註冊,也就是這時的beanFactory裏面沒有request和session這兩個scop
// 因此這裏就完成了beanFactory的賦值。建議打斷點進去看看
public ExistingWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
for (String scopeName : SCOPES) {
Scope scope = beanFactory.getRegisteredScope(scopeName);
if (scope != null) {
this.scopes.put(scopeName, scope);
}
}
}
// 因爲上面的方法並無值存入scopes,因此這裏也就沒執行裏面的內容
public void restore() {
this.scopes.forEach((key, value) -> {
if (logger.isInfoEnabled()) {
logger.info("Restoring user defined scope " + key);
}
this.beanFactory.registerScope(key, value);
});
}
}
複製代碼
WebApplicationContextUtils.registerWebApplicationScopes(),這個方法就是向beanFactory註冊web的scope了,源碼以下:
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
registerWebApplicationScopes(beanFactory, null);
}
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) {
// 註冊做用域
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());// 註冊request SCOP
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());// 註冊session SCOP
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // 註冊application SCOP
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
// 添加依賴項
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
複製代碼
這個方法也比較簡單,主要註冊了幾種註冊域request ,session ,application。
至此,org.springframework.context.support.AbstractApplicationContext.postProcessBeanFactory 這個方法的大概流程就講完了,這個方法主要就是作了兩點: