Skip to main content

Spring配置上下文有哪些常用注解?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

先叠个甲,严格来说,spring-contexts 模块下的注解都是用来配置上下文的,这片文章主要的内容还是趋向于这里面跟配置文件沾边的注解,因此这个标题可能有一点不够准确,我们领会一下就行~

问题详解

1. @ComponentScan

@ComponentScan 的作用就跟字面上一样,用与扫描指定路径上直接或间接带有 @Component 注解的类,并将其纳入 Spring 管理,比如:

@ComponentScan({"com.xyz.service","com.xyz.controller"})
public class Application { }

它还有一个复数版本 @ComponentScans,用于同时声明多个 @ComponentScan

SpringBoot 的核心注解 @SpringBootApplication 中的 scanBasePackages 等扫描相关的属性就来自于它。

路径扫描可能会扫到一些不需要的类,因此它还支持通过内部注解 @Filter 灵活筛选所需要的类:

@ComponentScan(excludeFilters = { 
@Filter( // 排除带有 @Service 注解的类
type = FilterType.ANNOTATION,
classes = Service.class
),
@Filter( // 排序直接或间接实现了 BaseService 接口的类
type = FilterType.ASSIGNABLE_TYPE,
classes = BaseService.class
)
})
public class Application { }

FilterType 是一个内部的枚举类,它支持按类型、带有的注解、表达式或者自定义的过滤器进行筛选。

2. @Conditional

@Conditional 是一个条件装配注解,用来注解在带有 @Component的类上,或带有 @Bean 注解的工厂方法上,并指定一个 Condition 接口的实现类,表示仅当指定的 Condition 判断为满足某些条件时,才向 Spring 容器注册对应的 Bean。

比如 Spring 提供的 @Profile 注解,它表示仅当激活指定环境的配置文件时才注册 bean:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class) // 使用 ProfileCondition
public @interface Profile {
String[] value();
}

@Profile("test") // 仅当 spring.profile.active = test 时才注册 Foo
@Component
public class Foo { }

在 SpringBoot 中我们会看到很多 @Conditional 的扩展注解,比如常见的几个:

  • @ConditionalOnMissingBean:仅当不存在某个 Bean 时,才注册当前 Bean;
  • @ConditionalOnBean:仅当存在某个 Bean 时,才注册当前 Bean;
  • @ConditionalOnMissingClass:仅当不存在某个 Class 文件时,才注册当前 Bean;
  • @ConditionalOnClass:仅当存在某个 Class 文件时,才注册当前 Bean;
  • @ConditionalOnProperty:仅当某个配置文件值为特定值时,才注册当前 Bean。

具体可以参见 org.springframework.boot.autoconfigure.condition 包,它们是 SpringBoot 自动装配功能的基石。

3. @ConfigurationProperties

@ConfigurationProperties用来将配置文件参数绑定的到某个上对象上。

现在有如下配置文件:

example:
code: example-name
list:
- one
- two
- three
items:
- id: 1
name: abc
- id: 2
name: abc
- id: 3
name: abc

然后我们在对应的按缩进创建一个对应的配置对象,然后再在某个配置类上通过@EnableConfigurationProperties 启用配置对象:

@ConfigurationProperties(prefix = "example")
@Data
public class ConfigProperties {

private String code;
private List<String> list;
private List<Item> items;

@Data
private class Item {
private Interget id;
private String name;
}
}

@Configuration
@EnableConfigurationProperties(ConfigProperties.class)
public class Example {
@Autowired
private ConfigProperties properties;
public void run() {
System.out.printlen(
JSON.toJSONString(properties)
)
}
}

输出结果如下:

{
"code": "example-name",
"list": ["one", "two", "three"],
"items": [
{
"id": 1,
"name": "abc"
},
{
"id": 2,
"name": "abc"
},
{
"id": 3,
"name": "abc"
}
]
}

4. @PropertySource

@PropertySource 用于注解在某个配置类上,从而指定从某个路径加载配置文件:

@PropertySource("classpath: application.properties")
@Configuration
public class Exampl { }

它还有一个复数版本 @PropertySources

5. @Import

@Import注解用于向 Spring 容器引入某些组件。它的效果等同于在引入的类上加 @Component注解。

5.1. 封装为 @EnableXXX 注解

我们通常会使用 @Import 用来引入某个功能所需的配置类,并且将该配置固化为一个名为 EnableXXX 的组合注解。

比如 Spring 提供的@EnableAsync本质上是一个用于引入 AsyncConfigurationSelector@Import

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class) // 引入 AsyncConfigurationSelector
public @interface EnableAsync {
}

而 SpringBoot 的核心注解 @SpringBootApplication 中,则通过 @EnableAutoConfiguration 引入了自动装配相关的组件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 引入 AutoConfigurationPackages.Registrar
@Import(AutoConfigurationImportSelector.class) // 引入 AutoConfigurationImportSelector
public @interface EnableAutoConfiguration {
}

5.2. 结合回调接口使用

此外,由于我们经常会使用 @Import 引入一些配置类,因此 Spring 也针对通过 @Import 引入的配置类提供了一些有用的回调接口:

ImportSelector

实现了该接口的类可以在 Spring 启动时返回配置类的全限定名,从而动态的加载更多的配置类。

比如 @SpringBootApplication 注解就间接的引入了 AutoConfigurationImportSelector,我们所说的自动装配的配置类就是在这里扫描并注册的。

ImportBeanDefinitionRegistrar

实现了该接口的类,允许根据 @Import 所在类(通常的 SpringBoot 启动类或 Spring 中的配置类)的元数据,批量的注册 Bean。

比如 Mybatis 的 MapperScannerRegistrar,它通过 @MapperScan 引入,然后再反过来根据 @MapperScan 的配置扫描并注册 Mapper接口。