在前面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. 2月9日 《Java 8实战》读后感

    第一部分 基础知识 第3章 Lambda表达式 使用函数式接口 Predicate Consumer Function 第二部分 函数式数据处理 第4章 引入流 第5章 使用流 第6章 用流收集数据 ...

  2. android——SQLite数据库存储(创建)

    Android 专门提供了SQLiteOpenHelper帮助类,借助这个类就可以非常简单的对数据库进行创建和升级. 首先SQLiteOpenHelper是一个抽象类,在使用的时候需要创建一个自己的帮 ...

  3. 什么时候使用redis?什么时候使用memcache?

    要清楚为什么,redis具有高可用特性,并且可固化,但特性有时候不能成为选择他的理由,一些业务场景中并不需要这样的特性.   什么时候倾向于选择redis? 1.复杂数据结构 value是哈希,列表, ...

  4. Python+Selenium - Web自动化测试(一):环境搭建

    清单列表: Python 3x Selenium Chrome Pycharm 一.Python的安装: Python官网下载地址:https://www.python.org/ 1.  进入官网地址 ...

  5. Android UI控件常用库汇总

    现在App的开发已经是非常成熟,涌现了一大批开源的工具.这些项目能够提高我们的搬砖效率.以下是一些在开发中比较常使用的控件和库. ListView WaveSwipeRefreshLayout 水滴效 ...

  6. Python --深入浅出Apriori关联分析算法(二) Apriori关联规则实战

    上一篇我们讲了关联分析的几个概念,支持度,置信度,提升度.以及如何利用Apriori算法高效地根据物品的支持度找出所有物品的频繁项集. Python --深入浅出Apriori关联分析算法(一) 这次 ...

  7. VMware Workstation 15 Pro 永久激活密钥

    VMware Workstation 15 Pro 永久激活密钥   一. 激活密钥 YG5H2-ANZ0H-M8ERY-TXZZZ-YKRV8 UG5J2-0ME12-M89WY-NPWXX-WQH ...

  8. Jesus Is Here[递推]2015沈阳online

    题目链接https://nanti.jisuanke.com/t/41175 自从百度之星初赛一上自己做出来一道打表找规律的题之后,这种膨胀的感觉让我近乎丧失理智,今天这道题我死死盯了两三个小时硬是没 ...

  9. python 25 类的成员

    目录 1. 类的私有成员 -- __ 1.1 类的私有静态属性 1.2 类的私有方法 1.3 对象的私有方法 2. 类的其他方法 2.1 类方法 @classmethod 2.2 静态方法 @stat ...

  10. HTML+JavaScript自己动手做日历

    当我们需要在页面中显示某月的事项,或是选择某一段日期时,常常要使用到日历组件.这一组件同样有着许多现成的类库,然而亲自动手开发一个日历,从中了解其实现原理也是非常必要的.在本例中我们就将制作一款非常经 ...