Java注解之實(shí)戰(zhàn)

一、什么是注解

  • Java 注解是在 JDK5 時(shí)引入的新特性,注解(也被稱為元數(shù)據(jù))為我們?cè)诖a中添加信息提供了一種形式化的方法,使我們可以在稍后某個(gè)階段方便的使用這些數(shù)據(jù)。

二、JDK常用注解、元注解

常用注解如下

  • @Override: 表示注解修飾的方法必須滿足重寫(xiě)的規(guī)則
  • @Deprecated: 表示成員過(guò)時(shí),編譯器可以在程序運(yùn)行的時(shí)候獲取到該注解
  • @SupressWarnings: 表示忽略編譯器的警告
    常見(jiàn)的元注解:
  • @Target 可聲明在哪些目標(biāo)元素之前
 ElementType.PARAMETER               聲明在參數(shù)上
 ElementType.METHOD                  聲明在方法上
 ElementType.FIELD                   聲明在字段上
 ElementType.TYPE                    聲明在類,接口,枚舉
 ElementType.CONSTRUCTOR             聲明在構(gòu)造函數(shù)
 ElementType.LOCAL_VARIABLE          局部變量
 ElementType.ANNOTATION_TYPE         注釋類型聲明
 ElementType.PACKAGE                 包聲明
  • @Retention 注解類的生命周期,及作用在哪個(gè)階段
RetentionPolicy.RUNTIME   程序運(yùn)行時(shí)期起作用
RetentionPolicy.SOURCE    在原文件中有效,被編譯器丟棄
RetentionPolicy.CLASS     程序編譯時(shí)期起作用
  • @Documented 生成文檔的時(shí)候,會(huì)被寫(xiě)入到文檔中

三、如何使用注解

那么注解是怎么在某個(gè)階段被方便的使用呢,比如我們自定義一個(gè)注解,程序是如何使用的呢?其實(shí)注解不能自己起作用,需要程序員自身去針對(duì)注解寫(xiě)專門(mén)的邏輯,比如:日志切面針對(duì)日志注解進(jìn)行掃描處理;登錄注解使用攔截器去判斷是否包含再處理登錄邏輯。

四、注解實(shí)戰(zhàn)

接下來(lái)介紹下注解在實(shí)際中的使用,本次介紹的demo是一個(gè)利用注解來(lái)實(shí)現(xiàn)動(dòng)態(tài)參數(shù)校驗(yàn)功能。
1、首先新建一個(gè)驗(yàn)證注解,用于切入點(diǎn)進(jìn)行參數(shù)校驗(yàn)。

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidParam {
}

2、其次編寫(xiě)實(shí)現(xiàn)此注解的掃描,對(duì)含有此注解的方法參數(shù)進(jìn)行校驗(yàn)。這里我們用到了SpringMVC中的參數(shù)解析器,RequestBody注解的操作類繼承了AbstractMessageConverterMethodArgumentResolver,因此我們也使用此類實(shí)現(xiàn)RequestBody相同參數(shù)解析功能。

@Slf4j
public class ValidMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
    private static Validator validator;

    static {    
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    public ValidMethodArgumentResolver(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice){
        super(converters, requestResponseBodyAdvice);
    }


    public ValidMethodArgumentResolver(List<HttpMessageConverter<?>> converters) {
        super(converters);
    }

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ValidParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        final Object object = readWithMessageConverters(nativeWebRequest, methodParameter, methodParameter.getParameterType());
        validateParam(object);
        return object;
    }

    /**
     * 參數(shù)校驗(yàn)
     * @param object
     */
    private void validateParam(Object object) {
        Set<ConstraintViolation<Object>> constraintViolations;
        if(object instanceof List){
            List list = (List)object;
            list.forEach(p -> validateParam(p));
        }else {
            constraintViolations = validator.validate(object);
            if (!constraintViolations.isEmpty()) {
                ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
                throw new ParamException(GloabEnums.PARAM_ERROR.getCode(), constraint.getMessage());
            }
        }
    }
}

使用之后我們需要將此類配置到參數(shù)解析器中,及實(shí)現(xiàn)WebMvcConfigurer類中,具體代碼如下:

 @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
        List<HttpMessageConverter<?>> converters = new ArrayList<>();
        converters.add(new MappingJackson2HttpMessageConverter());
        list.add(new ValidMethodArgumentResolver(converters));
    }

首先是實(shí)現(xiàn)HandlerMethodArgumentResolver方法的supportsParameter方法,用于判斷是否包含自定義驗(yàn)證注解,其次包含則調(diào)用resolveArgument方法對(duì)參數(shù)進(jìn)行處理驗(yàn)證;這里我們使用了validator驗(yàn)證工具包進(jìn)行校驗(yàn)。
3、接下來(lái)我們對(duì)入?yún)?shí)體進(jìn)行校驗(yàn)驗(yàn)證,這里我們使用了NotB

@Data
public class UserDto {
    @NotBlank(message = "名字不能為空")
    private String name;
    @NotBlank(message = "電話號(hào)碼為空")
    @Pattern(regexp = "^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\\d|9\\d)\\d{8}$", message = "電話號(hào)碼有誤")
    private String phone;
    private int age;
}
@Slf4j
@RestController
@RequestMapping("valid")
public class ValidController {

    @PostMapping("user")
    public R insertAddress(@ValidParam UserDto dto){
        log.info("參數(shù)驗(yàn)證成功,執(zhí)行正常邏輯:{}", dto);
        return R.ok();
    }
}

調(diào)用該接口:


image.png

有人會(huì)問(wèn),當(dāng)我們使用增、刪、改需要使用同一個(gè)類,如用如上參數(shù)注解校驗(yàn),則會(huì)引起新增的方法判斷了修改方法的校驗(yàn),我們不同的方法是需要對(duì)此類不同的字段進(jìn)行校驗(yàn),這不是要寫(xiě)多個(gè)類才能進(jìn)行校驗(yàn)嗎?
既然有不同的需求,我們就有不同的方案來(lái)進(jìn)行處理,這里我們引入group組這個(gè)概念,將字段劃分組進(jìn)行區(qū)分校驗(yàn)。

public interface QueryGroup {}

public @interface ValidParam {
    Class value() default QueryGroup.class;
}

@NotBlank(message = "名字不能為空", groups = QueryGroup.class)
    private String name;

@PostMapping("user")
    public R insertAddress(@ValidParam(value = QueryGroup.class) UserDto dto){
        log.info("參數(shù)驗(yàn)證成功,執(zhí)行正常邏輯:{}", dto);
        return R.ok();
    }

/**
     * 參數(shù)校驗(yàn)
     * @param object
     */
    private void validateParam(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations;
        if(object instanceof List){
            List list = (List)object;
            list.forEach(p -> validateParam(p, groups));
        }else {
            constraintViolations = validator.validate(object, groups);
            if (!constraintViolations.isEmpty()) {
                ConstraintViolation<Object> constraint = (ConstraintViolation<Object>)constraintViolations.iterator().next();
                throw new ParamException(GloabEnums.PARAM_ERROR.getCode(), constraint.getMessage());
            }
        }
    }

四、總結(jié)

1、注解就是對(duì)代碼的解釋,可以把它類同于標(biāo)簽
2、注解的定義只是interface前加了個(gè)@
3、注解不會(huì)自己起作用,需要程序開(kāi)發(fā)人員去開(kāi)發(fā)對(duì)應(yīng)的功能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容