Spring配置上下文有哪些常用注解?
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个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
接口。