Bean 何時被創建
Spring 中的一個Bean 是何時被創建的呢?如何你對此疑問,可以通過以此篇文章的做法去了解一個Bean 在Spring中是如何被創建的。
環境準備
1.pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
</dependencies>
2.啓動類
@SpringBootApplication
@ComponentScan(value = "com.seven")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
3.一個Bean
@Controller
public class TestController {
public TestController(){
System.out.println("TestController 創建了");
}
}
調試
1.直接啓動,通過日誌可以看到TestController 被創建了
2018-07-02 17:38:58.083 INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-02 17:38:58.088 INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-02 17:38:58.088 INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-02 17:38:58.089 INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-02 17:38:58.089 INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
TestController 創建了
2018-07-02 17:38:58.483 INFO 7076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@78dd667e: startup date [Mon Jul 02 17:38:55 CST 2018]; root of context hierarchy
2018-07-02 17:38:58.558 INFO 7076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-07-02 17:38:58.559 INFO 7076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-07-02 17:38:58.587 INFO 7076 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-02 17:38:58.587 INFO 7076 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2.設置斷點進行調試
這一步是關鍵的,在進行操作的時候,可根據自己的情況進行斷點調試,這邊給出幾個推薦的斷點位置
斷點一:啓動位置
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
斷點二: run 方法內部
進入run 方法裏面找到這個方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
該方法就是Spring Boot項目啓動時 所要執行的代碼,根據閱讀可以分爲以下步驟:
1. 加載環境變量
2. 創建上下文 ConfigurableApplicationContext 對象
3. 準備上下文 prepareContext()
4. 刷新上下文 refreshContext()
5. 刷新之後的處理 afterRefresh()
可以在以上的五個地方進行斷點設置,然後開始調試,結合控制檯的日誌一步一步觀察。
通過斷點調試,發現執行完refreshContext()方法執行完畢之後,日誌打印了
TestController 創建了
1
初步判斷,Bean在執行refreshContext() 方法的時候創建的,那麼接下來我們看一下refreshContext這個方法幹了什麼事情?
斷點三:
進入refreshContext 方法,找到最終調用的方法 refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
以步驟二的斷點設置方式,我們在進行一步調試,發現執行finishBeanFactoryInitialization(beanFactory)方法之後,日誌打印了
TestController 創建了
1
改方法的具體實現如下
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
return getEnvironment().resolvePlaceholders(strVal);
}
});
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
通過調試我們可以發現,執行beanFactory.preInstantiateSingletons();這個方法之後,TestController 被創建了,那麼我接下來要做的就是看這個方法具體怎麼實現的,調試過程中,我們通過shift+alt+F7 強制進入代碼,可以看到其具體實現如下:
@Override
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
採用調試的方式,通過觀察我們可以看到,我們的TestController Bean 被加載到beanNames 這個List,通過循環語句最終調用了getBean(beanName);去創建了對象,當然getBean 內部會調用doGetBean() 方法進行對象的獲取或者創建。
總結
TestController 這個對象的創建,經過了如下方法調用: SpringApplication.run(App.class,args)
refreshContext(context)
finishBeanFactoryInitialization(beanFactory)
beanFactory.preInstantiateSingletons()
getBean(beanName)
doGetBean()
解決這樣一個問題,門檻並非很高,需要的只是一點方法,不過可以由這個問題,牽引出很多問題,比如說:refreshContext 裏面除了創建Bean還做了哪些事情?afterRefresh()又幹了什麼事情?引發一系列的疑問?我想通過這種擴散的方式,可以對Spring 有更加深入的認識,我會在接下來的博客裏面,一步一步通過一個個問題揭開框架的面紗。
原文:https://blog.csdn.net/qq_23980427/article/details/80888040