Spring Boot 入门
官方地址
你是否已经厌烦了创建一个web项目
1)配置web.xml,加载spring和spring mvc
2)配置数据库连接、配置spring事务
3)配置加载配置文件的读取,开启注解
4)配置日志文件
...
配置完成之后部署tomcat 调试,每次新建都要重复现有步骤
但是如果使用Spring Boot 你可以start
上面分别告诉你,启动文件使用哪一个,如果没有设置则使用默认的,也就是application.properties
下面是web容器的端口,默认使用的是tomcat,当然也可以方便的更换成其他容器undertow等等,而这些操作只需要简单的更换pom文件和更新properties文件
这样激活的就是application-local.properties文件
方便调试不同的环境
这样就更换的了web容器
参数配置
因为没有了配置文件,我们看看针对spring mvc的一些MethodResolver小技巧 如何配置
本身boot 为我们提供了生命周期的口子:WebMvcConfigurationSupport
这样同样就完成了我们的参数解析,常用的扩展在此声明周期函数中都可以找到
具体的使用大家可以参考开头官方文档,使用非常详细了,接下来我们看看加载流程:
从开始:
首先经过代理之后最终看到其目的是为了实例化SpringApplication这个类
首先是deduceWebApplicationType,它的目的是为了明确应用的类型是标准类型还是web类型环境,
通过对应环境的核心类进行判断:
接着是:setInitializers
它会去调用getSpringFactoriesInstances,getSpringFactoriesInstances委托给getSpringFactoriesInstances 去调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)
loadFactoryNames的作用是:读取META-INF/spring.factories里面的配置文件 明确要实例化的组件
读取完成之后,开始实例化
ApplicationContextInitializer根据类文档,这个接口的主要功能是:
在Spring上下文被刷新之前进行初始化的操作。典型地比如在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不同环境下(如DEV,TEST,PRODUCTION等),加载不同的配置项而抽象出来的一个实体。
ApplicationListener是spring的事件接口,事件一套属于spring的观察者模式,咱们写个小例子:
定义一个event
mock 当登录之后,我们发一个事件,这样我登录的验证逻辑和登录成功之后要做的事情是分开的
我们看下,找的方法,略屌:
运行时异常的栈桢中获取 吊炸天
这样SpringApplication的构造方法就执行完毕了
// 运行run方法
public ConfigurableApplicationContext run(String... args) {
// 计时工具
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置java.awt.headless系统属性为true - 没有图形化界面
configureHeadlessProperty();
// KEY 1 - 获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发出开始执行的事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// KEY 2 - 根据SpringApplicationRunListeners以及参数来准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// KEY 3 - 创建Spring上下文
context = createApplicationContext();
// 准备异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// KEY 4 - Spring上下文前置处理
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// KEY 5 - Spring上下文刷新
refreshContext(context);
// KEY 6 - Spring上下文后置处理
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, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
从getRunListeners来看也是执行上面的流程读取配置文件实例化:
SpringApplicationRunListeners它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。至于具体的应用场景,后面用到的时候再来分析;
接下来的步骤配置环境,这里都做了域隔离
记得最开始的spring.profiles.active么,就是在这决定如何操作的
打印控制台最开始的banner信息
createApplicationContext创建上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
// WEB应用的上下文类型
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
接下来做前置上文的操作:prepareContext
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将环境和上下文关联起来
context.setEnvironment(environment);
// 为上下文配置Bean生成器以及资源加载器(如果它们非空)
postProcessApplicationContext(context);
// 调用初始化器
applyInitializers(context);
// 触发Spring Boot启动过程的contextPrepared事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
load(context, sources.toArray(new Object[sources.size()]));
// 触发Spring Boot启动过程的contextLoaded事件
listeners.contextLoaded(context);
}
配置Bean生成器以及资源加载器
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
还有最后的callRunners,它的作用是整个容器ready的时候去做一些事情,例如以前大家一定使用过
在具体的 业务域去初始化一些全局的东西,这样不是很好