看這篇文章之前可以先了解之前的跟蹤流程,https://www.jianshu.com/p/4934233f0ead
代碼過寬,可以shift + 鼠標滾輪 左右滑動查看
AbstractApplicationContext類refresh()方法中的第四個調用方法postProcessBeanFactory()的跟蹤。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
try {
// Allows post-processing of the bean factory in context subclasses.
// 允許在上下文的子類中對bean factory進行后處理
postProcessBeanFactory(beanFactory);
···
}
斷點進入跟蹤。
postProcessBeanFactory(零)
此方法的實現在AbstractRefreshableWebApplicationContext類中,它是XmlWebApplicationContext的父類。進入方法查看:
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*
* 注冊request/session scopes,一個ServletContextAwareProcessor處理器等。
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//ServletContextAwareProcessor中拿到應用上下文持有的servletContext引用和servletConfig引用
//1.添加ServletContextAwareProcessor處理器
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
//在自動注入時忽略指定的依賴接口
//通常被應用上下文用來注冊以其他方式解析的依賴項
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//2.注冊web應用的scopes
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
//3.注冊和環境有關的beans
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
postProcessBeanFactory方法的接口聲明在AbstractApplicationContext類中:
/**
* 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.
*
* 在應用上下文的內部bean factory初始化之后修改bean factory。
* 所有的bean definitions已經被加載,但是還沒有bean被實例化。
* 在明確的ApplicationContext實現中允許指定BeanPostProcessors等的注冊
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
1.addBeanPostProcessor
跟蹤標記為1的方法
此方法的實現在AbstractBeanFactory類中
這里要注冊的處理器ServletContextAwareProcessor帶有Aware單詞,這個單詞是“有意識、能意識到”的意思,個人理解就是能意識到ServletContext的存在,也就是能拿到ServletContext的引用,或者能對其進行設置。
//1.添加ServletContextAwareProcessor處理器
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
/**
* Add a new BeanPostProcessor that will get applied to beans created
* by this factory. To be invoked during factory configuration.
* <p>Note: Post-processors submitted here will be applied in the order of
* registration; any ordering semantics expressed through implementing the
* {@link org.springframework.core.Ordered} interface will be ignored. Note
* that autodetected post-processors(e.g. as beans in an ApplicationContext)
* will always be applied after programmatically registered ones.
*
* 添加一個新的BeanPostProcessor,在工廠創建bean的時候會應用得到。
* 在工廠配置時被調用。
* 注意:Post-processors是按照注冊的順序被提交的
* 任何通過實現Ordered接口的排序表達式都將被忽略。
* 注意,自動檢測的post-processors(作為一個在ApplicationContext的bean)總是在編程方式注冊后才會被使用。
*/
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
//beanPostProcessors是一個ArrayList,持有在創建bean時被應用的BeanPostProcessors
this.beanPostProcessors.remove(beanPostProcessor);
this.beanPostProcessors.add(beanPostProcessor);
//InstantiationAwareBeanPostProcessor這個接口有兩個方法
//一個在實例化之前被調用
//一個在實例化之后,初始化之前被調用,可以用來做一些特殊用途,比如代理
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
//DestructionAwareBeanPostProcessor這個接口只有一個方法,在被銷毀前調用
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true;
}
}
2.registerWebApplicationScopes
跟蹤標記為2的方法
此方法的實現在WebApplicationContextUtils類中
//2.注冊web應用的scopes
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
/**
* Register web-specific scopes ("request", "session", "globalSession", "application")
* with the given BeanFactory, as used by the WebApplicationContext.
*
* 注冊web特有的scopes("request", "session", "globalSession", "application")到指定的bean工廠
* 被WebApplicationContext使用
*/
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
//2.1注冊request Scope
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
//注冊session Scope
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
//注冊global session Scope
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
//注冊application Scope
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
// 為了能讓ContextCleanupListener監聽器檢測到,
// 將application Scope作為ServletContext的屬性進行注冊
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
//ServletRequest.class為key,對象為value放入到了beanFactory的resolvableDependencies屬性中
//resolvableDependencies是一個ConcurrentHashMap,映射依賴類型和對應的被注入的value
//value要是依賴類型的實例,要不value就應該是個ObjectFactory
//ObjectFactory和FactoryBean的區別可以看下文參考
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
//是否存在jsf
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
2.1 registerScope
跟蹤標記為2.1的方法
此方法的實現在AbstractBeanFactory類中
//2.1注冊request Scope
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
@Override
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
//singleton和prototype在這個方法中不進行注冊
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
//不能替換已存在的 singleton scope和 prototype scope
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
//scopes是AbstractBeanFactory的LinkedHashMap屬性
Scope previous = this.scopes.put(scopeName, scope);
//打印日志
if (previous != null && previous != scope) {
//對已經注冊過的scope進行替換
if (logger.isInfoEnabled()) {
logger.info("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
//沒注冊過的和同一個實例注冊兩次的scope都打印日志記錄下
if (logger.isDebugEnabled()) {
logger.debug("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
3.registerEnvironmentBeans
跟蹤標記為3的方法
此方法的實現在WebApplicationContextUtils類中
//3.注冊和環境有關的beans
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
/**
* Register web-specific environment beans ("contextParameters", "contextAttributes")
* with the given BeanFactory, as used by the WebApplicationContext.
*
* 注冊web特有的environment beans ("contextParameters", "contextAttributes")到指定工廠中
* 被WebApplicationContext所使用
*/
public static void registerEnvironmentBeans(
ConfigurableListableBeanFactory bf, ServletContext servletContext, ServletConfig servletConfig) {
//單例或者beanDefinition中不包含servletContext進入條件
if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
//3.1注冊servletContext單例,注冊方法跟蹤過一次,這里再跟蹤一次加深印象
bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
}
//單例或者beanDefinition中不包含servletConfig進入條件
if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
//注冊servletConfig單例
bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig);
}
//單例或者beanDefinition中不包含contextParameters進入條件
if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
Map<String, String> parameterMap = new HashMap<String, String>();
if (servletContext != null) {
Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
//將servletContext參數配置放入集合中
//也就是web.xml中context-param標簽里的param-name和param-value
parameterMap.put(paramName, servletContext.getInitParameter(paramName));
}
}
if (servletConfig != null) {
Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement();
//將servletConfig中的參數配置放入集合
parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
}
}
//以contextParameters作為name,集合轉換成不可修改狀態,作為value,進行注冊
bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
Collections.unmodifiableMap(parameterMap));
}
//單例或者beanDefinition中不包含contextAttributes進入條件
if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
Map<String, Object> attributeMap = new HashMap<String, Object>();
if (servletContext != null) {
Enumeration<?> attrNameEnum = servletContext.getAttributeNames();
while (attrNameEnum.hasMoreElements()) {
String attrName = (String) attrNameEnum.nextElement();
//將servletContext中設置的Attribute放入集合
attributeMap.put(attrName, servletContext.getAttribute(attrName));
}
}
//以contextAttributes作為name,集合轉換成不可修改狀態,作為value,進行注冊
bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
Collections.unmodifiableMap(attributeMap));
}
}
3.1 registerSingleton
跟蹤標記為3.1的方法
此方法的實現在DefaultListableBeanFactory類中
//3.1注冊servletContext單例
bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
//3.1.1調用父類方法,注冊單例
super.registerSingleton(beanName, singletonObject);
//AbstractBeanFactory類中有個集合屬性alreadyCreated
//里面保存在至少被創建過一次的beanName
//如果這個集合中存在beanName,那么說明已經進入了bean創建階段
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 無法再修改啟動時集合元素(為了穩定迭代)
synchronized (this.beanDefinitionMap) {
//beanName不在beanDefinitionMap中,說明是手動注冊
if (!this.beanDefinitionMap.containsKey(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames.size() + 1);
updatedSingletons.addAll(this.manualSingletonNames);
updatedSingletons.add(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
// 仍然處於啟動注冊階段
if (!this.beanDefinitionMap.containsKey(beanName)) {
//屬於手動注冊情況
this.manualSingletonNames.add(beanName);
}
}
//進入這個方法查看
clearByTypeCache();
}
/**
* Remove any assumptions about by-type mappings.
*
* 刪除按照類型映射有關的任何假設
*/
private void clearByTypeCache() {
//allBeanNamesByType是單例和非單例beanName的映射,key是依賴類型
this.allBeanNamesByType.clear();
//僅單例beanName的映射,key是依賴類型
this.singletonBeanNamesByType.clear();
}
3.1.1 registerSingleton
跟蹤標記為3.1.1的方法
此方法的實現在DefaultSingletonBeanRegistry類中
//2.1調用父類方法,注冊單例
super.registerSingleton(beanName, singletonObject);
/**
* Register the given existing object as singleton in the bean registry,
* under the given bean name.
* <p>The given instance is supposed to be fully initialized; the registry
* will not perform any initialization callbacks (in particular, it won't
* call InitializingBean's {@code afterPropertiesSet} method).
* The given instance will not receive any destruction callbacks
* (like DisposableBean's {@code destroy} method) either.
* <p>When running within a full BeanFactory: <b>Register a bean definition
* instead of an existing instance if your bean is supposed to receive
* initialization and/or destruction callbacks.</b>
* <p>Typically invoked during registry configuration, but can also be used
* for runtime registration of singletons. As a consequence, a registry
* implementation should synchronize singleton access; it will have to do
* this anyway if it supports a BeanFactory's lazy initialization of singletons.
*
* 在給定的bean name下,將存在的對象作為單例注冊在工廠中
* 給定的實例應該是完全初始化;工廠不執行任何初始化回調(特別是,他不會調用InitializingBean的
* afterPropertiesSet方法)
* 給定的實例也不接收任何銷毀回調(像DisposableBean的destroy方法)
* 當在完整的BeanFactory運行時:
* 如果你的bean需要接收初始化或者銷毀的回調,注冊一個bean definition替代一個存在的實例
* 通常此方法在工廠配置時被調用,也能在運行時單例注冊時被調用。
* 作為結果,工廠的實現應該同步單例的訪問;如果支持BeanFactory的單例的延遲初始化就不得不這樣做
*/
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
//不能注冊兩次
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
//進入這個方法
addSingleton(beanName, singletonObject);
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
*
* 添加給定單例對象到工廠的單例緩存中
* 用來被提早注冊的單例調用
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//singletonObjects是一個ConcurrentHashMap
//用來緩存單例對象
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
//singletonFactories是一個HashMap
//里面緩存着單例工廠
this.singletonFactories.remove(beanName);
//早期單例對象
//earlySingletonObjects是一個HashMap
this.earlySingletonObjects.remove(beanName);
//registeredSingletons是一個LinkedHashSet
//被注冊單例的集合,以注冊的順序包含着bean name
this.registeredSingletons.add(beanName);
}
}
postProcessBeanFactory方法也跟蹤完了。
未完···
參考
FactoryBean與ObjectFactory區別:
https://blog.csdn.net/m0_38043362/article/details/80284577
總結
- 添加后處理器、忽略依賴接口
- 2.注冊web應用的scopes
- 3.注冊和環境有關的beans
——————————————————————————————————
- 2
- 在工廠中注冊
requestsessionglobalSessionScope - 如果 ServletContext 不為null,用Scope實現類包裹后注冊到工廠,注冊為
application。同時在ServletContext 的屬性中保存一份。 - 在工廠中注冊各種可解析依賴
——————————————————————————————————
- 3
- 當 servletContext 不為null時,工廠以及其父工廠中沒有對應單例或者BeanDefinition,那么就在工廠中注冊單例,並標記為手動注冊的單例
- 同上注冊 servletConfig
- 拿到 servletContext 和 servletConfig 中的初始參數,都放入到map中,然后注冊到工廠,name為 contextParameters ,最后標記為手動注冊
- 拿到 servletContext 中的屬性,都放入到map中,然后注冊到工廠,name為 contextAttributes,最后標記為手動注冊
