Springboot 注解

@validated

服务器和浏览器互不信任,不能因为前端加入参判断了后台就不处理了,这样是不对的。

比如前台传过来一个对象作为入参参数,这个对象中有些属性允许为空,有些属性不允许为空。那么你还在使用if()else{}进行非空判断吗?不妨尝试下使用注解,可以使用@Validated

基础使用

因为spring-boot已经引入了基础包,所以直接使用就可以了
1 首先在controller上声明需要对数据进行校验

@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
 
    @PostMapping("/insert")
    public String validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
        System.out.println(use1Dto);
        return "success";
    }
}

2 然后在bean上声明需要被校验的字段

@Data
public class User1Dto {
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空!")
    private String username;
    /**
     * 性别
     */
    @NotBlank(message = "性别不能为空!")
    private String gender;
    /**
     * 年龄
     */
    @Min(value = 1, message = "年龄有误!")
    @Max(value = 120, message = "年龄有误!")
    private int age;
    /**
     * 地址
     */
    @NotBlank(message = "地址不能为空!")
    private String address;
    /**
     * 邮箱
     */
    @Email(message = "邮箱有误!")
    private String email;
    /**
     * 手机号码
     */
    @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!")
    private String mobile;
}

而后,当输入不能满足条件是,就会抛出异常,而后统一由异常中心处理
也可以用BindingResult,但是用了这个后就必须手动处理异常,侵入了正常的逻辑过程,并不推荐.

说明:若不做异常处理,@Validated注解的默认异常消息如下(示例):

2020-09-05 21:48:38.106  WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto): [Field error in object 'useDto' on field 'username': rejected value [null]; codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments []; default message [username]]; default message [用户名不能为空!]] ]

常用注解类型

注意,不要错用了异常类型,比如在int上不可用@size

数值检查,建议使用在StirngInteger类型,不建议使用在int类型上,因为表单值为""时无法转换为int,但可以转换为Stirng"",Integernull

嵌套校验

@Validated或者@Valid区别

  1. @Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
  2. @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
  3. 两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。

未嵌套情况

比如我们现在有个实体叫做Item

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "至少要有一个属性")
    private List<Prop> props;
}

Item带有很多属性,属性里面有:pidvidpidNamevidName,如下所示:

public class Prop {

    @NotNull(message = "pid不能为空")
    @Min(value = 1, message = "pid必须为正整数")
    private Long pid;

    @NotNull(message = "vid不能为空")
    @Min(value = 1, message = "vid必须为正整数")
    private Long vid;

    @NotBlank(message = "pidName不能为空")
    private String pidName;

    @NotBlank(message = "vidName不能为空")
    private String vidName;
}

属性这个实体也有自己的验证机制,比如pidvid不能为空,pidNamevidName不能为空等。
现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示:

@RestController
public class ItemController {

    @RequestMapping("/item/add")
    public void addItem(@Validated Item item) {
        doSomething();
    }
}

在上图中,如果Item实体的props属性不额外加注释,只有@NotNull@Size,无论入参采用@Validated还是@Valid验证,Spring Validation只会对Itemidprops做非空和数量验证,不会对props字段里的Prop实体进行字段验证,也就是@Validated@Valid加在方法参数前,都不会自动对参数进行嵌套验证。也就是说如果传的List中有Proppid为空或者是负数,入参验证不会检测出来。

嵌套情况

为了能够进行嵌套验证,必须手动在Item实体的props字段上明确指出这个字段里面的实体也要进行验证。由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且@Valid类注解上也说明了它支持嵌套验证功能,那么我们能够推断出:@Valid加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated@Valid来进行嵌套验证。

修改Item类如下所示:

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private List<Prop> props;
}

然后我们在ItemControlleraddItem函数上再使用@Validated或者@Valid,就能对Item的入参进行嵌套验证。此时Item里面的props如果含有Prop的相应字段为空的情况,Spring Validation框架就会检测出来,记录相应的错误。

@Validated和@Valid在嵌套验证功能上的区别

@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

BindingResult

    @PostMapping("/a")
    @ApiOperation(value = "测试", notes = "")
    public void test(@RequestBody @Valid TestEntity test,BindingResult bindingResult) {
        System.out.println(test.toString());
        if (bindingResult.hasErrors()) {
            throw new 自定义Exception("错误提示码",bindingResult.getFieldError().getDefaultMessage());
        }
    }

bindingResult.hasErrors() 判断是否校验通过,未通过bindingResult.getFieldError().getDefaultMessage()获取在TestEntity的属性设置的自定义message,如果没有设置,则返回默认值javax.validation.constraints.XXX.message

    @RequestMapping("/b")
    @ResponseBody
    public Response runBydomain(@RequestBody @Validated TaskDomainVO taskDomainVO, BindingResult bindingResult) {
        log.info("Execution code analyze,", taskDomainVO.toString());
        if (bindingResult.hasErrors()) {
            String errorCode = bindingResult.getAllErrors().get(0).getDefaultMessage();
            log.info("Execution failed, errorCode:{}", errorCode);
            return new Response(ResultCode.GLOBAL_PARAM_ERROR, errorCode);
        }

@Json***

jacksonmavern 依赖

<dependency> 
    <groupId>com.fasterxml.jackson.core</groupId> 
        <artifactId>jackson-databind</artifactId> 
    <version>2.5.3</version>
</dependency>

使用的背景
springboot项目中定义了很多类,我们在rest返回中直接返回或者在返回对象中使用这些类,spring已经使用jackson自动帮我们完成这些的to json。但是有时候自动转的json内容太多,或者格式不符合我们的期望,因此需要调整类的to json过程,或者说希望自定义类的json过程

@JsonProperty

概念

  • 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name@JsonProperty(“name”)
  • 对属性名称重命名,比如在很多场景下Java对象的属性是按照规范的驼峰书写,但在数据库设计时使用的是下划线连接方式,此处在进行映射的时候就可以使用该注解。

用法

例如:使用该注解将以下表结构转化为 Javabean

public class CustomerInfo{
    
    private int id;

    //使用 @JsonProperty注解将表结构中的字段映射到实体类中
    @JsonProperty("customer_name")
    private String customerName;
    
    @JsonProperty("customer_id")
    private String customerId;
    
    @JsonProperty("product_id")
    private String productId;
    
    @JsonProperty("source_address")
    private String sourceAddress;

    public int getId() {
        return id;
    }

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

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getSourceAddress() {
        return sourceAddress;
    }

    public void setSourceAddress(String sourceAddress) {
        this.sourceAddress = sourceAddress;
    }
}

@JsonIgnore

概念

  • 用于属性或者方法上(最好是属性上),用来完全忽略被注解的字段和方法对应的属性,即便这个字段或方法可以被自动检测到或者还有其他的注解,一般标记在属性或者方法上,返回的json数据即不包含该属性。

用法

使用情景:需要把一个List转换成json格式的数据传递给前台。但实体类中基本属性字段的值都存储在快照属性字段中。此时我可以在业务层中做处理,把快照属性字段的值赋给实体类中对应的基本属性字段。最后,我希望返回的json数据中不包含这两个快照字段,那么在实体类中快照属性上加注解@JsonIgnore,那么最后返回的json数据,将不会包含customerIdproductId两个属性值。

public class CustomerInfo {
    
    private int id;
    
    //使用 @JsonIgnore注解在生成json数据时,忽略该字段
    
    private String customerName;
    
    @JsonIgnore
    private String customerId;
    
    @JsonIgnore
    private String productId;
    
    private String sourceAddress;

    public int getId() {
        return id;
    }

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

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getSourceAddress() {
        return sourceAddress;
    }

    public void setSourceAddress(String sourceAddress) {
        this.sourceAddress = sourceAddress;
    }
}

@JsonIgnoreProperties

概念

是类注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。

用法:

@JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在类上之后,就会忽略类中不存在的字段。这个注解还可以指定要忽略的字段,例如 @JsonIgnoreProperties(value = {"fullName", "comment"})

@Data
@JsonIgnoreProperties(value = {"fullName", "comment"})
public class User {
    private String id;
    private String name;
    private String fullName;
    private String comment;
    private String mail;

    @JsonIgnore
    private String address;

    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date regDate;

    private Date reg2Date;
}

说明:User类的fullNamecomment字段会被@JsonIgnoreProperties注解忽略。address字段会被@JsonIgnore注解忽略。regDate会按照@JsonFormat(timezone = “GMT+8”, pattern = “yyyy-MM-dd HH:mm:ss”)进行格式转。

    @ApiOperation(value = "按用户id删除", notes="private")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "userId", defaultValue = "2", value = "userID", required = true, dataType = "string", paramType = "path"),
    })
    @DeleteMapping(value = "/users/{userId}", produces = "application/json;charset=UTF-8")
    public User delUser(@PathVariable String userId) {
        User user = (User)userSvc.deleteById(userId);
        log.info("rest del user={} by id={}", user, userId);
        return user;
    }

可以看到返回的对象是User,然后commentfullNameaddress属性被忽略了,regDate的格式进行转换。

@JsonFormat

概念

用于属性或者方法上(最好是属性上),可以方便的把Date类型直接转化为我们想要的模式。

用法

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date updateTime;

读取配置文件

  • @ConfigurationProperties : 是springboot的注解,用于把主配置文件(application.properties、application.yml)中配置属性设置到对于的Bean属性上
  • @PropertySource :是spring的注解,用于加载指定的属性配置文件到Spring的Environment中,可以和 @Value@ConfigurationProperties配合使用
  • @EnableConfigurationProperties : 用来开启ConfigurationProperties注解配置;如果不使用的话,@ConfigurationProperties加入注解的类上加@Component也是可以交于springboot管理。

读取默认配置文件(application.properties、application.yml)

application.yml配置:

person:
	name: wumingshi

实现方式一 @ConfigurationProperties + @Component作用于类上

@ConfigurationProperties(prefix = "person")
@Componment
@Data // lombok,用于自动生成getter、setter
public class Person {
    private String name;
}

@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;

    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实现方式二 @ConfigurationProperties + @Bean作用在配置类的bean方法上

@Data
public class Person {
    private String name;
}
@Configuration
public class PersonConf{
    @Bean
    @ConfigurationProperties(prefix="person")
    public Person person(){
        return new Person();
    }
}
@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;
    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实现方式三 @ConfigurationProperties注解到普通类、 @EnableConfigurationProperties注解定义为bean

@ConfigurationProperties(prefix="person")
@Data
public class Person {
    private String name;
}
// 说明: @EnableConfigurationProperties可以直接注到启动类上,也可以放在自定义配置类,自定义配置类使用@Configuration标注
@SpringBootApplication
@EnableConfigurationProperties(Person.class)
public class DbApplication {
    public static void main(String[] args) {
        SpringApplication.run(DbApplication.class, args);
    }
}
@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;
    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实现方式四 @Value作用属性上

@RestController
@RequestMapping("/db")
public class TestController {
    @Value("${person.name}")
    private String name;
    @GetMapping("/person")
    public String parsePerson() {
        return name;
    }
}

实现方式五 使用自带的Environment对象

@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Environment environment;
    @GetMapping("/person")
    public String parsePerson() {
        return environment.getProperty("person.name");
    }
}

读取自定义配置文件(比如:config.properties)

说明: PropertySource默认不支持yml、yaml

config.properties配置

person.name=wumingshi

实现方式一 @Configuration + @PropertySource + Environment

@Data
public class Person {
    private String name;
}
@Configuration
@PropertySource(value = "classpath:config.properties")
public class PersonConf {
    @Autowired
    private Environment environment;
    @Bean
    public Person person(){
        Person person = new Person();
        person.setName(environment.getProperty("person.name"));
        return person;
    }
}
@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;
    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实现方式二 @Component+ @PropertySource + @Value

@Component
@PropertySource(value = "classpath:config.properties")
@Data
public class Person {
    @Value("${person.name}")
    private String name;
}
@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;
    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实现方式三 @Component+ @PropertySource + @ConfigurationProperties

@Component
@PropertySource("classpath:dangxiaodang.properties")
@ConfigurationProperties(prefix = "person")
public class Person{
    private String name;
}
@RestController
@RequestMapping("/db")
public class TestController {
    @Autowired
    private Person person;
    @GetMapping("/person")
    public String parsePerson() {
        return person.getName();
    }
}

实际使用

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;


@Data
@PropertySource(value = "classpath:config.properties")
@Component
public class ProjectConfig {
    @Autowired
    private Environment env;

    public String getProperty(String str){
        return env.getProperty(str);
    }

    public String getProjectPathForCover(String str){
        return env.getProperty("cover_root_path")+"/"+str;
    }

    public String getProjectRootPath(){return env.getProperty("root_path");}

}
posted @ 2022-03-08 13:58  dongye95  阅读(116)  评论(0编辑  收藏  举报