[toc]
1.新建入门程序
使用IDEA的SpringInitializer 创建一个 SpringBoot 应用
pom文件我只引入了 spring-boot-starter-web
:
1 | <dependency> |
启动类如下1
2
3
4
5
6
7
8
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
尝试注释掉@SpringBootApplication
注解并启动1
2
3org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:156) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
...
因为没有 ServletWebServerFactory
,而导致无法启动IOC容器。
那@SpringBootApplication
是做什么的呢?
2.@SpringBootApplication
还原@SpringBootApplication
注解
1 | /** |
从SpringBoot1.2.0开始出现的,在 SpringBoot1.1及以前的版本,在启动类上标注的注解应该是三个:@Configuration
+@EnableAutoConfiguration
+ @ComponentScan
,只不过从1.2以后 SpringBoot 帮我们整合起来了,即 @SpringBootApplication
= (默认属性)@Configuration
+ @EnableAutoConfiguration
+ @ComponentScan
注意
@SpringBootConfiguration
点开查看发现里面还是应用了@Configuration
文档注释已经描述的很详细:它是一个组合注解,包括3个注解。标注它之后就会触发自动配置(@EnableAutoConfiguration
)和组件扫描(@ComponentScan
)。
那这三个注解有什么作用呢?
3. @SpringBootConfiguration
1 | /** |
从文档注释以及它的声明上可以看出,它被 @Configuration
标注,说明它实际上是标注配置类的,而且是标注主启动类的。
3.1 @Configuration的作用
@Configuration
是JavaConfig形式的Spring Ioc容器的配置类使用的那个@Configuration
,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration
之后,本身其实也是一个IoC容器的配置类。
例如:
3.1.1表达形式层面
基于XML配置的方式是这样:
1 |
|
而基于JavaConfig的配置方式是这样:
1 |
|
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。
3.1.2注册bean定义层面
基于XML的配置形式是这样:
1 | <bean id="mockService" class="..MockServiceImpl"> |
而基于JavaConfig的配置形式是这样的:
1 |
|
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
3.1.3表达依赖注入关系层面
为了表达bean与bean之间的依赖关系,在XML形式中一般是这样:
1 | <bean id="mockService" class="..MockServiceImpl"> |
而基于JavaConfig的配置形式是这样的:
1 |
|
如果一个bean的定义依赖其他bean,则直接调用对应的JavaConfig类中依赖bean的创建方法就可以了。
@Configuration:提到@Configuration就要提到他的搭档@Bean。使用这两个注解就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
1 | <beans> |
相当于:
1 |
|
3.1.4验证
初始化一个IOC容器,并打印IOC容器中的所有bean的name:
1 | public class MainApp { |
1 | org.springframework.context.annotation.internalConfigurationAnnotationProcessor |
可以发现组件,以及配置类本身被成功加载。
@Configuration
的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
@Bean
注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
3.2 @SpringBootConfiguration的附加作用
借助IDEA搜索 @SpringBootConfiguration
的出现位置,发现除了 @SpringBootApplication
外,只有一个位置使用了它:
发现是一个测试包中的usage(默认的 SpringInitializer
会把 spring-boot-starter-test
一起带进来,故可以搜到这个usage。如果小伙伴手动使用Maven创建 SpringBoot 应用且没有导入 spring-boot-start-test
依赖,则这个usage都不会搜到)。
翻看 SpringBoot 的官方文档,发现通篇只有两个位置提到了 @SpringBootConfiguration
,还真有一个跟测试相关:
第三段中有对 @SpringBootConfiguration
的描述:
The search algorithm works up from the package that contains the test until it finds a class annotated with
@SpringBootApplication
or@SpringBootConfiguration
. As long as you structured your code in a sensible way, your main configuration is usually found.搜索算法从包含测试的程序包开始工作,直到找到带有
@SpringBootApplication
或@SpringBootConfiguration
标注的类。只要您以合理的方式对代码进行结构化,通常就可以找到您的主要配置。
这很明显是解释了 SpringBoot 主启动类与测试的关系,标注 @SpringBootApplication
或 @SpringBootConfiguration
的主启动类会被 Spring测试框架 的搜索算法找到。回过头看上面的截图,引用 @SpringBootConfiguration
的方法恰好叫 getOrFindConfigurationClasses,与文档一致。
4. @ComponentScan
@ComponentScan
这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan
的功能其实就是自动扫描并加载符合条件的组件(比如@Component
和@Repository
等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan
自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan
所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
不过在上面的声明中有显式的指定了两个过滤条件:
1 | (excludeFilters = { (type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), |
这两个过滤器又是做什么的呢
4.1 TypeExcludeFilter
文档注释原文翻译:
Provides exclusion TypeFilters that are loaded from the BeanFactory and automatically applied to SpringBootApplication scanning. Can also be used directly with @ComponentScan as follows:
提供从 BeanFactory 加载并自动应用于
@SpringBootApplication
扫描的排除TypeFilter
。
1
2 > (excludeFilters = (type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
>
Implementations should provide a subclass registered with BeanFactory and override the match(MetadataReader, MetadataReaderFactory) method. They should also implement a valid hashCode and equals methods so that they can be used as part of Spring test’s application context caches. Note that TypeExcludeFilters are initialized very early in the application lifecycle, they should generally not have dependencies on any other beans. They are primarily used internally to support spring-boot-test.
实现应提供一个向 BeanFactory 注册的子类,并重写
match(MetadataReader, MetadataReaderFactory)
方法。它们还应该实现一个有效的hashCode
和equals
方法,以便可以将它们用作Spring测试的应用程序上下文缓存的一部分。注意,
TypeExcludeFilters
在应用程序生命周期的很早就初始化了,它们通常不应该依赖于任何其他bean。它们主要在内部用于支持spring-boot-test
。
从文档注释中大概能看出来,它是给了一种扩展机制,能让我们向IOC容器中注册一些自定义的组件过滤器,以在包扫描的过程中过滤它们。
这种Filter的核心方法是 match
方法,它实现了过滤的判断逻辑:
1 |
|
注意看if结构体中的第一句,它会从 BeanFactory
(可以暂时理解成IOC容器)中获取所有类型为 TypeExcludeFilter
的组件,去执行自定义的过滤方法。
由此可见,TypeExcludeFilter
的作用是做扩展的组件过滤。
4.2 AutoConfigurationExcludeFilter
看这个类名,总感觉跟自动配置相关,还是看一眼它的源码:
1 | public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) |
它的 match
方法要判断两个部分:是否是一个配置类,是否是一个自动配置类。其实光从方法名上也就看出来了,下面的方法是其调用实现,里面有一个很关键的机制:SpringFactoriesLoader.loadFactoryNames
,我们留到下面解释。
小结
@SpringBootApplication
是组合注解。@ComponentScan
中的exclude
属性会将主启动类、自动配置类屏蔽掉。@Configuration
可标注配置类,@SpringBootConfiguration
并没有对其做实质性扩展。
@EnableAutoConfiguration
的作用篇幅较长,单独成篇。