在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom。

本章对于后续继续学习SpringBoot至关重要,了解SpringBoot运行原理对于我们深入学习SpringBoot有着非常重要的作用。

SpringBoot的自动配置从何而来

要想了解SpringBoot的自动配置,我们可以在源码看到相关代码和配置。
SpringBoot关于自动配置的源码在spring-boot-autoconfigure-2.1.x.jar内,打开maven依赖我们可以看见

如果想了解SpringBoot为我们做了哪些自动配置,可以通过下面方式查看当前项目中已启用和未启用的自动配置的报告。
  1. 运行jar时增加--debug参数:
java -jar xx.jar --debug
  1. 在application.properties中设置属性:
debug=true
启动时,通过控制台我们可以看到哪些配置已使用自动配置,哪些配置没有自动配置。

已启用自动配置

未启用自动配置

仔细看上图我们可以发现,相关如

@ConditionalOnClass found required class ...    \   @ConditionalOnClass did not find required class ...

的字眼非常多,可见@ConditionalOnClass注解可能在自动配置中起着主要作用,那究竟是如何起作用的呢?

运行原理

关于SpringBoot的运作原理,我们还是回归到@SpringBootApplication注解上来,这个注解是一个组合注解,它的核心功能是一个开启自动配置注解@EnableAutoConfiguration

下面我们来看下@EnableAutoConfiguration注解的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {};
}

这里我们重点关注@Import的导入功能,AutoConfigurationImportSelector使用SpringFactoriesLoader.loadFactoryNames方法来扫描META-INF/spring.factories文件中描述的jar包,所以我们立马到刚刚打开的自动配置类中的META-INF/spring.factories中找一下是否真的有这样一个文件。

好家伙,还真的有

马上打开看一看

核心注解

我们打开上面配置的任何其中一个注解,一般都有下面的条件注解,打开源码spring-boot-autoconfigure下的org/springframework/boot/condition看看都有哪些注解

简单介绍一下每个注解代表代表的条件:
@ConditionalOnBean: 当容器里有指定的Bean条件下。
@ConditionalOnClass: 当类路径下有指定的类的条件下。
@ConditionalOnExpression: 基于SpEL表达式作为判断条件。
@ConditionalOnJava: 基于JVM版本作为判断条件。
@ConditionalOnjndi: 在基于JNDI存在的条件下查找指定的位置。
@ConditionalOnMissingBean: 当容器里没有Bean的情况下。
@ConditionalOnMIssingClass: 当类路径下没有指定的类的条件下。
@ConditionalOnNotWebApplication: 当前项目不是Web项目的条件下。
@ConditionalOnProperty: 指定的属性是否有指定的值。
@ConditionalOnResource: 类路径是否有指定的值。
@ConditionalOnSingleCandidate: 当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication: 当前项目是web项目的条件下
这些注解都是组合了@Conditional元注解,只是使用了不同的条件(Condition)
下面我们简单分析一下@ConditionalOnWebApplication注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY; public static enum Type {
ANY,
SERVLET,
REACTIVE; private Type() {
}
}
}
看看OnWebApplicationCondition.class 是如何定义条件的

这里我们主要看isWebApplication方法,判断条件主要如下:
(1)GenericWebApplicationContext是否在类路径中;
(2)容器中是否有名为session的scope;
(3)当前容器的Environment是否为ConfigurableWebEnvironment
(4)当前的ResourceLoader是否为WebApplicationContext(ResourceLoader是ApplicationContext的顶级接口之一);
(5)构造ConditionOutcom类的isMatch方法返回布尔值来确定条件。

这里的isWebApplication()方法,spring-boot-1.x和2.x有区别,在1.x中只判断了servlet,而在2.x增加了了reative和any的webapplication判断

简单示例

在以往的web项目中,通常需要在web.xml中配置一个filter,对请求进行编码,如下所示:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
因此如果要实现自动配置的话,需要满足一下条件:
(1)能配置CharacterEncodingFilter这个Bean;
(2)能配置encoding和forceEncoding这两个参数。

参数配置

在上一章我们讲到了类型安全的配置,Spring Boot的自动配置也是基于这一点实现的,这里的配置可以在application.properties中直接配置。
源码如下图:

代码解释:

(1)在application.properties配置的前缀是spring.http.encoding;

(2)默认编码方式为UTF-8,若修改可配置spring.http.encoding.charset=编码;

(3)设置force,默认为true,若修改可配置spring.http.encoding.force=false;

配置Bean

上面我们已经配置好相关参数,现在根据条件配置CharacterEncodingFilter的Bean,下面看看源码:

@Configuration
@EnableConfigurationProperties({HttpProperties.class}) //1
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})//2
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//3
public class HttpEncodingAutoConfiguration {
private final Encoding properties; public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
} @Bean//4
@ConditionalOnMissingBean//5
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}

代码解释:

(1)开启属性注入,通过@EnableCondigurationProperties声明

(2)当CharacterEncodingFilter在类路径的条件下;

(3)当设置spring.http.encoding.enabled的情况下,如果没有设置则默认为true,即条件符合;

(4)像使用Java配置的方式配置CharacterEncoding这个Bean;

(5)当容器中没有这个Bean的时候新建Bean

实战-自定义一个starter pom

前面我们已经详细讲述了spring boot是如何实现自动化配置的,现在我们来动手自己写一个starter pom实现自动化配置。

要求:当某个类存在的时候,自动配置这个类的Bean,并可将Bean的属性在application.properties中配置

创建一个普通MAVEN工程

创建一个普通MAVEN工程,并加入spring boot自动配置依赖

属性配置

我们仿照HttpProperties来配置,我们自定义starter 的配置文件
@ConfigurationProperties(prefix = "xicent.service")
public class MyServiceProperties {
private MyServiceProperties.MyProperties myProperties = new MyServiceProperties.MyProperties(); public MyProperties getMyProperties() {
return myProperties;
} public void setMyProperties(MyProperties myProperties) {
this.myProperties = myProperties;
} public static class MyProperties{
public static final String DEFAULT_NAME;
private String author;
private String age; static {
DEFAULT_NAME = "wjx";
} 省略 get/set..

使用类型安全的方式获取属性。author如果不设置,会给默认值。

判断依据类

public class MyService {
private String name;
private String age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAge() {
return age;
} public void setAge(String age) {
this.age = age;
}
}

这个类作为我们的判断依据类,如果存在,则创建这个类的Bean。

自动配置类(关键)


@Configuration
@EnableConfigurationProperties(MyServiceProperties.class)
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "xicent.service",value = "enabled",matchIfMissing = true)
public class MyServiceAutoConfiguration {
private MyServiceProperties.MyProperties properties; public MyServiceAutoConfiguration(MyServiceProperties properties) {
this.properties = properties.getMyProperties();
} @Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService(){
MyService myService = new MyService();
myService.setName(properties.getAuthor());
myService.setAge(properties.getAge()); return myService;
}
}

这里我们仿照了HttpEncodingAutoConfiguration的写法,其实MyServiceProperties也可以直接用@Autowired直接注入的。

@ConditionalOnClass判断MyService这个类是否在类路径中存在,并且容器中没有这个Bean的情况下,我们对这个Bean进行自动配置。

注册配置

在前面我们也带大家看过,每个自动配置的包中,在src/main/resources/META-INF下都会有一个spring.factories配置文件。
下面我们就将我们刚刚写好的自动配置类,在这个配置文件中进行注册。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xicent.starter.config.MyServiceAutoConfiguration
如果有多个自动配置,用","隔开,此处的"\"是为了换行后仍能读到属性。

添加在仓库

如果是公司提供给其他项目使用,则可以直接上传到公司私服。这里为了方便测试,我们就打包到本地仓库。
直接点击idea->maven project->lifecycle->install

新建Spring Boot项目加入依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>com.xicent</groupId>
<artifactId>mystarter</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>

mystarter是我们刚刚手动写好的自动配置,加入web是为了待会用接口访问,方便测试。

添加配置

刚刚我们的自动配置中允许配置两个参数,其中author如果不配置,提供默认值。
xicent.service.my-properties.age=23
xicent.service.my-properties.author=kris

注入依赖

@SpringBootApplication
@RestController
public class TeststarterApplication { @Autowired
MyService myService; @GetMapping("/")
String testStarter(){
return myService.getName()+":"+myService.getAge();
} public static void main(String[] args) {
SpringApplication.run(TeststarterApplication.class, args);
} }

访问接口

如果不配置author

到这里,我们自定义的starter pom就大功告成啦~ 是不是感觉其实挺简单的,Spring Boot自动配置的神秘面纱也就被我们悄悄揭开了。

如果您觉得有用记得分享喔~

公众号搜索:喜讯XiCent 获取更多福利~


公众号搜索:喜讯XiCent 获取更多福利资源~

Spring boot运行原理-自定义自动配置类的更多相关文章

  1. 自定义的Spring Boot starter如何设置自动配置注解

    本文首发于个人网站: 在Spring Boot实战之定制自己的starter一文最后提到,触发Spring Boot的配置过程有两种方法: spring.factories:由Spring Boot触 ...

  2. Spring Boot运行原理

    概述 本文主要写了下Spring Boot运行原理,还有一个小例子. Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的. Spring Boot关 ...

  3. Spring Boot源码探索——自动配置的内部实现

    前面写了两篇文章 <Spring Boot自动配置的魔法是怎么实现的>和 <Spring Boot起步依赖:定制starter>,分别分析了Spring Boot的自动配置和起 ...

  4. spring boot @EnableWebMvc禁用springMvc自动配置原理。

    说明: 在spring boot中如果定义了自己的java配置文件,并且在文件上使用了@EnableWebMvc 注解,那么sprig boot 的默认配置就会失效.如默认的静态文件配置路径:&quo ...

  5. 【串线篇】spring boot嵌入式Servlet容器自动配置原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PREC ...

  6. Spring Boot 运行原理

    Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...

  7. Spring boot 配置文件参数映射到配置类属性

    [参考文章]:SpringBoot之@EnableConfigurationProperties分析 [参考文章]:在Spring Boot中使用 @ConfigurationProperties 注 ...

  8. Spring Boot(15)——自动配置Validation

    自动配置Validation当应用中的Classpath下存在javax.validation的实现时,Spring Boot的org.springframework.boot.autoconfigu ...

  9. Spring Boot 运行原理 - 核心注解

    https://www.jianshu.com/p/23f504713b94 核心注解 打开上面任意一个AutoConfiguration文件,一般都有下面的条件注解,在spring-boot-aut ...

随机推荐

  1. 大话 Spring Session 共享

    javaweb中我们项目稍微正规点,都会用到单点登录这个技术.实现它的方法各家有各界的看法.这几天由于公司项目需求正在研究.下面整理一下最近整理的心得. 简介 在分布式项目中另我们头疼的是多项目之间的 ...

  2. (二)c#Winform自定义控件-按钮

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  3. spring-boot-plus后台快速开发脚手架之代码生成器使用(十)

    spring-boot-plus 代码生成 Generator 代码生成内容 spring-boot-plus在mybatis-plus基础上,新增param/vo等模板 拓展controller/s ...

  4. 写论文的第三天 自建zookeeper集群

    日志___2019.1.25 基于hadoop集群搭建zookeeper集群 Filezilla上传zookeeper压缩包到主节点 安装zookeeper到/usr/local目录 命令:tar – ...

  5. Tomcat源码分析 (十)----- 彻底理解 Session机制

    Tomcat Session 概述 首先 HTTP 是一个无状态的协议, 这意味着每次发起的HTTP请求, 都是一个全新的请求(与上个请求没有任何联系, 服务端不会保留上个请求的任何信息), 而 Se ...

  6. Okhttp3源码解析(2)-Request分析

    ### 前言 前面我们讲了 [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析]( ...

  7. 关于C#中的“?”

    目录 1. 可空类型修饰符(T?) 2. 三元(运算符)表达式(?: ) 3. 空合并运算符(??) 4. NULL检查运算符(?.) 关于C#中的"?" shanzm-2019年 ...

  8. .net测试篇之测试神器Autofixture在几个复杂场景下的使用示例以及与Moq结合

    系列目录 为String指定一个值. 在第三节里我们讲了如何使用自定义配置加上一个自定义算法生成一个自定义字符串,然而有些时候我们仅仅是需要某个字段是有意义的,这个时候随便生成的字符串也满足不了我们的 ...

  9. 阿里云部署 Flask + WSGI + Nginx 转载详解

    我采用的部署方案是: Web 服务器采用 uwsgi host Flask 用 Supervisor 引用 uwsgi 作常规启动服务 基于 Nginx 作反向代理 首先, 阿里云服务器可以通过 SS ...

  10. 深入Java源码剖析之字符串常量

    字符串在Java生产开发中的使用频率是非常高的,可见,字符串对于我们而言非常关键.那么从C语言过来的同学会发现,在C中是没有String类型的,那么C语言要想实现字符串就必须使用char数组,通过一个 ...