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 创建了
初步判断,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 创建了
改方法的具体实现如下
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 有更加深入的认识,我会在接下来的博客里面,一步一步通过一个个问题揭开框架的面纱。
问题
Bean 的创建时间能不能被修改?如果可以,改如何配置?
深入理解Spring —— 控制Bean 的创建时机