首先Spring Boot项目中都会如下启动类:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
从上面代码可以看出,注解@SpringBootApplication
和SpringApplication.run()
方法是最为重要的部分。这里主要来看看@SpringBootApplication
注解部分。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
虽然定义使用了多个Annotation进行了原信息标注,但实际上重要的只有三个Annotation:
- @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
- @EnableAutoConfiguration
- @ComponentScan
如果在启动类使用这个三个注解,整个SpringBoot应用依然可以与之前的启动类功能一样。但每次写这3个比较啰嗦,所以写一个@SpringBootApplication方便点。
这三个注解中@Configuration和@ComponentScan对我们来说并不陌生,今天我们的主角是@EnableAutoConfiguration
。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">exclude</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> String<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">excludeName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
其中最关键的要属@Import(AutoConfigurationImportSelector.class)
,借助AutoConfigurationImportSelector
,@EnableAutoConfiguration
可以帮助SpringBoot应用将所有符合条件的@Configuration
配置都加载到当前SpringBoot创建并使用的IoC容器。
借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration
可以智能的自动配置功效才得以大功告成!
在AutoConfigurationImportSelector类中可以看到通过 SpringFactoriesLoader.loadFactoryNames()
把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中,spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:
- @ConditionalOnClass : classpath中存在该类时起效
- @ConditionalOnMissingClass : classpath中不存在该类时起效
- @ConditionalOnBean : DI容器中存在该类型Bean时起效
- @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
- @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
- @ConditionalOnExpression : SpEL表达式结果为true时
- @ConditionalOnProperty : 参数设置或者值一致时起效
- @ConditionalOnResource : 指定的文件存在时起效
- @ConditionalOnJndi : 指定的JNDI存在时起效
- @ConditionalOnJava : 指定的Java版本存在时起效
- @ConditionalOnWebApplication : Web应用环境下起效
- @ConditionalOnNotWebApplication : 非Web应用环境下起效
...
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
SpringFactoriesLoader
SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件META-INF/spring-factories
加载配置,spring-factories是一个典型的java properties文件,只不过Key和Value都是Java类型的完整类名,比如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
- 1
对于@EnableAutoConfiguration
来说,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI扩展的场景,而在@EnableAutoConfiguration
场景中,它更多提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration
的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration
作为查找的Key,获得对应的一组@Configuration
类。
SpringFactoriesLoader是一个抽象类,类中定义的静态属性定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
,此外还有三个静态方法:
- loadFactories:加载指定的factoryClass并进行实例化。
- loadFactoryNames:加载指定的factoryClass的名称集合。
- instantiateFactory:对指定的factoryClass进行实例化。
在loadFactories方法中调用了loadFactoryNames以及instantiateFactory方法。
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<T>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
loadFactories方法首先获取类加载器,然后调用loadFactoryNames
方法获取所有的指定资源的名称集合、接着调用instantiateFactory
方法实例化这些资源类并将其添加到result集合中。最后调用AnnotationAwareOrderComparator.sort
方法进行集合的排序。
一个例子
上面介绍了很多原理的知识,下面结合一个例子来加深理解,例子展示的是当项目启动时如果某个类存在就自动配置这个Bean,并且这个属性可以在application.properties中配置
新建一个Maven项目,pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<span class="token generics function"><span class="token punctuation"><</span>groupId<span class="token punctuation">></span></span>com<span class="token punctuation">.</span>chm<span class="token punctuation">.</span>test<span class="token operator"><</span><span class="token operator">/</span>groupId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>artifactId<span class="token punctuation">></span></span>spring<span class="token operator">-</span>boot<span class="token operator">-</span>starter<span class="token operator">-</span>hello<span class="token operator"><</span><span class="token operator">/</span>artifactId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>version<span class="token punctuation">></span></span><span class="token number">1.0</span><span class="token operator">-</span>SNAPSHOT<span class="token operator"><</span><span class="token operator">/</span>version<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>packaging<span class="token punctuation">></span></span>jar<span class="token operator"><</span><span class="token operator">/</span>packaging<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>name<span class="token punctuation">></span></span>spring<span class="token operator">-</span>boot<span class="token operator">-</span>starter<span class="token operator">-</span>hello<span class="token operator"><</span><span class="token operator">/</span>name<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> FIXME change it to the project's website <span class="token operator">--</span><span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>url<span class="token punctuation">></span></span>http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>www<span class="token punctuation">.</span>example<span class="token punctuation">.</span>com<span class="token operator"><</span><span class="token operator">/</span>url<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>properties<span class="token punctuation">></span></span>
<span class="token operator"><</span>project<span class="token punctuation">.</span>build<span class="token punctuation">.</span>sourceEncoding<span class="token operator">></span>UTF<span class="token operator">-</span><span class="token number">8</span><span class="token operator"><</span><span class="token operator">/</span>project<span class="token punctuation">.</span>build<span class="token punctuation">.</span>sourceEncoding<span class="token operator">></span>
<span class="token operator"><</span>maven<span class="token punctuation">.</span>compiler<span class="token punctuation">.</span>source<span class="token operator">></span><span class="token number">1.8</span><span class="token operator"><</span><span class="token operator">/</span>maven<span class="token punctuation">.</span>compiler<span class="token punctuation">.</span>source<span class="token operator">></span>
<span class="token operator"><</span>maven<span class="token punctuation">.</span>compiler<span class="token punctuation">.</span>target<span class="token operator">></span><span class="token number">1.8</span><span class="token operator"><</span><span class="token operator">/</span>maven<span class="token punctuation">.</span>compiler<span class="token punctuation">.</span>target<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>properties<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>dependencies<span class="token punctuation">></span></span>
<span class="token generics function"><span class="token punctuation"><</span>dependency<span class="token punctuation">></span></span>
<span class="token generics function"><span class="token punctuation"><</span>groupId<span class="token punctuation">></span></span>junit<span class="token operator"><</span><span class="token operator">/</span>groupId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>artifactId<span class="token punctuation">></span></span>junit<span class="token operator"><</span><span class="token operator">/</span>artifactId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>version<span class="token punctuation">></span></span><span class="token number">4.11</span><span class="token operator"><</span><span class="token operator">/</span>version<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>scope<span class="token punctuation">></span></span>test<span class="token operator"><</span><span class="token operator">/</span>scope<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>dependency<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>dependency<span class="token punctuation">></span></span>
<span class="token generics function"><span class="token punctuation"><</span>groupId<span class="token punctuation">></span></span>org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token operator"><</span><span class="token operator">/</span>groupId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>artifactId<span class="token punctuation">></span></span>spring<span class="token operator">-</span>boot<span class="token operator">-</span>autoconfigure<span class="token operator"><</span><span class="token operator">/</span>artifactId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>version<span class="token punctuation">></span></span><span class="token number">2.0</span><span class="token number">.4</span><span class="token punctuation">.</span>RELEASE<span class="token operator"><</span><span class="token operator">/</span>version<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>dependency<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>dependency<span class="token punctuation">></span></span>
<span class="token generics function"><span class="token punctuation"><</span>groupId<span class="token punctuation">></span></span>org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token operator"><</span><span class="token operator">/</span>groupId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>artifactId<span class="token punctuation">></span></span>spring<span class="token operator">-</span>boot<span class="token operator">-</span>configuration<span class="token operator">-</span>processor<span class="token operator"><</span><span class="token operator">/</span>artifactId<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>version<span class="token punctuation">></span></span><span class="token number">2.0</span><span class="token number">.4</span><span class="token punctuation">.</span>RELEASE<span class="token operator"><</span><span class="token operator">/</span>version<span class="token operator">></span>
<span class="token generics function"><span class="token punctuation"><</span>optional<span class="token punctuation">></span></span><span class="token boolean">true</span><span class="token operator"><</span><span class="token operator">/</span>optional<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>dependency<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>dependencies<span class="token operator">></span>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
项目目录结构如下:
Hello.java
public class Hello {
<span class="token keyword">private</span> String msg<span class="token punctuation">;</span> <span class="token keyword">public</span> String <span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"hello "</span> <span class="token operator">+</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> String <span class="token function">getMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setMsg</span><span class="token punctuation">(</span>String msg<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>msg <span class="token operator">=</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
HelloProperties.java
@ConfigurationProperties(prefix = "hello") //获取属性值 public class HelloProperties {
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String MSG <span class="token operator">=</span> <span class="token string">"world"</span><span class="token punctuation">;</span> <span class="token keyword">private</span> String msg <span class="token operator">=</span> MSG <span class="token punctuation">;</span> <span class="token keyword">public</span> String <span class="token function">getMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setMsg</span><span class="token punctuation">(</span>String msg<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>msg <span class="token operator">=</span> msg<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
HelloAutoConfiguration.java
@Configuration //为带有@ConfigurationProperties注解的Bean提供有效的支持。 // 这个注解可以提供一种方便的方式来将带有@ConfigurationProperties注解的类注入为Spring容器的Bean。 @EnableConfigurationProperties(HelloProperties.class)//开启属性注入,通过@autowired注入 @ConditionalOnClass(Hello.class)//判断这个类是否在classpath中存在,如果存在,才会实例化一个Bean // The Hello bean will be created if the hello.enable property exists and has a value other than false // or the property doesn't exist at all. @ConditionalOnProperty(prefix="hello", value="enabled", matchIfMissing = true) public class HelloAutoConfiguration {
<span class="token annotation punctuation">@Autowired</span> <span class="token keyword">private</span> HelloProperties helloProperties<span class="token punctuation">;</span> <span class="token annotation punctuation">@Bean</span> <span class="token annotation punctuation">@ConditionalOnMissingBean</span><span class="token punctuation">(</span>Hello<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token comment">//容器中如果没有Hello这个类,那么自动配置这个Hello</span> <span class="token keyword">public</span> Hello <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Hello hello <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hello<span class="token punctuation">.</span><span class="token function">setMsg</span><span class="token punctuation">(</span>helloProperties<span class="token punctuation">.</span><span class="token function">getMsg</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> hello<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.chm.test.HelloAutoConfiguration
- 1
- 2
最后使用mvn package
将上面项目打包,使用mvn install:install-file
命令将打包文件上传到本地Maven仓库进行测试。下面再新建一个Maven项目用于测试。
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<groupId>com.chm.test</groupId>
<artifactId>test-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<name>test-starter</name>
<!-- FIXME change it to the project’s website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<boot.version>2.0.4.RELEASE</boot.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.chm.test</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>KaTeX parse error: Expected 'EOF', got '&' at position 95: …n punctuation">&̲lt;/</span>vers…{boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
App.java
@SpringBootApplication @RestController public class App {
<span class="token annotation punctuation">@Autowired</span> <span class="token keyword">private</span> Hello hello<span class="token punctuation">;</span> <span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> String <span class="token function">index</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> hello<span class="token punctuation">.</span><span class="token function">sayHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span>String<span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> SpringApplication<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>App<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
application.properties
#可以不配置
hello.enabled=true
hello.msg=charmingfst
#以debug模式运行
debug=true
- 1
- 2
- 3
- 4
- 5
- 6
- 7
以debug模式运行,可以看到我们的配置:
启动项目,打开浏览器,运行结果如下:
参考文章
</div>