Spring Boot之所以受开发者欢迎, 其中最重要的一个因素就是其配置简单。传统的Spring应用需要手动配置各种.xml文件,为数据库访问,事务支持,缓存功能等提供各项繁杂且重复的配置。Spring Boot将这种繁杂且重复的工作通过预定义的启动器(starter)来实现,只要引入即可拥有相应的功能支持,从而将开发者从复杂的配置工作中解放出来,能够更专注于业务逻辑的开发。

配置方式

在Spring Boot中,虽然仍然可以通过之前的.xml文件方式来进行配置,但最好还是通过基于java的配置来进行配置管理。在Spring Boot中,基于java的配置是通过注解@Configuration来实现的

1
2
3
4
5
6
7
8
@Configuration
public class MyConfig {

@Bean
public MyService myService(){
return new MyService();
}
}

上述代码将一个MyService的Bean注入了容器,这样在其它地方就可以直接通过@Autowired来引用访问。与.xml文件中通过<bean></bean>实例化的效果是一样的。

1
2
3
4
5
6
7
@Autowired
private MyService myService;

@RequestMapping("/hi")
public String sayHello(@RequestParam String name){
return myService.sayHello(name);
}

实际项目开发中,有可能存在一些基于xml配置的旧服务,比如以jar包的形式发布,如果要复用该怎么引入呢?很简单,在@Configuration注解标注的类上,加入@ImportResource注解引用相应的xml文件即可,

1
2
3
4
5
6
7
8
9
@Configuration
@ImportResource("spring.xml")
public class MyConfig {

@Bean
public MyService myService(){
return new MyService();
}
}

这样类路径下spring.xml配置文件中声明的内容都将生效。在一个应用中,可以定义多个@Configuration配置类,这些配置类可以被@ComponentScan自动扫描并注入容器。

如果应用中没有通过@ComponentScan进行自动扫描,则可在主配置类(一般为入口类)上通过@Import({MyConfig.class})的方式类引入其它配置类

自动配置

个人认为,自动配置是Spring Boot非常基础但又核心的部分。曾经遇到几个面试者,简历写着精通Spring Boot,当问及自动配置时却支支吾吾不知所云。其实理解Spring Boot的自动配置也不难,基本了解如下几部分差不多就够了:

  1. @EnableAutoConfiguration注解
  2. SpringApplication类
  3. spring-boot-autoconfigure jar包
  4. spring.factories文件

@EnableAutoConfiguration注解
这个注解的作用是告诉Spring Boot基于添加的jar依赖来自动配置Spring,比如添加了spring-boot-starter-web依赖,则Spring Boot认为你在开发一个web应用,就会自动做好web相应配置。这个注解一般放在主类上。在前面的示例项目中, 我们在主类上都是使用@SpringBootApplication, 查看源码可以知道:

  • @SpringBootApplication 这个注解实际上等效于 @SpringBootConfiguration(等效于@Configuration),
  • @EnableAutoConfiguration,启用自动配置
  • @ComponentScan 自动扫描@Component, @Service, @Controller等注解标注的各类组件

三者的组合。如果去掉@EnableAutoConfiguration注解,则Spring Boot将不会自动配置Spring(如实例化必要的Bean),将可能导致应用启动失败。

SpringApplication类
在应用主类中,我们是通过SpringApplication的run方法来启动应用的,如:

1
2
3
4
5
6
7
@SpringBootApplication
public class SpringbootConfigApplication {

public static void main(String[] args) {
SpringApplication.run(SpringbootConfigApplication.class, args);
}
}

查看源码,SpringApplication的静态run方法,实际也是通过创建SpringApplication实例,调用实例方法执行,在SpringApplication构造器方法中,调用了getSpringFactoriesInstances 方法,

1
2
3
4
5
6
7
8
9
10
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources{
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

追溯下去,最终会调用到SpringFactoriesLoader的loadSpringFactories方法,

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
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
...

try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

在该方法中,会从所有的META-INF目录下加载spring.factories文件里配置的各类型的类名称(包括初始化器,监听器,自动配置类等)。然后上层方法中通过反射机制实例化这些初始化器、监听器,自动配置等,从而完成相应Bean的自动化配置与注入。

spring-boot-autoconfigure

官方提供的starter,如spring-boot-starter-web, 都依赖了spring-boot-starter, 而spring-boot-starter又依赖了spring-boot-autoconfigure。 在spring-boot-autoconfigure中提供了大量官方提供的自动配置类,并且包含META-INF/spring.factories文件,如下图

spring-boot-autoconfigure

spring.factories

由上图可看出,spring.factories包含了

  • org.springframework.context.ApplicationContextInitializer 应用初始化器
  • org.springframework.context.ApplicationListener 应用监听器
  • org.springframework.boot.autoconfigure.AutoConfigurationImportListener 自动配置引入监听器
  • org.springframework.boot.autoconfigure.AutoConfigurationImportFilter 自动配置引入过滤器
  • org.springframework.boot.autoconfigure.EnableAutoConfiguration 自动配置类
  • org.springframework.boot.diagnostics.FailureAnalyzer 失败分析器
  • org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider 模板提供者

其中org.springframework.boot.autoconfigure.EnableAutoConfiguration即实现自动配置的@Configuration配置类列表。

Spring Boot就是通过这种自动配置机制,以starter依赖包的方式,使开发者非常方便地使用项目开发中的许多常用功能,如数据库访问、缓存、队列等。同时,用户也可以根据自身需求,自定义自己的starter(后面介绍)。

通过注解控制自动配置

Spring Boot自动配置包含了许多条件类注解及顺序类注解,这些注解可方便地让自动配置按照某种条件或者顺序进行配置。

其中条件类注解包括:

  • 类级别条件注解 @ConditionalOnClass: 类路径中存在指定的类才进行该配置;@ConditionalOnMissingClass: 类路径中不存在指定的类才进行该配置
  • 实例级别条件注解 @ConditionalOnBean:只有在当前上下文中存在指定Bean时,才进行该配置
    @ConditionalOnMissingBean: 只有在当前上下文不存在指定Bean时,才进行该配置
  • 属性级别条件注解 @ConditionalOnProperty:当存在某个指定属性,且值为指定值时,才进行该配置
  • 资源级别条件注解 @ConditionalOnResource:在类路径下存在指定的Resource时,才进行配置
  • Web应用条件注解 @ConditionalOnWebApplication:该应用为Web应用时进行该配置
    @ConditionalOnNotWebApplication: 该应用不为Web应用时进行该配置
  • SpEL( Spring Expression Language)表达式注解 @ConditionalOnExpression: 计算SpEL表达式值,值为true时才进行该配置

顺序类注解包括:

  • @AutoConfigureAfter: 在指定的配置类初始化后再加载
  • @AutoConfigureBefore: 在指定的配置类初始化前加载
  • @AutoConfigureOrder: 数值越小越先初始化

注意:自动配置类不应该位于组件扫描路径(@ComponentScan注解指定的扫描路径)下,否则上述条件注解与顺序注解可能不会生效。建议只在自动配置的类上注解@ConditionalOnBean, @ConditionalOnMissingBean,因为这可以保证在用户定义bean已经添加到ApplicationContext之后才会加载。这两个注解放在class上,则相当于class里面每一个@Bean标注的方法都加上了。

自动配置是非侵入式的,你可以在任何地方自定义配置来覆盖自动配置中的某些内容,比如你在应用中通过@Configuration类注入一个自定义的DataSource,默认的基于内存的DataSource将被覆盖

禁用某个自动配置类

有时候引入的自动配置可能包含我们不想让其生效的配置类,这时候可以通过@EnableAutoConfiguration注解的属性进行排除,使其不生效。

1
@EnableAutoConfiguration(exclude = {XXAutoConfiguration.class})

其中XXAutoConfiguration为某个自动配置类,如果该类不在应用的类路径中,则可以通过属性excludeName指定完整类路径来排除。@SpringBootApplicationz注解同样支持

1
@SpringBootApplication(exclude = {XXAutoConfiguration.class})

本文示例项目源码地址:https://github.com/ronwxy/springboot-demos/tree/master/springboot-config




我的个人博客地址:http://blog.jboost.cn
我的头条空间: https://www.toutiao.com/c/user/5833678517/#mid=1636101215791112
我的github地址:https://github.com/ronwxy
我的微信公众号:jboost-ksxy

——————————————————————————————————————————————————

微信公众号
欢迎关注我的微信公众号,及时获取最新分享