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的时候去做一些事情,例如以前大家一定使用过
在具体的 业务域去初始化一些全局的东西,这样不是很好