04、Spring Boot @Value和@Configurationproperties注解的区别
Spring Boot    2019-07-25 21:51:22    1232    0    0
acme   Spring Boot

    首先简单说一下两个注解之前的区别。


@Configurationproperties@Value
功能批量注入配置文件中的属性每个属性单独注入
松散语法支持不支持
SpEL不支持支持
JSR303数据校验支持不支持
复杂类型封装支持不支持

首先说一下功能上面,@Configurationproperties只需要在类上面标明,便可以针对该类的所有属性批量注入,而@Value却需要在每个属性上面单独注入。

其次,松散语法是指使用"-"或者"_"以及驼峰命名之间自动转变,@Value并不支持

SpEL语法和JSR303数据校验以及复杂类型封装就没有什么好说了的。

下面上代码,老样子,首先是pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cc.acme_me</groupId>
    <artifactId>spring-boot-annotation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-annotation</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

这次先把配置文件放在前面,使用properties来配置属性

server.port=8080
################User################
user.id=1
user.user-name=admin
user.email=springboot#acme-me.cc

这次少配置东西,少一点属性方便观察。

接着是Entity的User类

package cc.acme_me.springbootannotation.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.validation.constraints.Email;

/**
 * User entity
 *
 * @author acme
 * @Component 标识注入Spring容器中(只有注入Spring容器才能使用)
 */
@Component
public class User {

    @Value("${user.id}")//获取配置文件中的值
    private Integer id;

    @Value("${user.user-name}")//获取配置文件中的值
    //@Value("${user.userName}")//获取配置文件中的值  不支持
    private String userName;

    @Value("123")//字面量直接注入
    private String password;

    @Value("#{15*3}")//SpEL表达式
    private Integer privilegeLevel;

    @Email//在@Value下并不生效
    @Value("${user.email}")//获取邮件
    private String email;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", privilegeLevel=" + privilegeLevel +
                ", email='" + email + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getPrivilegeLevel() {
        return privilegeLevel;
    }

    public void setPrivilegeLevel(Integer privilegeLevel) {
        this.privilegeLevel = privilegeLevel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

按照上面的内容我们运行测试类

package cc.acme_me.springbootannotation;

import cc.acme_me.springbootannotation.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootAnnotationApplicationTests {

    @Autowired
    private User user;

    @Test
    public void contextLoads() {
        System.out.println(user);
    }

}

 

会发现测试通过了,可以看到这里emal的值并不是一个正确的邮箱地址,说明@Email校验在@Value下并未生效。

现在我们将User进行修改,使用@

package cc.acme_me.springbootannotation.entity;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Email;


/**
 * User entity
 *
 * @author acme
 * @ConfigurationProperties 注解用于标识注释参数 prefix标识对应的前缀
 * @Component 标识注入Spring容器中(只有注入Spring容器才能使用)
 */
@ConfigurationProperties(prefix = "user")
@Validated
@Component
public class User {

    private Integer id;

    private String userName;

    private String password;

    private Integer privilegeLevel;

    @Email
    private String email;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", privilegeLevel=" + privilegeLevel +
                ", email='" + email + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getPrivilegeLevel() {
        return privilegeLevel;
    }

    public void setPrivilegeLevel(Integer privilegeLevel) {
        this.privilegeLevel = privilegeLevel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

再次运行测试类,会发现控制台报错了。

报错的位置加载user注入的时候出现了问题

而且如果拉到最后面,还有更明显的提示Binding validation errors on user- Field error in object 'user' on field 'email': rejected value [springboot#acme-me.cc];

这里已经说了是email这个属性校验失败,拉到最后其实还有很好的中文提示:

这已经告诉了说不是一个合法的邮件地址,说明校验在@ConfigurationProperties下是有效的。

我们更改application.properties文件中的user.email属性

application.properties

其它属性不变,再次运行测试类

会发现绿条一次通过,至于password和privilegeLevel为null,是因为我们配置文件中没有这两个文件,所以这两个文件现在为空。

最后,我们发现两个都能用,那么不得折腾一下吗?如果两个注解都配置了,可以一起用吗?

如果其中某个属性在配置文件中有值,而在属性上使用@Value的字面量去设置值,那么这个属性的值会是配置文件中的还是@Value注入进来的呢?

这里更改一下application.properties文件

server.port=8080
################User################
user.id=1
user.user-name=admin
[email protected]
user.password=123

在更改一下User类

package cc.acme_me.springbootannotation.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Email;


/**
 * User entity
 *
 * @author acme
 * @ConfigurationProperties 注解用于标识注释参数 prefix标识对应的前缀
 * @Component 标识注入Spring容器中(只有注入Spring容器才能使用)
 */
@ConfigurationProperties(prefix = "user")
@Validated
@Component
public class User {

    private Integer id;

    private String userName;

    @Value("321")//properties文件也配置了这个属性,运行测试类这里会获取哪个值呢?
    private String password;

    @Value("#{3*3}")
    private Integer privilegeLevel;

    @Email
    private String email;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", privilegeLevel=" + privilegeLevel +
                ", email='" + email + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getPrivilegeLevel() {
        return privilegeLevel;
    }

    public void setPrivilegeLevel(Integer privilegeLevel) {
        this.privilegeLevel = privilegeLevel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

上测试结果!

最后的最后,在这里说一下使用原则:在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;如果专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;

Pre: 05、Spring Boot,拆分配置文件以及配置类注解

Next: 03、Spring Boot配置文件-properties

1232
Table of content