<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring 自定義注解你了解過嗎?

          共 746字,需瀏覽 2分鐘

           ·

          2020-09-12 02:28

          3c8f748aa1daeb8e4674f932e7145045.webp??Java大聯(lián)盟

          ? 幫助萬千Java學(xué)習(xí)者持續(xù)成長

          關(guān)注


          134f5193e72a2fbccf449c23d8fca273.webp


          作者|何甜甜在嗎

          https://juejin.im/post/5cf376e16fb9a07eee5eb6eb


          B 站搜索:楠哥教你學(xué)Java

          獲取更多優(yōu)質(zhì)視頻教程

          在業(yè)務(wù)開發(fā)過程中我們會遇到形形色色的注解,但是框架自有的注解并不是總能滿足復(fù)雜的業(yè)務(wù)需求,我們可以自定義注解來滿足我們的需求。根據(jù)注解使用的位置,文章將分成字段注解、方法、類注解來介紹自定義注解。


          字段注解

          字段注解一般是用于校驗字段是否滿足要求,hibernate-validate依賴就提供了很多校驗注解 ,如@NotNull、@Range等,但是這些注解并不是能夠滿足所有業(yè)務(wù)場景的。

          比如我們希望傳入的參數(shù)在指定的String集合中,那么已有的注解就不能滿足需求了,需要自己實現(xiàn)。


          自定義注解

          定義一個@Check注解,通過@interface聲明一個注解

          @Target({?ElementType.FIELD})?//只允許用在類的字段上
          @Retention(RetentionPolicy.RUNTIME)?//注解保留在程序運行期間,此時可以通過反射獲得定義在某個類上的所有注解
          @Constraint(validatedBy?=?ParamConstraintValidated.class)
          public?@interface?Check?{
          ????/**
          ?????*?合法的參數(shù)值
          ?????*?*/
          ????String[]?paramValues();

          ????/**
          ?????*?提示信息
          ?????*?*/
          ????String?message()?default?"參數(shù)不為指定值";

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

          ????Class[]?payload()?default?{};
          }


          @Target 定義注解的使用位置,用來說明該注解可以被聲明在那些元素之前。

          ElementType.TYPE:說明該注解只能被聲明在一個類前。

          ElementType.FIELD:說明該注解只能被聲明在一個類的字段前。

          ElementType.METHOD:說明該注解只能被聲明在一個類的方法前。

          ElementType.PARAMETER:說明該注解只能被聲明在一個方法參數(shù)前。

          ElementType.CONSTRUCTOR:說明該注解只能聲明在一個類的構(gòu)造方法前。

          ElementType.LOCAL_VARIABLE:說明該注解只能聲明在一個局部變量前。

          ElementType.ANNOTATION_TYPE:說明該注解只能聲明在一個注解類型前。

          ElementType.PACKAGE:說明該注解只能聲明在一個包名前


          @Constraint?通過使用validatedBy來指定與注解關(guān)聯(lián)的驗證器

          @Retention 用來說明該注解類的生命周期。

          RetentionPolicy.SOURCE: 注解只保留在源文件中?

          RetentionPolicy.CLASS?: 注解保留在class文件中,在加載到JVM虛擬機(jī)時丟棄

          RetentionPolicy.RUNTIME: 注解保留在程序運行期間,此時可以通過反射獲得定義在某個類上的所有注解。


          驗證器類

          驗證器類需要實現(xiàn)ConstraintValidator泛型接口

          public?class?ParamConstraintValidated?implements?ConstraintValidator?{
          ????/**
          ?????*?合法的參數(shù)值,從注解中獲取
          ?????*?*/
          ????private?List?paramValues;

          ????@Override
          ????public?void?initialize(Check?constraintAnnotation)?{
          ????????//初始化時獲取注解上的值
          ????????paramValues?=?Arrays.asList(constraintAnnotation.paramValues());
          ????}

          ????public?boolean?isValid(Object?o,?ConstraintValidatorContext?constraintValidatorContext)?{
          ????????if?(paramValues.contains(o))?{
          ????????????return?true;
          ????????}

          ????????//不在指定的參數(shù)列表中
          ???
          ?????return?false;
          ????}
          }

          第一個泛型參數(shù)類型Check:注解,第二個泛型參數(shù)Object:校驗字段類型。需要實現(xiàn)initialize和isValid方法,isValid方法為校驗邏輯,initialize方法初始化工作。


          使用方式

          定義一個實體類

          @Data
          public?class?User?{
          ????/**
          ?????*?姓名
          ?????*?*/
          ????private?String?name;

          ????/**
          ?????*?性別?man?or?women
          ?????*?*/
          ????@Check(paramValues?=?{"man",?"woman"})
          ????private?String?sex;
          }

          對sex字段加校驗,其值必須為woman或者man。


          測試

          @RestController("/api/test")
          public?class?TestController?{
          ????@PostMapping
          ????public?Object?test(@Validated?@RequestBody?User?user)?{
          ????????return?"hello?world";
          ????}
          }

          注意需要在User對象上加上@Validated注解,這里也可以使用@Valid注解,@Validated 和 @Valid 的區(qū)別,這篇建議看下。


          方法、類注解

          在開發(fā)過程中遇到過這樣的需求,如只有有權(quán)限的用戶的才能訪問這個類中的方法或某個具體的方法、查找數(shù)據(jù)的時候先不從數(shù)據(jù)庫查找,先從guava cache中查找,在從redis查找,最后查找mysql(多級緩存)。

          這時候我們可以自定義注解去完成這個要求,第一個場景就是定義一個權(quán)限校驗的注解,第二個場景就是定義spring-data-redis包下類似@Cacheable的注解。


          權(quán)限注解

          自定義注解

          @Target({?ElementType.METHOD,?ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?PermissionCheck?{
          ????/**
          ?????*?資源key
          ?????*?*/
          ????String?resourceKey();
          }

          該注解的作用范圍為類或者方法上


          攔截器類

          public?class?PermissionCheckInterceptor?extends?HandlerInterceptorAdapter?{
          ????/**
          ?????*?處理器處理之前調(diào)用
          ?????*/
          ????@Override
          ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,
          ?????????????????????????????Object?handler)?throws?Exception?{
          ????????HandlerMethod?handlerMethod?=?(HandlerMethod)handler;
          ????????PermissionCheck?permission?=?findPermissionCheck(handlerMethod);

          ????????//如果沒有添加權(quán)限注解則直接跳過允許訪問
          ????????if?(permission?==?null)?{
          ????????????return?true;
          ????????}

          ????????//獲取注解中的值
          ????????String?resourceKey?=?permission.resourceKey();

          ????????//TODO?權(quán)限校驗一般需要獲取用戶信息,通過查詢數(shù)據(jù)庫進(jìn)行權(quán)限校驗
          ????????//TODO?這里只進(jìn)行簡單演示,如果resourceKey為testKey則校驗通過,否則不通過
          ????????if?("testKey".equals(resourceKey))?{
          ????????????return?true;
          ????????}

          ????????return?false;
          ????}

          ????/**
          ?????*?根據(jù)handlerMethod返回注解信息
          ?????*
          ?????*?@param?handlerMethod?方法對象
          ?????*?@return?PermissionCheck注解
          ?????*/
          ????private?PermissionCheck?findPermissionCheck(HandlerMethod?handlerMethod)?{
          ????????//在方法上尋找注解
          ????????PermissionCheck?permission?=?handlerMethod.getMethodAnnotation(PermissionCheck.class);
          ????????if?(permission?==?null)?{
          ????????????//在類上尋找注解
          ????????????permission?=?handlerMethod.getBeanType().getAnnotation(PermissionCheck.class);
          ????????}

          ????????return?permission;
          ????}
          }

          權(quán)限校驗的邏輯就是你有權(quán)限你就可以訪問,沒有就不允許訪問,本質(zhì)其實就是一個攔截器。我們首先需要拿到注解,然后獲取注解上的字段進(jìn)行校驗,校驗通過返回true,否則返回false


          測試

          ?@GetMapping("/api/test")
          ?@PermissionCheck(resourceKey?=?"test")
          ?public?Object?testPermissionCheck()?{
          ?????return?"hello?world";
          ?}

          該方法需要進(jìn)行權(quán)限校驗所以添加了PermissionCheck注解。


          緩存注解

          自定義注解

          @Target({?ElementType.METHOD,?ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          public?@interface?CustomCache?{
          ????/**
          ?????*?緩存的key值
          ?????*?*/
          ????String?key();
          }

          注解可以用在方法或類上,但是緩存注解一般是使用在方法上的。


          切面

          @Aspect
          @Component
          public?class?CustomCacheAspect?{
          ????/**
          ?????*?在方法執(zhí)行之前對注解進(jìn)行處理
          ?????*
          ?????*?@param?pjd
          ?????*?@param?customCache?注解
          ?????*?@return?返回中的值
          ?????*?*/
          ????@Around("@annotation(com.cqupt.annotation.CustomCache)?&&?@annotation(customCache)")
          ????public?Object?dealProcess(ProceedingJoinPoint?pjd,?CustomCache?customCache)?{
          ????????Object?result?=?null;

          ????????if?(customCache.key()?==?null)?{
          ????????????//TODO?throw?error
          ????????}

          ????????//TODO?業(yè)務(wù)場景會比這個復(fù)雜的多,會涉及參數(shù)的解析如key可能是#{id}這些,數(shù)據(jù)查詢
          ????????//TODO?這里做簡單演示,如果key為testKey則返回hello?world
          ????????if?("testKey".equals(customCache.key()))?{
          ????????????return?"hello?word";
          ????????}

          ????????//執(zhí)行目標(biāo)方法
          ????????try?{
          ????????????result?=?pjd.proceed();
          ????????}?catch?(Throwable?throwable)?{
          ????????????throwable.printStackTrace();
          ????????}

          ????????return?result;
          ????}
          }

          因為緩存注解需要在方法執(zhí)行之前有返回值,所以沒有通過攔截器處理這個注解,而是通過使用切面在執(zhí)行方法之前對注解進(jìn)行處理。

          如果注解沒有返回值,將會返回方法中的值


          測試

          @GetMapping("/api/cache")
          @CustomCache(key?=?"test")
          public?Object?testCustomCache()?{
          ????return?"don't?hit?cache";
          }


          推薦閱讀

          1、Spring Boot+Vue項目實戰(zhàn)

          2、B站:4小時上手MyBatis Plus

          3、一文搞懂前后端分離

          4、快速上手Spring Boot+Vue前后端分離


          楠哥簡介

          資深 Java 工程師,微信號?southwindss

          《Java零基礎(chǔ)實戰(zhàn)》一書作者

          騰訊課程官方 Java 面試官,今日頭條認(rèn)證大V

          GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)

          致力于幫助萬千 Java 學(xué)習(xí)者持續(xù)成長。




          有收獲,就在看?fafb397a26d6b57a83a44d4873bdc4df.webp
          瀏覽 57
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  那个网站可以看毛片 | 少妇精品在线 | 猫咪AV成人永久网站在线观看 | 北条麻妃九九九精品视频免费观看 | 丝袜二区 |