SpringBoot常用注解


话不多说,过一下配置文件

yml配置文件主要写法

参数写法

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
mystarter.config:
# 布尔类型
enable: true

# 数字或基本类型
# size: ${random.int} 随机数
size: 18

# 字符串
# address: "beijing \n aaa" 加双引号不会转义
# address: 'beijing \n aaa' 加单引号会转义
address: beijing
# 时间date
date: 2021/01/01

# 自定义bean map
# user: {username: root, password: 123456} 行内写法
user:
# username: ${mystarter.config.address} 引用其它配置,不存在则为 "${mystarter.config.address}"字符串
# username: ${mystarter.config.address: testuser} 引用其它配置,不存在则使用缺省值
username: root
password: 123456
map:
ip: 10.0.0.1
loginName: root

# 数组 list set
# array: [aaa,bbb,ccc] 行内写法
array:
- aaa
- bbb
- ccc
list:
- ddd
- eee
set:
- fff
- ggg

激活配置文件或配置块

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
spring:
application:
name: demo-test
profiles:
# 配置文件激活
# 1、创建配置文件时名称为 application-{name}.yml 可使用 spring.profiles.active={name}去激活,找不到则使用默认主配置
# 激活后配置内容 = 配置文件交集 + 激活文件非交集部分
# 2、也可激活块部分,yml文档块 spring.profiles.active=dev1
# 3、使用命令行激活 --spring.profiles.active=dev1
# 4、使用jvm参数 -Dspring.profiles.active=dev1
active: dev1
server:
port: 8080
--- # 新的块
server:
port: 8081
spring:
profiles: dev1
--- # 新的块
server:
port: 8082
spring:
profiles: dev2
# spring boot启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
# file:./config/
# file:./
# classpath:/config(- classpath:/
# 以上是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级配置内容会覆盖低优先级配置内容。
# 我们也可以通过配置spring.config.location来改变默认配置

另外,properties文件,特殊#—注释用于标记文档拆分

1
2
3
4
spring.application.name=MyApp
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.application.name=MyCloudApp

外部配置加载顺序

SpringBoot也可以从以下位置加载配置﹔优先级从高到低﹔高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置,以下列举实用的外部配置,官网文档

  1. 命令行参数,多个参数用空格分开

    1
    java -jar spring-boot-xxx-0.0.1-SNAPSHOT.jar --server.port=9090
  2. 来自java:comp/env的JNDI属性

  3. Java系统属性( System.getProperties() )

  4. 操作系统环境变量

  5. RandomValuePropertySource配置的random.*属性值

  6. jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

  7. jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

  8. jar包外部的application.properties或application.yml(不带spring.profile)配置文件

  9. jar包内部的application.properties或application.yml(不带spring.profile)配置文件

  10. @Configuration注解类上的@PropertySource

  11. 通过SpringApplication.setDefaultProperties指定的默认属性

优先级从 1 > 2 > … > 11,jar包外 > jar包内,带{profile} > 不带{profile} ;

@ConfigurationProperties

从配置文件给bean配置属性

基本用法

搭配组件型注解,如@Component,指定前缀,以属性名进行赋值,依赖于空构造函数;

  1. ignoreInvalidFields

    当配置文件中的值无法映射给bean的属性。如字符串给数字赋值等,SpringBoot应用会抛异常而起动失败,而ignoreInvalidFields属性设置为true(默认为false)时可以使应用不会停止,即使用属性默认的值。

  2. ignoreUnknownFields

    忽略位置属性;当配置文件中出现了类中没有定义的属性时,即出现了未知的属性,SpringBoot默认时忽略它的(ignoreUnknownFields默认为true),ignoreUnknownFields设为false时,表示要对未知属性“斤斤计较”,结果自然是启动失败。

1
2
3
4
5
6
7
8
9
10
@ConfigurationProperties(prefix = "ordinary.bind", ignoreInvalidFields = true, ignoreUnknownFields = false)
@Slf4j
@NoArgsConstructor
@Data
@Component
public class OrdinaryPropertiesBindExample {

private String name;
private Boolean enable = true;
}

使用 Spring Boot Configuration Processor自动补全

pom.xml中加入Spring Boot Configuration Processor的依赖,并重新build之后,IDEA中编写配置文件时就可以自动补全了

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

自动补全

标记配置属性为 Deprecated

使用@DeprecatedConfigurationProperty注解作用与属性的get方法上,表示该属性已经过时,重新build之后,属性对应的自动补全提示也会发生改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ConfigurationProperties(prefix = "ordinary.bind", ignoreInvalidFields = true, ignoreUnknownFields = false)
@Slf4j
@NoArgsConstructor
@Data
@Component
public class OrdinaryPropertiesBindExample {

private String name;
private Boolean enable = true;

@DeprecatedConfigurationProperty(reason = "我已经过时", replacement = "none")
public Boolean getEnable() {
return this.enable;
}
}

自动补全-过时

搭配@ConstructorBinding

@ConstructorBinding注解,顾名思义,构造绑定,通过构造方法,从配置文件获取值给bean设置属性。使用此方式时,需要通过@EnableConfigurationProperties注解修饰的配置类来扫描该类加入IOC,不能对通过常规Spring机制创建的bean使用构造函数绑定,即不能添加组件型注解(如@Component)这样的注解加入IOC,原因也很简单,这里不作解释。

搭配@ConstructorBinding的配置方式,一般在给类内为 final的属性初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ConstructorBinding
@ConfigurationProperties("constructor.bind")
@Slf4j
@Data
public class ConstructorPropertiesBindExample {

private final String name;// 一般用于运行时不可变的配置

public ConstructorPropertiesBindExample(String name) {

log.info("构造方法被调用");
this.name = name;
}
}
1
2
3
4
5
@EnableConfigurationProperties(ConstructorPropertiesBindExample.class)// 激活
@Component
public class ConstructorPropertiesBindExampleActive {

}

搭配@Bean

搭配@Bean注解时,不依赖该bean的无参构造,仅在bean实例化后进行属性设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
@Configuration
public class ExampleConfiguration {

@ConfigurationProperties("atbean.bind")
@Bean
public ExampleBean exampleBean() {

return new ExampleBean();
}
// 定义一个pojo
@NoArgsConstructor
@AllArgsConstructor
@Data
public static class ExampleBean {

private String examName;
}
}

@ConfigurationPropertiesBinding与自定义转换器

实现org.springframework.core.convert.converter.Converter接口实现自定义转换器,使用@ConfigurationPropertiesBinding声名为Spring可用的转换器

设置性别类。不设置枚举是因为Spring默认支持枚举的字符串配置了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Sex {

public static final Sex MAN = new Sex("man");
public static final Sex WOMAN = new Sex("woman");

private String value;

private Sex(String value) {

this.value = value;
}

public String getValue() {
return value;
}
}

实现性别类的转换器,接收不符合规则的String抛出IllegalArgumentException异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@ConfigurationPropertiesBinding
@Component
public class SexConverter implements Converter<String,Sex> {

private static final Map<String,Sex> SEX_VALUE_SET =
Arrays.stream(new Sex[]{Sex.MAN,Sex.WOMAN}).collect(Collectors.toMap(Sex::getValue,sex -> sex));

@Override
public Sex convert(String source) {

if (SEX_VALUE_SET.containsKey(source)) {

return SEX_VALUE_SET.get(source);
}else {

throw new IllegalArgumentException("The sex string value must in " + SEX_VALUE_SET.keySet().toString());
}

}
}

建立一个pojo

1
2
3
4
5
6
7
8
9
@ConfigurationProperties(prefix = "sexbean")
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ExampleSexBean {

private Sex sex;
}

yml文件如下配置即可达到目的,配置的值不是 man或woman则抛出异常

1
2
3
# 自定义转换器
sexbean:
sex: man

一般来说像上面这种情况使用枚举即可,这里为了举例不使用枚举。

条件注入

仅列举常用的条件注入(有必要的话,可以取去了解SpringBootCondition

@ConditionalOnProperty

根据【配置文件是否存在对应的键值对】作为注入条件

以下配置可理解为:配置文件存在 “ex.config.flag=true”时注入bean,若不存在该key值则默认为false(不注入)

1
2
3
4
5
@ConditionalOnProperty(prefix = "ex.config", name = "flag", havingValue = "1", matchIfMissing = false)
@Component
public class ExampleConfiguration {

}

@ConditionalOnBean

根据【容器内是否存在符合条件的bean】作为注入条件

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
public @interface ConditionalOnBean {
/**
* 需要作为条件的类的Class对象数组
*/
Class<?>[] value() default {};
/**
* 需要作为条件的类的Name,Class.getName()
*/
String[] type() default {};
/**
* (用指定注解修饰的bean)条件所需的注解类
*/
Class<? extends Annotation>[] annotation() default {};
/**
* spring容器中bean的名字
*/
String[] name() default {};
/**
* 搜索容器层级,当前容器,父容器,全部
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
* 可能在其泛型参数中包含指定bean类型的其他类
*/
Class<?>[] parameterizedContainer() default {};
}
1
2
3
4
5
@ConditionalOnBean(value = SpringbootAnnotationApplication.class)
//@ConditionalOnBean(name = "springbootAnnotationApplication")
@Component
public class ExampleConditionalOnBean {
}

@ConditionalOnClass

根据【工程中引入是否存在该class】作为注入条件

1
2
3
4
5
@ConditionalOnClass(SpringbootAnnotationApplication.class)
//@ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication") // 类全限定名
@Component
public class ExampleConditionalOnClass {
}

@ConditionalOnJava

根据【当前使用的java版本与配置的版本对比结果】作为注入条件

1
2
3
4
5
6
@ConditionalOnJava(
value = JavaVersion.EIGHT, // java 1.8
range = ConditionalOnJava.Range.EQUAL_OR_NEWER)// 大于或等于 ConditionalOnJava.Range.OLDER_THAN 为小于
@Component
public class ExampleConditionalOnJava {
}

@ConditionalOnExpression

根据【spel表达式结果】作为注入条件,注解内值不符合表达式规则则抛出异常

1
2
3
4
@ConditionalOnExpression("1 + 1 == 2")
@Component
public class ExampleConditionalOnExpression {
}

@ConditionalOnMissingBean

根据【容器内是否不存在符合条件的bean】作为注入条件

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
 @Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
/**
* 需要检查的 bean 的 class 类型。当 ApplicationContext 不包含每一个被指定的 class 时条件匹配。
*/
Class<?>[] value() default {};
/**
* 需要检查的 bean 的 class 类型名称(Java全限定名)。当 ApplicationContext 不包含每一个被指定的 class 时条件匹配。
*/
String[] type() default {};
/**
* 识别匹配 bean 时,可以被忽略的 bean 的 class 类型
*/
Class<?>[] ignored() default {};
/**
* 识别匹配 bean 时,可以被忽略的 bean 的 class 类型名称(Java全限定名)
*/
String[] ignoredType() default {};
/**
* 装饰需要检查的 bean 的注解。当 ApplicationContext 不包含带有这些注解的 bean 时条件匹配。
*/
Class<? extends Annotation>[] annotation() default {};
/**
* 需要检查的 bean 的 name。当 ApplicationContext 不包含任意指定的每一个的 class 时条件匹配。
*/
String[] name() default {};
/**
* 搜索容器层级,当前容器,父容器,全部
*/
SearchStrategy search() default SearchStrategy.ALL;
}
1
2
3
4
@ConditionalOnMissingBean(name = "springbootAnnotationApplication999")
@Component
public class ExampleConditionalOnMissingBean {
}

@ConditionalOnMissingClass

根据【容器内是否不存在该class的bean】作为注入条件

1
2
3
4
@ConditionalOnMissingClass("ws.springboot.springbootannotation.SpringbootAnnotationApplication999") // 类全限定名
@Component
public class ExampleConditionalOnMissingClass {
}

@ConditionalOnMissingFilterBean

根据【容器内是否不存在该过滤器】作为注入条件

1
2
3
4
@ConditionalOnMissingFilterBean(CorsFilter.class)
@Component
public class ExampleConditionalOnMissingFilterBean {
}

@ConditionalOnCloudPlatform

根据【应用所处云平台】作为注入条件

1
2
3
4
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
@Component
public class ExampleConditionalOnCloudPlatform {
}
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
public enum CloudPlatform {

/**
* 无(无平台,返回false,bean不会被注入)
*/
NONE,

/**
* 云计算平台
*/
CLOUD_FOUNDRY,

/**
* Heroku平台
*/
HEROKU,

/**
* SAP 云平台.
*/
SAP,

/**
* K8S.
*/
KUBERNETES
}

@ConditionalOnWebApplication

根据【应用是否所处于web环境】作为注入条件

1
2
3
4
5
6
7
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.ANY) // 任何web环境
//@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // 基于servlet的web应用
//@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) // 基于响应式的web应用(猜测可能是webflux)
@Component
public class ExampleConditionalOnWebApplication {
}

@ConditionalOnNotWebApplication

根据【应用是否所不处于web环境】作为注入条件

1
2
3
4
@ConditionalOnNotWebApplication
@Component
public class ExampleConditionalOnNotWebApplication {
}

@ConditionalOnResource

根据【资源是否存在】作为注入条件,如日志的相关配置等

1
2
3
4
@ConditionalOnResource(resources = "classpath:application.yml")
@Component
public class ExampleConditionalOnResource {
}

@ConditionalOnSingleCandidate

根据【容器内是否存在该class的bean,且只有一个实例或为首选的】作为注入条件,即容器中存在该class的bean的实例,且只有一个时注入条件为true,当存在多个实例时,有一个实例为首选的(如加上了@Primary注解),注入条件也为true,即在其它bean中注入该class的bean时,如使用@Autowired且不指定bean名称时,不会发生依赖注入失败,这个时候,以这个calss为value的@ConditionalOnResource注解条件匹配结果就是true。

1
2
3
4
5
6
7
8
9
10
@ConditionalOnSingleCandidate(value = I.class)
@Component
public class ExampleConditionalOnSingleCandidate { // 不可注入
}

interface I{}
@Component
class A implements I{}
@Component
class B implements I{}
1
2
3
4
5
6
7
8
9
10
11
@ConditionalOnSingleCandidate(value = I.class)
@Component
public class ExampleConditionalOnSingleCandidate { // 可注入
}

interface I{}
@Primary
@Component
class A implements I{}
@Component
class B implements I{}

条件注入的组合

组合条件 AND

  1. 在类上使用多个@ConditionalOnXxxx

    1
    2
    3
    4
    5
    @ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication")
    @ConditionalOnJava(value = JavaVersion.EIGHT, range = ConditionalOnJava.Range.EQUAL_OR_NEWER)
    @Component
    public class ExampleConditionalAnd1 {
    }
  2. 自定义注解上使用其它条件注入注解

    1
    2
    3
    4
    5
    6
    7
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication")
    @ConditionalOnJava(value = JavaVersion.EIGHT, range = ConditionalOnJava.Range.EQUAL_OR_NEWER)
    public @interface MyConditionalAndAnno {
    }

    应用到要进行条件注入的组件上

    1
    2
    3
    4
    @MyConditionalAndAnno
    @Component
    public class ExampleConditionalAnd3 {
    }
  3. 继承AllNestedConditions类封装条件,使用静态内部类标注条件,所有条件将进行【逻辑与】操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    * and条件注入
    * @author WindShadow
    * @date 2021/2/3
    * @since
    */
    public class MyConditionalAnd extends AllNestedConditions {

    public MyConditionalAnd() {
    super(ConfigurationPhase.PARSE_CONFIGURATION);
    }

    @ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication")
    static class OnClass{}
    @ConditionalOnJava(value = JavaVersion.EIGHT, range = ConditionalOnJava.Range.EQUAL_OR_NEWER)
    static class OnJava{}
    }
    1
    2
    3
    4
    5
    @Conditional(MyConditionalAnd.class)
    @Component
    public class ExampleConditionalAnd2 {

    }

    其中ConfigurationPhase枚举的两个值意义如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum ConfigurationPhase {

            /**
             * 配置类解析阶段,如果条件为false,配置类将不会被解析
             */
            PARSE_CONFIGURATION,
            /**
             * bean注册阶段,如果为false,bean将不会被注册
             */
    REGISTER_BEAN
        }

组合条件 OR

继承AnyNestedCondition类封装条件,使用静态内部类标注条件,所有条件将进行【逻辑或】操作

1
2
3
4
5
6
7
8
9
10
public class MyConditionalOr extends AnyNestedCondition {
public MyConditionalOr() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication")
static class OnClass{}
@ConditionalOnJava(value = JavaVersion.NINE, range = ConditionalOnJava.Range.EQUAL_OR_NEWER)
static class OnJava{}
}
1
2
3
4
@Conditional(MyConditionalOr.class)
@Component
public class ExampleConditionalOr {
}

组合条件 NOT

继承NoneNestedConditions类封装条件,使用静态内部类标注条件,所有条件将进行【逻辑非】操作

1
2
3
4
5
6
7
8
9
10
public class MyConditionalNot extends NoneNestedConditions {

public MyConditionalNot() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnClass(name = "ws.springboot.springbootannotation.SpringbootAnnotationApplication999")
static class OnClass{}
@ConditionalOnJava(value = JavaVersion.NINE, range = ConditionalOnJava.Range.EQUAL_OR_NEWER)
static class OnJava{}
}
1
2
3
4
@Conditional(MyConditionalNot.class)
@Component
public class ExampleConditionalNot {
}

Servlet组件的扫描

ServletComponentScan

@SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet@WebFilter@WebListener注解自动注册,无需其他代码。

1
2
3
4
5
6
7
8
9
@ServletComponentScan
@SpringBootApplication
public class DemoApplication {

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