Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
目录:
1 学习SpirngBoot的准备知识点
1.1 关于AbstractApplicationContext类以及其子类的一些解读。
1.1.1 AbstractApplicationContext类信息注释的解读,从类注释开始学习spring。
AbstractApplicationContext类注释中,@see的关键类/接口BeanFactoryPostProcessor ,BeanPostProcessor ,ApplicationListener ,ApplicationEventMulticaster ,SimpleApplicationEventMulticaster ,DefaultResourceLoader 的解读。1.2 AbstractApplicationContext类的子类,常用的spring-xxx.xml方式启动和springBoot无xml方式选用的ApplicationContext子类,以及这两个子类的差异。
1.3 spring注解,@Bean @ComponentScan @Configuration @ConditionalOnXXXX @Import @EnableXXX @PropertySource 的学习
2 SpringBoot的启动
2.1 通过war包启动和通过jar包启动的共通点和差异点。
2.2 SpringBoot中设计的SpringApplication类的作用,SpringApplication源码解读,以及在AbstractApplicationContext.refresh()前后做了一些什么事情
2.3 AbstractApplicationContext.refresh() 实现IOC容器源码解读,YY一下spring设计的一些精髓。
- 1.1 从类AbstractApplicationContext注释开始学习spring。看AbstractApplicationContext的源码的注释上,有几个@see 的类,这个类都是在applicitonContext IOC容器启动中几个比较重要的类,首先查看下这几个类接口的定义,后面在解读源码时讲spring是怎么去实现这些接口的。关于spring钩子的详细解读参考链接
- 1.1.1 Aware接口族
- 1.1.2 InitializingBean接口和DisposableBean接口
- 1.1.3 ImportBeanDefinitionRegistrar接口
- 1.1.4 BeanPostProcessor接口和BeanFactoryPostProcessor接口
- 1.1.5 BeanDefinitionRegistryPostProcessor 接口
- 1.1.6 FactoryBean接口
- 1.1.7 ApplicationListener
以下为AbstractApplicationContext,BeanPostProcessor,BeanFactoryPostProcessor源码,以及官方注释:
/**
* Abstract implementation of the {@link org.springframework.context.ApplicationContext}
* interface. Doesn't mandate the type of storage used for configuration; simply
* implements common context functionality. Uses the Template Method design pattern,
* requiring concrete subclasses to implement abstract methods.
*
* <p>In contrast to a plain BeanFactory, an ApplicationContext is supposed
* to detect special beans defined in its internal bean factory:
* Therefore, this class automatically registers
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors},
* {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessors}
* and {@link org.springframework.context.ApplicationListener ApplicationListeners}
* which are defined as beans in the context.
*
* <p>A {@link org.springframework.context.MessageSource} may also be supplied
* as a bean in the context, with the name "messageSource"; otherwise, message
* resolution is delegated to the parent context. Furthermore, a multicaster
* for application events can be supplied as "applicationEventMulticaster" bean
* of type {@link org.springframework.context.event.ApplicationEventMulticaster}
* in the context; otherwise, a default multicaster of type
* {@link org.springframework.context.event.SimpleApplicationEventMulticaster} will be used.
*
* <p>Implements resource loading through extending
* {@link org.springframework.core.io.DefaultResourceLoader}.
* Consequently treats non-URL resource paths as class path resources
* (supporting full class path resource names that include the package path,
* e.g. "mypackage/myresource.dat"), unless the {@link #getResourceByPath}
* method is overwritten in a subclass.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Mark Fisher
* @author Stephane Nicoll
* @since January 21, 2001
* @see #refreshBeanFactory
* @see #getBeanFactory
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
* @see org.springframework.beans.factory.config.BeanPostProcessor
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.MessageSource
*/
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
}
Bean工厂的后置处理器接口,可以管理我们的bean工厂内所有的beandefinition(未实例化)数据
在AbstractApplictionContext.refresh()方法中,通过invokeBeanFactoryPostProcessors(beanFactory);调用。
public interface BeanFactoryPostProcessor {
/**
* 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 overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
/**
* Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
* the registration of further bean definitions <i>before</i> regular
* BeanFactoryPostProcessor detection kicks in. In particular,
* BeanDefinitionRegistryPostProcessor may register further bean definitions
* which in turn define BeanFactoryPostProcessor instances.
*
* @author Juergen Hoeller
* @since 3.0.1
* @see org.springframework.context.annotation.ConfigurationClassPostProcessor
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
在AbstractApplictionContext.refresh()方法中,通过registerBeanPostProcessors注册加载类信息,通过finishBeanFactoryInitialization(beanFactory)实例化执行BeanPostProcessor的相关操作
Bean的后置处理器接口
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
-
1.2 AbstractApplicationContext子类信息,我们SpringBoot的web环境用到的是AnnotationConfigEmbeddedWebApplicationContext子类,非web环境用到的是AnnotationConfigApplicationContext子类。而传统通过spring-xxx.xml方式启动的web环境,用到的是ClassPathXmlApplicationContext.有兴趣的可以自行点击AbstractApplicationContext.refresh()方法里面的各个方法,查询这两个子类在实现上的差异。
SpringBoot正式部分
- 2.0 war方式启动写法和jar方式启动写法
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
public class WarApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
Map<String, Object> properties = new HashMap<>();
return builder.properties(properties).sources(this.getClass());
}
}
@SpringBootApplication
@MapperScan(basePackages = "com.xxx.xxx.mapper")
@ServletComponentScan(basePackages = "com.xxx.xxxx")
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 2.1 war包方式启动,基于servlet3.0的扩展。通过实现ServletContainerInitializer的onStartup方法,SpringServletContainerInitializer调用springboot的SpringBootServletInitializer类的onStartup方法,==>调用createRootApplicationContext方法==》运行SpringAppliction的run方法,
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case a ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
/*
这儿通过此方法中的new SpringApplication(sources),调用initialize(),
初始实例化了spring自带的一些Initializers和Listeners保存在SpringApplication的list中
(具体实现原理通过:SpringFactoriesLoader.loadFactoryNames(type, classLoader)加载所有
jar包META-INF下的spring.factories里面指定type的是实现类),详细见下图A1 图A2:
*/
SpringApplicationBuilder builder = createSpringApplicationBuilder(){
};
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
/*
重点:这个地方设置了SpirngBoot的AbstrctApplictionContext的实例化子类类型,而
通过jar方式启动,这个为null,SpringApplication在new ApplicationContext的时候
,用的SpirngApplication.DEFAULT_WEB_CONTEXT_CLASS作为默认值。
*/
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
/*
重点:这个地方设置了一个source类信息,就是@SpringBootApplication标注的主类。
这个在后面refresh()方法中会讲到,工厂后置处理器中就是通过
这个SpringAppliction中的两个类为起始,扫描加载注册了整个工程中的类信息。
*/
builder = configure(builder);
SpringApplication application = builder.build();
if (application.getSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
Assert.state(!application.getSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
//重点:这个地方额外增加了一个source类型信息
application.getSources().add(ErrorPageFilterConfiguration.class);
}
return run(application);
}
图A1图A2
2.2 接下来看一下SpringApplication最重要的一个方法:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();构造一个任务执行观察器
stopWatch.start();开始执行,记录开始时间
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获得EventPublishingRunListener实例(图B0)
SpringApplicationRunListeners listeners = getRunListeners(args);
/*通过EventPublishingRunListener的 ApplicationStartedEvent
(this.application, this.args)springBoot启动事件,之前初始化加载的多个监听器都会监听此事件,
执行listener.onApplicationEvent(event)方法。
*/
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境,加载spring的properties或者yml配置文件key-value到environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
/*1 创建ApplicationContext,这里面的contextClass类型为之前SpringApplication设置的值,
2 AnnotationConfigEmbeddedWebApplicationContext,这个类的构建方法会调用
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)加载一部分
内置beanFactoryPostProcessor,其中包含refresh()方法中最开始第一个调用的beanFactoryPostProcessor,这个在后面invokeBeanFactoryPostProcessors中会讲到。
*/
context = createApplicationContext();
//这儿加载的是一些spring异常包装类,见下图B1
analyzers = new FailureAnalyzers(context);
/*
1 设置environment
2 调用之前SpringApplication初始化中加载的ApplicationContextInitializer.initialize()
包括加载ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
3注册之前SpringApplication前面设置source的类信息。
load(context, sources.toArray(new Object[sources.size()]));
4 发布ApplicationPreparedEvent(this.application, this.args, 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);
}
}
图B0图B1
AbstractApplicationContext最重要的一个方法
因这个方法相关篇幅较多,网上的资料也特别多,就不再这个地方重新写一遍。
参考:SpringBoot源码分析之Spring容器的refresh过程
Spring源码finishBeanFactoryInitialization和getBean
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();
}
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();
}
}
}
SpirngBoot Jar方式启动,内嵌jetty或者tomcat容器实现方法
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}