二、启动你的springboot
2.1 启动springboot有两种方式:
(1)SpringApplication.run(springboot配置注解类.class,args)
@SpringBootApplication
public class Ep1Application {
public static void main(String[] args) {
SpringApplication.run(Ep1Application.class, args);
}
}
如果你希望添加一些自己的配置,那么也可以用引用.set的方式构建启动类,但是这会使代码不简洁
@SpringBootApplication
public class Ep1Application {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication();
springApplication.setSources(new HashSet<String>(Collections.singleton(Ep1Application.class.getName())));
springApplication.setBannerMode(Banner.Mode.CONSOLE);
springApplication.setWebApplicationType(WebApplicationType.NONE);
springApplication.setAdditionalProfiles("prod");
springApplication.setHeadless(true);
springApplication.run();
}
}
(2)new SpringApplicationBuilder --使用builder设计模式,使代码看起来更简洁
@SpringBootApplication
public class Ep1Application {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(Ep1Application.class)//SpringBootApplication配置类
.bannerMode(Banner.Mode.CONSOLE)//banner 也就是启动logo
.web(WebApplicationType.NONE)//web类型,【SERVLET、REACTIVE、NONE】
.profiles("prod") //配置类型【开发为dev,测试为test,生产为prod】
.headless(true) //运行 headless 服务器,来进行简单的图像处理
.run(args);
}
}
.sources(Ep1Application.class) // SpringBootApplication配置类
.bannerMode(Banner.Mode.CONSOLE) // banner 也就是启动logo
.web(WebApplicationType.NONE) // web类型,【SERVLET、REACTIVE、NONE】
.profiles("prod") // 配置类型【开发为dev,测试为test,生产为prod】
.headless(true) // 运行 headless 服务器,来进行简单的图像处理
重点是:启动springboot直接运行run方法即可,因为spring约定大于配置,使用默认的即可快速启动一个springboot工程。
2.2 为什么run方法要获取当前注解@SpringBootApplication类的class
目的是以当前的class路径去扫描其下的各种组件;我们举一个例子来说明
说明:aaa包下有a组件类,重写了toString方法,bbb包下有b组件类,重写了toString方法。两类都有@Component注解
aaa包下建立包含@SpringBootApplication注解的TestApplication类作为Springboot source的参数。
当我在启动的时候同时a类可以正常加载 b类没有被加载。由此也说明source中的配置类会作为一个根路径由上到下去扫描组件
SpringApplication可以从不同的来源读取bean,一般是使用带有@Configuration注解的类来引导,同时也可以使用
- 由AnnotatedBeanDefinitionReader加载的完全限定类名
- 由XmlBeanDefinitionReader加载的XML资源或由GroovyBeanDefinitionReader加载的groovy脚本的位置
- 由ClassPathBeanDefinitionScanner扫描的包的名称
因为我们的Ep1Application带有@SpringBootApplication注解,而这个注解又继承自@Configuration注解,所以应用当前类来加载是没问题的,你也可以自己定义一个带有@SpringBootApplication的类去加载。
2.3 run方法执行了哪些功能
下面直接献上run方法的具体代码
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
接下来我们逐行分析一下:
1 | StopWatch stopWatch = new StopWatch(); stopWatch.start(); |
一个执行状态记录器,主要记录了一些启动时监控参数 执行start方法设置开始时间 |
2 | ConfigurableApplicationContext context = null; | 创建应用环境变量的引用 |
3 | Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); | 初始化异常类空集合 |
4 | configureHeadlessProperty(); | 设置Headless模式【true/false】 Headless模式是在缺少显示屏、键盘或者鼠标是的系统配置。在java.awt.toolkit和java.awt.graphicsenvironment类中有许多方法,除了对字体、图形和打印的操作外还可以调用显示器、键盘和鼠标的方法。但是有一些类中,比如Canvas和Panel,可以在headless模式下执行。 |
5 | SpringApplicationRunListeners listeners = getRunListeners(args); | 使用工厂模式创建springboot运行时监听器。 工厂就是指从META-INF/spring.factorie读取需要加载的监听器。 然后使用组合模式,迭代读取到的监听器。 |
6 | listeners.starting(); | starting()方法开启监听器。 |
7 | ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); | 创建应用参数实体 初始化了CommandLineArgs 然后调用parser方法解析设置的命令参数 如果有设置命令,执行下述方法,将参数放入map 集合 commandLineArgs.addOptionArg(optionName, optionValue); 否则执行下述方法将命令放入下述list集合 commandLineArgs.addNonOptionArg(arg); |
8 | ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); |
准备环境参数配置 一、获取web类型,生成对应的环境实体 SERVLET: new StandardServletEnvironment(); REACTIVE: new StandardReactiveWebEnvironment(); default: new StandardEnvironment(); 二、set环境参数到map集合中 三、发出通知,环境已经准备好了 |
9 | configureIgnoreBeanInfo(environment); | 设置环境变量 spring.beaninfo.ignore = true |
10 | Banner printedBanner = printBanner(environment); | 打印banner |
11 | context = createApplicationContext(); | 根据web推断类型创建应用上下文 SERVLET: AnnotationConfigServletWebServerApplicationContext REACTIVE: AnnotationConfigReactiveWebServerApplicationContext default: AnnotationConfigApplicationContext |
12 | exceptionReporters = getSpringFactoriesInstances( context); |
加载异常分析器到上下文中 |
13 | prepareContext(context, environment, listeners, applicationArguments,printedBanner); | 准备应用上下文 为context设置环境参数 为context设置beanNameGenerator,resourceLoader,classloader 递归所有待初始化的初始化器 调用他们的初始化方法,注册单例对象 获取启动类sources 调用BeanDefinitionLoader的load方法注册bean 调用listeners的contextLoaded方法 |
14 | refreshContext(context); | 主要执行下列步骤 准备刷新 prepareRefresh(); 初始化beanfactory prepareBeanFactory(beanFactory); 丰富类注解 postProcessBeanFactory(beanFactory); 激活beanfactory处理器 invokeBeanFactoryPostProcessors(beanFactory); 注册bean的初始化逻辑处理器 registerBeanPostProcessors(beanFactory); 国际化 initMessageSource(); 初始化应用事件广播 initApplicationEventMulticaster(); 留给子类继承扩展使用 onRefresh(); 注册到消息广播器中 registerListeners(); 完成刷新过程 finishBeanFactoryInitialization(beanFactory); 清除缓存 finishRefresh(); |
15 | afterRefresh(context, applicationArguments); | 刷新的后续处理 执行finish方法,获取最终的ApplicationReadyEvent事件。 基本spring程序已经正常工作 |
16 | stopWatch.stop(); | 结束执行状态记录器 设置结束时间、总共执行时间、最后一个执行任务等信息 |
17 | new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); |
打印服务启动日志 |
18 | listeners.started(context); | EventPublishRunningListener 启动 |
19 | callRunners(context, applicationArguments); | 从context中获取所有的runner,根据runner的 类型
执行相应的run方法 |
20 | listeners.running(context); | context发布事件:ApplicationReadyEvent |
21 | return context; | 返回上下文 |