spring mvc 10個常用註解,提升開發效率,你用到了幾個?

日常開發中常用的spring註解

    1. @RestController

標明此Controller提供RestAPI,則Controller中的方法不返回jsp頁面,配置的視圖解析器InternalResourceViewResolver不起作用,返回的內容就是Return 裏的內容。因爲現在大多都是前後端分離項目,所以這個註解會經常使用。

@RestController("test")
public class Mytest{
    
    @RequestMapping("/hello")
    public String myHello(){
        return "hello";
    }
}

等同於:

@Controller("test")
public class Mytest{
    
    @RequestMapping("/hello")
    @ResponseBody
    public String myHello(){
        return "hello";
    }
}

    2. @RequestMapping及其變體

映射http請求url到java方法,其變體有GetMapping, PostMapping,PutMapping, DeleteMapping,同上都是簡化寫法

@RestController("test")
public class Mytest{
    
    @RequestMapping(value = "mytest", method = RequestMethod.POST)
    public String myHello(){
        return "hello";
    }
}

等同於:

 

@RestController("test")
public class Mytest{
    
    @PostMapping(value = "mytest")
    public String myHello(){
        return "hello";
    }
}

     3. @RequestParam

    映射請求參數到java方法的參數,寫到方法參數上的時候,就是必須要求前端傳入參數時帶上這個參數,如果不帶則會報錯,但是加上默認值default value就不會報錯,或者required 可以設置非必需和必須。

value:請求中傳入參數的名稱,如果不設置後臺接口的value值,則會默認爲該變量名。比如上圖中第一個參數如果不設置value="page",則前端傳入的參數名必須爲pageNum,否則在後臺接口中pageNum將接收不到對應的數據

required:該參數是否爲必傳項。默認是true,表示請求中一定要傳入對應的參數,否則會報404錯誤,如果設置爲false時,當請求中沒有此參數,將會默認爲null,而對於基本數據類型的變量,則必須有值,這時會拋出空指針異常。如果允許空值,則接口中變量需要使用包裝類來聲明。

defaultValue:參數的默認值,如果請求中沒有同名的參數時,該變量默認爲此值。注意默認值可以使用SpEL表達式,如"#{systemProperties['java.vm.version']}"

@RequestMapping(value = "/testRequestParam")
    public String testRequestParam(@RequestParam(value="username")String un,@RequestParam(value = "age",required = false,defaultValue = "0") int age){
        System.out.println("testRequestParam,username:"+un+",age,"+age);
        return "success";
    }

    4. @PageableDefault

    指定分頁參數默認值    

         配合Pageable pageable 對象 使用起來非常方便 前端只需要 寫上分頁相關信息 size:15  page :1  sort : age,desc 這樣的形式即可

@GetMapping
public List<User> query(@RequestParam String username, @PageableDefault(size = 20, page = 0, sort = "username") Pageable pageable) {
    System.out.println(pageable.getPageNumber());
    System.out.println(pageable.getPageSize());
    System.out.println(pageable.getSort());
    List<User> users = Lists.newArrayList();
    users.add(new User("a", "132"));
    users.add(new User("b", "456"));
    users.add(new User("c", "789"));
    return users;
}

    5. @PathVariable

完美配合Rest風格API,通過 @PathVariable 可以將 URL 中佔位符參數綁定到控制器處理方法的入參中:URL 中的 {xxx} 佔位符可以通過@PathVariable(“xxx“) 綁定到操作方法的入參中。其中參數name 和 參數value 的效果是同等的 ,都是指定url中片段的參數名

@GetMapping("/{id:\\d+}")// 正則表達式限定參數
@JsonView(User.UserDetailView.class)// 指定方法返回詳細對象視圖,有username和password
public User getInfo(@PathVariable String id) {
 return new User(id,"tom","123");
}

    6.@JsonView

很好用的註解,用來控制Json輸出內容

在某一些請求返回的JSON中,我們並不希望返回某些字段。而在另一些請求中需要返回某些字段。  

可以自定義選擇給前端傳Json對象的不同屬性,使用它就可以很好的避免創建多個重複的DTO對象給前端傳值問題。非常方便。

        使用步驟:

  • 1.使用接口來聲明多個視圖
  • 2.在值對象的get方法上指定視圖
  • 3.在Controller的方法上指定視圖

            步驟1.使用接口來聲明多個視圖(例如查詢用戶列表場景。就不傳PASSWORD,查詢用戶詳情的場景就是要帶上PASSWORD)

package com.ls.dto;

/**
* @program: ls-web
* @description: 封裝用戶數據
* @author: Liang Shan
* @create: 2019-10-28 10:37
**/
public class User {
    /*用戶簡單視圖*/
    public interface UserSimpleView {
    };
    /*用戶詳細視圖*/
    public interface UserDetailView extends UserSimpleView {
    };
    private String username;
    private String password;


    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;
    }
}

 

            步驟2.在值對象的get方法上指定視圖。

package com.ls.dto;
import com.fasterxml.jackson.annotation.JsonView;
/**
* @program: ls-web
* @description: 封裝用戶數據
* @author: Liang Shan
* @create: 2019-10-28 10:37
**/
public class User {
    /*用戶簡單視圖*/
    public interface UserSimpleView {
    };
    /*用戶詳細視圖*/
    public interface UserDetailView extends UserSimpleView{
    };
    private String username;
    private String password;


    @JsonView(UserSimpleView.class)
    public String getUsername() {
        return username;
    }


    public void setUsername(String username) {
        this.username = username;
    }
    // 因爲有繼承關係,所以使用了UserDetailView的查詢接口也會有username這個字段
    @JsonView(UserDetailView.class)
    public String getPassword() {
        return password;
    }


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

 步驟3.在Contrtoller方法上指定視圖。

package com.ls.web.controller;


import com.fasterxml.jackson.annotation.JsonView;
import com.ls.dto.User;
import org.assertj.core.util.Lists;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


import java.util.List;


/**
* @program: ls-web
* @description: 用戶Controller層
* @author: Liang Shan
* @create: 2019-10-28 10:15
**/
@RestController
public class UserController {
    @GetMapping("/user")
    @JsonView(User.UserSimpleView.class)// 指定方法返回簡單對象視圖,只有username
    public List<User> query(@RequestParam  String username, @PageableDefault(size = 20, page = 0, sort = "username") Pageable pageable) {
        System.out.println(pageable.getPageNumber());
        System.out.println(pageable.getPageSize());
        System.out.println(pageable.getSort());
        List<User> users = Lists.newArrayList();
        users.add(new User("a","132"));
        users.add(new User("b","456"));
        users.add(new User("c","789"));
        return users;
    }


    @GetMapping("/user/{id:\\d+}")// 正則表達式限定參數
    @JsonView(User.UserDetailView.class)// 指定方法返回詳細對象視圖,有username和password
    public User getInfo(@PathVariable String id) {
        User user = new User("a","132");
        user.setUsername("tom");
        return user;
    }
}

 返回結果 

簡單視圖:

詳情視圖:

    7. @RequestBody

映射請求體到JAVA方法的參數

主要用來接收前端傳遞給後端的json字符串中的數據的(請求體中的數據的);GET方式無請求體,所以使用@RequestBody接收數據時,前端不能使用GET方式提交數據,而是用POST方式進行提交。在後端的同一個接收方法裏,@RequestBody與@RequestParam()可以同時使用,@RequestBody最多隻能有一個,而@RequestParam()可以有多個。

    8. @JsonFormat

從數據庫獲取時間傳到前端進行展示的時候,我們有時候可能無法得到一個滿意的時間格式的時間日期,在數據庫中顯示的是正確的時間格式,獲取出來卻變成了很醜的時間戳,@JsonFormat註解很好的解決了這個問題,我們通過使用@JsonFormat可以很好的解決:後臺到前臺時間格式保持一致的問題

實體類中的屬性上打上這個註解:

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

    9. @DateTimeFormat

我們在使用WEB服務的時,可能會需要用到,傳入時間給後臺,比如註冊新用戶需要填入出生日期等,這個時候前臺傳遞給後臺的時間格式同樣是不一致的,而我們的與之對應的便有了另一個註解,@DataTimeFormat便很好的解決了這個問題,接下來記錄一下具體的@JsonFormat與DateTimeFormat的使用過程。

同上可以一起使用, 也是實體類中的屬性上打上這個註解:

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

    10. @Valid 註解和BindingResult

@Valid 註解和BindingResult驗證請求參數的合法性並處理校驗結果 。

校驗參數是寫API不可避免的事情,一定要驗證用戶傳上來的數據是不是有效的,只有在用戶傳上來的數據滿足自己的業務要求的時候,才應該再進行業務的處理,所以校驗是一個非常常見的場景,傳統的校驗無非就是使用 if()手動來判斷,但是如果API很多,就會有很多重複代碼,如果好點的可以封裝爲方法,不好的話,萬一修改,那將會是非常麻煩的一件事情。

所以使用@Valid註解來做參數的校驗可以很好的解決這個問題,springboot利用切面集合進行所有controller的參數攔截和校驗,避免了業務代碼的臃腫,當出現業務邏輯的修改時也十分方便。

 步驟1:

先在實體對象中填寫需要校驗參數的註解,例如@NotBlank @NotNull等等

public class User {

    private String id;

    private String username;

    @NotBlank(message = "密碼不能爲空")
    private String password;

    private LocalDate birthday;

步驟2:

然後在controller層的對象參數前加上@Valid註解就可以正常的判斷了

@PostMapping
public User create(@Valid @RequestBody User user) {
    System.out.println(user.getId());
    System.out.println(user.getUsername());
    System.out.println(user.getPassword());
    System.out.println("hahaha"+user.getBirthday());
    user.setId("1");
    return user;
}

校驗註解的規則:

@AssertFalse

Boolean,boolean

驗證註解的元素值是false

@AssertTrue

Boolean,boolean

驗證註解的元素值是true

@NotNull

任意類型

驗證註解的元素值不是null

@Null

任意類型

驗證註解的元素值是null

@Min(value=值)

BigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存儲的是數字)子類型

驗證註解的元素值大於等於@Min指定的value值

@Max(value=值)

和@Min要求一樣

驗證註解的元素值小於等於@Max指定的value值

@DecimalMin(value=值)

和@Min要求一樣

驗證註解的元素值大於等於@ DecimalMin指定的value值

@DecimalMax(value=值)

和@Min要求一樣

驗證註解的元素值小於等於@ DecimalMax指定的value值

@Digits(integer=整數位數, fraction=小數位數)

和@Min要求一樣

驗證註解的元素值的整數位數和小數位數上限

@Size(min=下限, max=上限)

字符串、Collection、Map、數組等

驗證註解的元素值的在min和max(包含)指定區間之內,如字符長度、集合大小

@Past

java.util.Date,java.util.Calendar;Joda Time類庫的日期類型

驗證註解的元素值(日期類型)比當前時間早

@Future

與@Past要求一樣

驗證註解的元素值(日期類型)比當前時間晚

@NotBlank

CharSequence子類型

驗證註解的元素值不爲空(不爲null、去除首位空格後長度爲0),不同於@NotEmpty,@NotBlank只應用於字符串且在比較時會去除字符串的首位空格

@Length(min=下限, max=上限)

CharSequence子類型

驗證註解的元素值長度在min和max區間內

@NotEmpty

CharSequence子類型、Collection、Map、數組

驗證註解的元素值不爲null且不爲空(字符串長度不爲0、集合大小不爲0)

@Range(min=最小值, max=最大值)

BigDecimal,BigInteger,CharSequence, byte, short, int, long等原子類型和包裝類型

驗證註解的元素值在最小值和最大值之間

@Email(regexp=正則表達式,flag=標誌的模式)

CharSequence子類型(如String)

驗證註解的元素值是Email,也可以通過regexp和flag指定自定義的email格式

@Pattern(regexp=正則表達式,flag=標誌的模式)

String,任何CharSequence的子類型

驗證註解的元素值與指定的正則表達式匹配

@Valid

任何非原子類型

指定遞歸驗證關聯的對象;如用戶對象中有個地址對象屬性,如果想在驗證用戶對象時一起驗證地址對象的話,在地址對象上加@Valid註解即可級聯驗證

 

在Valid 判斷後,如果發生了錯誤,是不會進入方法體的,直接返回給前端錯誤碼,但是如果我想在發生錯誤後進入方法體內執行,比如記錄錯誤日誌等,就需要BindingResult類來處理了。

一個@Valid 對應一個 BindingResult,如果有多個,就要依次對應。

@PutMapping("/{id:\\d+}")
@JsonView(User.UserDetailView.class)
public User update(@Valid@RequestBody User user,BindingResult errors) {
    if (errors.hasErrors()) {
        errors.getAllErrors().stream().forEach(e ->{
            System.out.println(e.getCode()+" " +e.getDefaultMessage());} );
    }
    return user;
}

控制檯打印的輸出結果: 

然而使用javax提供的校驗註解只能是滿足基礎的簡單驗證,再不過就是使用正則表達式對格式進行校驗,但是實際開發環境中,可能需要更爲複雜的業務數據的校驗,比如說是查詢數據庫的數值後對比這種複雜的業務邏輯的校驗,這時候基本的校驗註解就不能滿足我們的需求了,所以,我們也可以自定義校驗註解,來達到符合自己的業務邏輯需求的註解

自定義校驗註解:

步驟1:新建一個註解類 ,要打上@Target @Retention @Constraint 註解

然後需要實現代碼中的三個屬性(message可以不要,但是使用時必須強制要求填寫message)

 

package com.ls.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @Description: 自定義約束校驗註解
* @Author: Liang Shan
* @Date: 2019/10/30 0030
*/
/**
*@Target:註解可以使用的地方
*@Retention(
*1、RetentionPolicy.SOURCE:註解只保留在源文件,當Java文件編譯成class文件的時候,註解被遺棄;
*2、RetentionPolicy.CLASS:註解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認的生命週期;
*3、RetentionPolicy.RUNTIME:註解不僅被保存到class文件中,jvm加載class文件之後,仍然存在;
*@Constraint:校驗實現類
*/
@Target({ElementType.METHOD, ElementType.FIELD})// @Target是爲了定義這個註解可以標註在哪裏,這裏因爲是需要做參數的校驗,所以定義,可以標註在方法上和字段上
@Retention(RetentionPolicy.RUNTIME)// 運行時的一個註解
@Constraint(validatedBy = MyConstraintValidator.class)// javax校驗註解,聲明這個註解用於校驗的,並且可以指定當前註解的具體業務邏輯實現類是哪一個類
public @interface MyConstraint {
    // 如果要實現javax的校驗註解,就需要實現三個參數 第一個message, 第二個groups 第三個payload
    String message() default "{自定義約束校驗註解消息}";


    Class<?>[] groups() default {};


    Class<? extends Payload>[] payload() default {};
}

步驟2:實現校驗實現類

需要實現ConstraintValidator這個泛型接口,泛型的第一個參數是實現哪個註解類,第二個參數是能夠使用這個註解的數據類型是什麼

package com.ls.validator;

import com.ls.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
* @program: ls-security
* @description: 註解實現業務邏輯類
* 實現了 javax的 ConstraintValidator的接口 需要定義它的泛型, 有兩個值, 第一個值是指定我需要去實現業務邏輯的註解類,第二個就是限定這個註解能夠驗證的數據類型
* @author: Liang Shan
* @create: 2019-10-30 11:34
**/
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {


    /*
    * 這個類不需要去打@Compoment註解,實現了ConstraintValidator接口的類,Spring會自動把他註冊到bean裏,所以可以直接使用@Autowired註解
    * */
    @Autowired
    private HelloService helloService;


    /**
     * @Description: 初始化方法,在這個方法中可以進行一些初始化的處理
     * @Param: [constraintAnnotation]
     * @return: void
     * @Author: Liang Shan
     * @Date: 2019/10/30 0030
     */
    @Override
    public void initialize(MyConstraint constraintAnnotation) {
        System.out.println("自定義註解實現初始化");
    }




    /**
     * @Description: 實現具體判斷業務邏輯的方法
     * @Param: [s, constraintValidatorContext]
     * @return: boolean
     * @Author: Liang Shan
     * @Date: 2019/10/30 0030
     */
    @Override
    public boolean isValid(Object a, ConstraintValidatorContext constraintValidatorContext) {
        System.out.println("進入到判斷體中");
        return false;
    }
}

步驟3:在你的實體類中打上註解

/**
* @program: ls-web
* @description: 封裝用戶數據
* @author: Liang Shan
* @create: 2019-10-28 10:37
**/
public class User {
    /*用戶簡單視圖*/
    public interface UserSimpleView {
    };
    /*用戶詳細視圖*/
    public interface UserDetailView extends UserSimpleView{
    };

    private String id;

    @MyConstraint(message = "用戶名只能是小明")
    private String username;

    @NotBlank(message = "密碼不能爲空")
    private String password;

    @Past(message = "時間不正確")
    private Date birthday;
}

步驟4:使用@Valid 激活註解

@PutMapping("/{id:\\d+}")// 正則表達式限定參數
@JsonView(User.UserDetailView.class)// 指定方法返回詳細對象視圖,有username和password
public User update(@Valid @RequestBody User user,BindingResult errors) {
    if (errors.hasErrors()) {
        errors.getAllErrors().stream().forEach(e ->{
            System.out.println(e.getCode()+" " +e.getDefaultMessage());} );
    }
    return user;
}

最後,驗證通過完成:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章