Hello,欢迎来到程序员社区。 今天聊一聊 详细SpringBoot教程之启动配置原理,希望对大家有所帮助。
Java面试手册PDF下载:点击下载最全Java面试手册
写在前面
鉴于有人留言说想要学习SpringBoot相关的知识,我这里打算写一个SpringBoot系列的相关博文,目标呢是想让看了这一系列博文的同学呢,能够对SpringBoot略窥门径,这一系列的博文初步都定下来包括SpringBoot介绍、入门、配置、日志相关、web开发、数据访问、结合docker、缓存、消息队列、检索、任务安全、分布式等等一系列的博文,工作量很大,是个漫长的过程,每一步我都尽量详细,配上截图说明,也希望对看的同学真的有用。
单纯就是想分享技术博文,还想说一句就是,如果觉得有用,请点个关注、给个赞吧,也算对我来说是个宽慰,毕竟也得掉不少头发,嘿嘿嘿
系列文章传送条
详细SpringBoot教程之入门(一)
详细SpringBoot教程之入门(二)
详细SpringBoot教程之配置文件(一)
详细SpringBoot教程之配置文件(二)
详细SpringBoot教程之日志框架
详细SpringBoot教程之Web开发(一)
详细SpringBoot教程之Web开发(二)
详细SpringBoot教程之Web开发(三)
详细SpringBoot教程之数据访问
详细SpringBoot教程之启动配置原理
详细SpringBoot教程之缓存开发
启动配置原理
到这里,我们讲解的只是其实已经够我们开发一些比较小的SpringBoot应用了,但是我们随着在使用的过程中,可以对SpringBoot进行底层原理分析,这样我们才能真正将SpringBoot使用的得心应手,然后我们还可以自己开发自己的starter场景启动器,是不是很刺激,后面我们会将,而我这里首先要几个重要的事件回调机制
我们之前也提到过,一般我们的配置核心是在各个配置的META-INF/spring.factories中,而我们SpringBoot的启动相关是
- ApplicationContextInitializer
- SpringApplicationRunListener
只需要放在ioc容器中
- ApplicationRunner
- CommandLineRunner
启动流程
创建SpringApplication对象
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
运行run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
Banner printedBanner = printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
编程电子书汇总 //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
//s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
事件监听机制
配置在META-INF/spring.factories
ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializerConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
}
}
SpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
//必须有的构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);Java面试手册
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...finished...");
}
}
配置(META-INF/spring.factories)
org.springframework.context.ApplicationContextInitializer=
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=
com.atguigu.springboot.listener.HelloSpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run....");
}
}
CommandLineRunner
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
}
}
自定义starter
我们在之前的SpringBoot中,引入依赖的starter是不是很舒服,那么我们现在来学着怎么开发自己的starter,其实也非常简单,具体操作如下,首先我们使用Idea创建一个空项目
然后我们会到这里的界面,在这里,我们可以给空项目创建模块,我们如果要自己的启动器的话,那么最基本需要两个模块,不过是哪两个模块呢?这里我们要来讲讲。
我们需要搞清楚两个问题,如下
-
这个场景需要使用到的依赖是什么?
-
如何编写自动配置
我们找个starter来进行分析,如下
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie //结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
//自动配置类要能加载
//将需要启动就加载的自动配置类,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
现在我们
所以我们应该符合以下模式:
-
启动器只用来做依赖导入;
-
专门来写一个自动配置模块;
-
启动器依赖自动配置;别人只需要引入启动器(starter)
-
像mybatis-spring-boot-starter,我们命名应该自定义启动器名-spring-boot-starter
又到了我们熟悉的界面,我们要创建两个,一个是啥都不勾选的SpringBoot项目,还有一个只勾选web模块,创建好了之后,在pom.xml中进行如下更改
启动器模块
project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
modelVersion>4.0.0modelVersion>
groupId>com.atguigu.startergroupId>
artifactId>atguigu-spring-boot-starterartifactId>
version>1.0-SNAPSHOTversion>
dependencies>
dependency>
groupId>com.atguigu.startergroupId>
artifactId>atguigu-spring-boot-starter-autoconfigurerartifactId>
version>0.0.1-SNAPSHOTversion>
dependency>
dependencies>
project>
自动配置模块
project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
modelVersion>4.0.0modelVersion>
groupId>com.atguigu.startergroupId>
artifactId>atguigu-spring-boot-starter-autoconfigurerartifactId>
version>0.0.1-SNAPSHOTversion>
packaging>jarpackaging>
name>atguigu-spring-boot-starter-autoconfigurername>
description>Demo project for Spring Bootdescription>
parent>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-parentartifactId>
version>1.5.10.RELEASEversion>
relativePath/>
parent>
properties>
project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
java.version>1.8java.version>
properties>
dependencies>
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starterartifactId>
dependency>
dependencies>
project>
package com.atguigu.starter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "atguigu.hello")
public class HelloProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
package com.atguigu.starter;
public class HelloService {
HelloProperties helloProperties;
public HelloProperties getHelloProperties() {
return helloProperties;
}
public void setHelloProperties(HelloProperties helloP编程电子书汇总roperties) {
this.helloProperties = helloProperties;
}
public String sayHellAtguigu(String name){
return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
}
}
package com.atguigu.starter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.aJava面试手册nnotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication //web应用才生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean
public HelloService helloService()编程电子书汇总{
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}
下一篇
SpringBoot官网其实给了很多案例给我们参考,集成了很多模块的示例代码,SpringBoot放在GitHub上可以下载运行然后研究,下一遍对缓存进行讲解。
> 时间不一定能证明很多东西,但是一定能看透很多东西。坚信自己的选择,不动摇,使劲跑,明天会更好。