Spring Boot运行原理
概述
本文主要写了下Spring Boot运行原理,还有一个小例子。
Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的。
Spring Boot关于自动配置的源码在spring-boot-autoconfigure-1.3.0.x.jar内。如果想知道Spring Boot为我们做了哪些自动配置,可以查看这里的源码。
可以通过下面的几种方式查看当前项目中已启用可未启用的自动配置的报告:
1:运行jar时添加--debug参数:java -jar xx.jar --debug。
2:在application.properties中设置属性:debug=true。
3:也可以在开发工具中配置运行时参数,此处就不再截图了。
Spring Boot运作原理
关于Spring Boot的运作原理,还是要看@SpringBootApplication注解,这个注解是一个组合注解,它的核心功能是由@EnableAutoConfiguration注解提供的。
@EnableAutoConfiguration注解的源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
import org.springframework.context.annotation.Import; @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableAutoConfigurationImportSelector.class, Registrar.class})
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {}; String[] excludeName() default {};
}
这里的关键功能是@Import注解导入的配置功能,EnableAutoConfigurationImportSelector使用SpringFactoriesLoador.loadFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包。
spring.factories文件中声名了一些自动配置,如:

打开上面任意一个类,一般都有下面的条件注解,在spring-boot-autoconfigure-1.3.0.x.jar的org.springframwork.boot.autoconfigure.condition包下,条件注解如下。
@ConditionalOnBean:当容器里有指定的Bean的条件下。
@ConditionalOnClass:当类路径下有指定的类的条件下。
@ConditionalOnExpression:基于SpEL表达式作为判断条件。
@ConditionalOnOnJava:基于JVM版本作为判断条件。
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置。
@ConditionalOnMissingBean:当容器里没有指定Bean的情况下。
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下。
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值。
@ConditionalOnResource:类路径是否有指定的值。
@ConditionalOnSingleCandidate:当·指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean。
@ConditionalOnWenApplication:当前项目是Web项目的条件下。
这些注解都是组合了@Conditional元注解,只是使用了不同的条件。
下面我们来分析一下@ConditionalOnWebApplication注解。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.boot.autoconfigure.condition; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional; @Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
}
ConditionalOnWebApplication源码
从源码可以看出,此注解使用的条件是OnWebApplicationCondition,下面我们来看看这个条件是如何构造的:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.boot.autoconfigure.condition; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment; @Order(-2147483628)
class OnWebApplicationCondition extends SpringBootCondition {
private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context.support.GenericWebApplicationContext"; OnWebApplicationCondition() {
} public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean webApplicationRequired = metadata.isAnnotated(ConditionalOnWebApplication.class.getName());
ConditionOutcome webApplication = this.isWebApplication(context, metadata);
if (webApplicationRequired && !webApplication.isMatch()) {
return ConditionOutcome.noMatch(webApplication.getMessage());
} else {
return !webApplicationRequired && webApplication.isMatch() ? ConditionOutcome.noMatch(webApplication.getMessage()) : ConditionOutcome.match(webApplication.getMessage());
}
} private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (!ClassUtils.isPresent("org.springframework.web.context.support.GenericWebApplicationContext", context.getClassLoader())) {
return ConditionOutcome.noMatch("web application classes not found");
} else {
if (context.getBeanFactory() != null) {
String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
if (ObjectUtils.containsElement(scopes, "session")) {
return ConditionOutcome.match("found web application 'session' scope");
}
} if (context.getEnvironment() instanceof StandardServletEnvironment) {
return ConditionOutcome.match("found web application StandardServletEnvironment");
} else {
return context.getResourceLoader() instanceof WebApplicationContext ? ConditionOutcome.match("found web application WebApplicationContext") : ConditionOutcome.noMatch("not a web application");
}
}
}
}
OnWebApplicationCondition源码
从isWebApplication方法可以看出,判断条件是:
1:GenericWebApplicationContext是否在类路径中;
2:容器里是否有名为session的scope;
3:当前容器的Enviroment是否为StandardServletEnvironment;
4:当前的ResourceLoader是否为WebApplicationContext(ResourceLoador是ApplicationContext的顶级接口之一);
5:我们需要构造ConditionOutcome类的对象来帮助我们,最终通过ContitionOutcome.isMatch方法来返回布尔值来确定条件;
实例分析
通过上面写的,我们初步了解了Spring Boot的运作原理和主要的条件注解,下面来分析一个简单的Spring Boot内置的自动配置功能:http的编码配置。
在常规项目中,http编码一般是在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的自动配置是基于类型安全的配置,关于http编码的配置在HttpEncodingProperties类中,源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.boot.autoconfigure.web; import java.nio.charset.Charset;
import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(
prefix = "spring.http.encoding"//在application.properties配置的时候前缀是spring.http.encoding。
)
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");//默认编码方式UTF-8,如果要修改可以使用:spring.http.encoding.charset=编码。
private Charset charset;
private boolean force; public HttpEncodingProperties() {
this.charset = DEFAULT_CHARSET;
this.force = true;//设置forceEncoding,默认为true,若果要修改可以使用spring.http.encoding.force=false。
} public Charset getCharset() {
return this.charset;
} public void setCharset(Charset charset) {
this.charset = charset;
} public boolean isForce() {
return this.force;
} public void setForce(boolean force) {
this.force = force;
}
}
HttpEncodingProperties源码
配置Bean:
通过调用上面的配置,并根据条件配置CharacterEncodingFilter的Bean,源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.springframework.boot.autoconfigure.web; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.web.OrderedCharacterEncodingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter; @Configuration
@EnableConfigurationProperties({HttpEncodingProperties.class})//开启属性注入,通过@EnableConfigurationProperties声明,使用@Autowired注入。
@ConditionalOnClass({CharacterEncodingFilter.class})//当CharacterEncodingFilter在类路径的条件下
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},//当设置spring.http.encoding=enabled的情况下
matchIfMissing = true//如果没有设置,则默认为true,即条件符合。
)
public class HttpEncodingAutoConfiguration {
@Autowired
private HttpEncodingProperties httpEncodingProperties; public HttpEncodingAutoConfiguration() {
} @Bean//使用Java配置的方式配置CharacterEncodingFilter这个bean。
@ConditionalOnMissingBean({CharacterEncodingFilter.class})//如果容器中没有这个bean的时候新建bean。
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.httpEncodingProperties.getCharset().name());
filter.setForceEncoding(this.httpEncodingProperties.isForce());
return filter;
}
}
HttpEncodingAutoConfiguration源码
自己写一个starter pom
我们也可以仿照上面http编码配置的例子自己写一个自动配置。代码如下:
package com.wisely.spring_boot_starter_hello; import org.springframework.boot.context.properties.ConfigurationProperties; /**
* 属性配置
*/
@ConfigurationProperties(prefix = "hello")
public class HelloServiceProperties { private static final String MSG = "world"; private String msg = MSG; public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
属性配置
package com.wisely.spring_boot_starter_hello; /**
* 判断依据类
*/
public class HelloService {
private String msg; public String sayHello() {
return "Hello" + msg;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
判断依据类
package com.wisely.spring_boot_starter_hello; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* 自动配置类
*/
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enabled", matchIfMissing = true)
public class HelloServiceAutoConfiguration { @Autowired
private HelloServiceProperties helloServiceProperties; @Bean
@ConditionalOnMissingBean(HelloService.class)
public HelloService helloService() {
HelloService helloService = new HelloService();
helloService.setMsg(helloServiceProperties.getMsg());
return helloService;
}
}
自动配置类
在src/main/resources下新建META-INF/spring.factories,并添加代码:

然后新建一个项目,在pom.xml添加依赖

最后运行看效果。
package com.wisely.ch6_5; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.wisely.spring_boot_starter_hello.HelloService;
@RestController
@SpringBootApplication
public class Ch65Application { @Autowired
HelloService helloService; @RequestMapping("/")
public String index(){
return helloService.sayHello();
} public static void main(String[] args) {
SpringApplication.run(Ch65Application.class, args);
}
}
运行
Spring Boot运行原理的更多相关文章
- Spring Boot 运行原理
Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...
- Spring boot运行原理-自定义自动配置类
在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom. 本章对于后续继续学习S ...
- Spring Boot 运行原理 - 核心注解
https://www.jianshu.com/p/23f504713b94 核心注解 打开上面任意一个AutoConfiguration文件,一般都有下面的条件注解,在spring-boot-aut ...
- Spring Boot 运作原理
Spring Boot 运作原理 1.Spring Boot 简介 SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了 ...
- spring boot启动原理步骤分析
spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...
- struts1,struts2,hibernate,spring的运行原理结构图
一.struts1运行原理 1.初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的Servlet,在启动时总控制器会读取配置文件(s ...
- spring boot 运行提示:Process finished with exit code 1
spring boot 运行提示:Process finished with exit code 1 经检查发现是由于在application.properties配置文件中将某些自定义配置项移除了, ...
- Spring Boot启动原理解析
Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置 ...
- Spring Boot核心原理
Spring Boot核心原理 spring-boot-starter-xxx 方便开发和配置 1.没有depoy setup tomcat 2.xml文件里面的没有没有了 @SpringBootA ...
随机推荐
- Java丨时间判断谁前谁后
直奔主题: String date_str1 = "2016-06-02 23:03:123"; String date_str2 = "2016-06-03 03:03 ...
- bootstrap学习大纲
bootstrap 学习分三部分,分别是 css样式,css组件,js插件. 下面介绍三部分分别要学习的内容: 1.css样式:栅格系统,排版,代码,表格,表单,按钮,图片,辅助类,响应式工具. 2. ...
- CodeForces - div1 -650D:Zip-line(主席树 占位)
(和南京那题很像,比赛的时候就两个队A了.我们队找到了思路,但是花了1个多小时没有写出来,emmmm,我的锅,当时线段树写丑了. 题意:给定数组,Q次询问,假设把第i个大小hi改为b,求最长上升子序列 ...
- 洛谷P4092树——并查集
题目:https://www.luogu.org/problemnew/show/P4092 利用并查集,倒序离线,那么从倒序来看被撤销标记的点就再也不会被标记,所以用并查集跳过: 莫名其妙的WA,调 ...
- ubuntu12.04下有线网无电缆插入问题
解决方案: 1.查看是否没装网卡驱动: 2.检查网卡是否损坏: 3.检查网线是否损坏(注意:有时候不一定是网线损坏了,而是网线太细了,导通性不好.本人用细网线试了一下,windows下面可以连接网络, ...
- 基于Jenkins自动构建系统开发
1 绪论 1.1 课题的研究背景 随着IT行业的不断发展,软件开发的复杂度也随着不断提高.与此同时,软件的开发团队也越来越庞大,而如何更好地协同整个团队进行高效准确的工作,从而确保软件开发的质量已经 ...
- 序列化框架MJExtension详解 + iOS ORM框架
当开发中你的模型中属性名称和 字典(JSON/XML) 中的key 不能一一对应时, 或者当字典中嵌套了多层字典数组时..., 以及教你如何用 MJExtension 配置类来统一管理你的模型配置, ...
- Android之打包签名
一.什么的是打包? 打包就是根据签名和其他标识生成安装包. 二.什么是签名? 1.在android应用文件(apk)中保存的一个特别字符串 2.用来标识不同的应用开发者:开发者A,开发者B 3.一个应 ...
- TP5之页面跳转样式
1.效果图 2.上干货 为了增加对移动设备的支持,在 /application/common.php加入以下函数: function isMobile() { if (isset ($_SERVER ...
- ajax 的三种使用方法
第一种 也是最古老的一种方法之一 from 表单直接提交数据到php文件里 action为路径 <form method="post" action="./inde ...