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

          天天都在使用的 Java 注解,你真的了解它嗎?

          共 18294字,需瀏覽 37分鐘

           ·

          2020-11-29 01:22




          Hello,大家好,我是阿粉,Java 的注解相信大家天天都在用,但是關于注解的原理,大家都了解嗎?這篇文章通過意見簡單的示例給大家演示一下注解的使用和原理。

          Java 元注解

          注解(Annotation)是一種可以放在 Java 類上,方法上,屬性上,參數前面的一種特殊的注釋,用來注釋注解的注解叫做元注解。元注解我們平常不會編寫,只需要添加到我們自己編寫的注解上即可,。

          Java 自帶的常用的元注解有@Target@Retention@Documented@Inherited 分別有如下含義

          1. @Target:標記這個注解使用的地方,取值范圍在枚舉 java.lang.annotation.ElementTypeTYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE
          2. @Retention :標識這個注解的生命周期,取值范圍在枚舉 java.lang.annotation.RetentionPolicySOURCE,CLASS,RUNTIME,一般定義的注解都是在運行時使用,所有要用 @Retention(RetentionPolicy.RUNTIME);
          3. @Documented:表示注解是否包含到文檔中。
          4. @Inherited :使用@Inherited定義子類是否可繼承父類定義的Annotation@Inherited僅針對@Target(ElementType.TYPE)類型的annotation有效,并且僅針對class的繼承,對interface的繼承無效。

          定義注解

          上面介紹了幾個元注解,下面我們定義一個日志注解來演示一下,我們通過定義一個名為OperationLog ?的注解來記錄一些通用的操作日志,比如記錄什么時候什么人查詢的哪個表的數據或者新增了什么數據。編寫注解我們用的是 @interface 關鍵字,相關代碼如下:

          package?com.api.annotation;

          import?java.lang.annotation.*;

          /**
          ?*?

          ?*?Function:

          ?*?Author:@author?子悠

          ?*?Date:2020-11-17 22:10

          ?*?Desc:用于記錄操作日志

          ?*/

          @Target({ElementType.METHOD})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public?@interface?OperationLog?{

          ????/**
          ?????*?操作類型
          ?????*
          ?????*?@return
          ?????*/

          ????String?type()?default?OperationType.SELECT;

          ????/**
          ?????*?操作說明
          ?????*
          ?????*?@return
          ?????*/

          ????String?desc()?default?"";

          ????/**
          ?????*?請求路徑
          ?????*
          ?????*?@return
          ?????*/

          ????String?path()?default?"";

          ????/**
          ?????*?是否記錄日志,默認是
          ?????*
          ?????*?@return
          ?????*/

          ????boolean?write()?default?true;

          ????/**
          ?????*?是否需要登錄信息
          ?????*
          ?????*?@return
          ?????*/

          ????boolean?auth()?default?true;
          ???/**
          ?????*?當?type?為?save?時必須
          ?????*
          ?????*?@return
          ?????*/

          ????String?primaryKey()?default?"";

          ????/**
          ?????*?對應?service?的?Class
          ?????*
          ?????*?@return
          ?????*/

          ????Class?defaultServiceClass()?default?Object.class;
          }

          說明

          上面的注解,我們增加了@Target({ElementType.METHOD}) , @Retention(RetentionPolicy.RUNTIME), @Documented 三個元注解,表示我們這個注解是使用在方法上的,并且生命周期是運行時,而且可以記錄到文檔中。然后我們可以看到定義注解采用的u是@interface ?關鍵字,并且我們給這個注解定義了幾個屬性,同時設置了默認值。主要注意的是平時我們編寫的注解一般必須設置@Target@Retention,而且 @Retention一般設置為RUNTIME,這是因為我們自定義的注解通常要求在運行期讀取,另外一般情況下,不必寫@Inherited

          使用

          上面的動作只是把注解定義出來了,但是光光定義出來是沒有用的,必須有一個地方讀取解析,才能提現出注解的價值,我們就采用 Spring 的 AOP 攔截這個注解,將所有攜帶這個注解的方法所進行的操作都記錄下來。

          package?com.api.config;

          import?lombok.extern.slf4j.Slf4j;
          import?org.aspectj.lang.ProceedingJoinPoint;
          import?org.aspectj.lang.annotation.Around;
          import?org.aspectj.lang.annotation.Aspect;
          import?org.aspectj.lang.annotation.Pointcut;
          import?org.aspectj.lang.reflect.MethodSignature;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.core.annotation.Order;
          import?org.springframework.stereotype.Component;
          import?org.springframework.web.bind.annotation.GetMapping;
          import?org.springframework.web.bind.annotation.PostMapping;
          import?org.springframework.web.bind.annotation.RequestMapping;

          import?javax.servlet.http.HttpServletRequest;
          import?java.lang.reflect.Field;
          import?java.lang.reflect.Method;
          import?java.util.*;

          /**
          ?*?

          ?*?Function:

          ?*?Author:@author?子悠

          ?*?Date:2020-11-17 14:40

          ?*?Desc:aspect for operation log

          ?*/

          @Aspect
          @Component
          @Order(-5)
          @Slf4j
          public?class?LogAspect?{
          ????/**
          ?????*?Pointcut?for?methods?which?need?to?record?operate?log
          ?????*/

          ????@Pointcut("within(com.xx.yy.controller..*)?&&?@annotation(com.api.annotation.OperationLog)")
          ????public?void?logAspect()?{
          ????}

          ????/**
          ?????*?record?log?for?Admin?and?DSP
          ?????*
          ?????*?@param?joinPoint?parameter
          ?????*?@return?result
          ?????*?@throws?Throwable
          ?????*/

          ????@Around("logAspect()")
          ????public?Object?around(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
          ????????Object?proceed?=?null;
          ????????String?classType?=?joinPoint.getTarget().getClass().getName();
          ????????Class?targetCls?=?Class.forName(classType);
          ????????MethodSignature?ms?=?(MethodSignature)?joinPoint.getSignature();
          ????????Method?targetMethod?=?targetCls.getDeclaredMethod(ms.getName(),?ms.getParameterTypes());
          ????????OperationLog?operation?=?targetMethod.getAnnotation(OperationLog.class);
          ????????if?(null?!=?operation?&&?operation.write())?{
          ????????????SysMenuOpLogEntity?opLogEntity?=?new?SysMenuOpLogEntity();
          ????????????StringBuilder?change?=?new?StringBuilder();
          ????????????if?(StrUtil.isNotBlank(operation.type()))?{
          ????????????????switch?(operation.type())?{
          ????????????????????case?OperationType.ADD:
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????String?addString?=?genAddData(targetCls,?operation.defaultServiceClass(),?joinPoint.getArgs());
          ????????????????????????opLogEntity.setAfterJson(addString);
          ????????????????????????change.append(OperationType.ADD);
          ????????????????????????break;
          ????????????????????case?OperationType.DELETE:
          ????????????????????????String?deleteString?=?autoQueryDeletedData(targetCls,?operation.primaryKey(),?operation.defaultServiceClass(),?joinPoint.getArgs());
          ????????????????????????opLogEntity.setBeforeJson(deleteString);
          ????????????????????????change.append(OperationType.DELETE);
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????break;
          ????????????????????case?OperationType.EDIT:
          ????????????????????????change.append(OperationType.EDIT);
          ????????????????????????setOpLogEntity(opLogEntity,?targetCls,?operation.primaryKey(),?operation.defaultServiceClass(),?joinPoint.getArgs());
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????break;
          ????????????????????case?OperationType.SELECT:
          ????????????????????????opLogEntity.setBeforeJson(getQueryString(targetCls,?operation.defaultServiceClass(),?joinPoint.getArgs()));
          ????????????????????????change.append(operation.type());
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????break;
          ????????????????????case?OperationType.SAVE:
          ????????????????????????savedDataOpLog(opLogEntity,?targetCls,?operation.primaryKey(),?operation.defaultServiceClass(),?joinPoint.getArgs());
          ????????????????????????change.append(operation.type());
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????break;
          ????????????????????case?OperationType.EXPORT:
          ????????????????????case?OperationType.DOWNLOAD:
          ????????????????????????change.append(operation.type());
          ????????????????????????proceed?=?joinPoint.proceed();
          ????????????????????????break;
          ????????????????????default:
          ????????????????}
          ????????????????opLogEntity.setExecType(operation.type());
          ????????????}
          ????????????StringBuilder?changing?=?new?StringBuilder();
          ????????????if?(StrUtil.isNotBlank(opLogEntity.getExecType()))?{
          ????????????????if?(operation.auth())?{
          ????????????????????LoginUserVO?loginUser?=?getLoginUser();
          ????????????????????if?(null?!=?loginUser)?{
          ????????????????????????opLogEntity.setUserId(loginUser.getUserId());
          ????????????????????????opLogEntity.setUserName(loginUser.getUserName());
          ????????????????????????changing.append(loginUser.getUserName()).append("-");
          ????????????????????}?else?{
          ????????????????????????log.error("用戶未登錄");
          ????????????????????}
          ????????????????}
          ????????????????opLogEntity.setCreateTime(DateUtils.getCurDate());
          ????????????????opLogEntity.setRemark(getOperateMenuName(targetMethod,?operation.desc()));
          ????????????????opLogEntity.setPath(getPath(targetMethod,?targetMethod.getName()));
          ????????????????opLogEntity.setChanging(changing.append(change).toString());
          ????????????????menuOpLogService.save(opLogEntity);
          ????????????}
          ????????}
          ????????return?proceed;
          ????}

          ????/**
          ?????*?query?data?by?userId
          ?????*
          ?????*?@param?targetCls???????????class
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return
          ?????*?@throws?Exception
          ?????*/

          ????private?String?queryByCurrentUserId(Class?targetCls,?Class?defaultServiceClass)?throws?Exception?{
          ????????BaseService?baseService?=?getBaseService(targetCls,?defaultServiceClass);
          ????????LoginUserVO?loginUser?=?dspBaseService.getLoginUser();
          ????????if?(null?!=?loginUser)?{
          ????????????Object?o?=?baseService.queryId(loginUser.getUserId());
          ????????????return?JsonUtils.obj2Json(o);
          ????????}
          ????????return?null;
          ????}

          ????/**
          ?????*?return?query?parameter
          ?????*
          ?????*?@param?targetCls???????????class
          ?????*?@param?args????????????????parameter
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return
          ?????*?@throws?Exception
          ?????*/

          ????private?String?getQueryString(Class?targetCls,?Class?defaultServiceClass,?Object[]?args)?{
          ????????if?(args.length?>?0)?{
          ????????????Class?entityClz?=?getEntityClz(targetCls,?defaultServiceClass);
          ????????????for?(Object?arg?:?args)?{
          ????????????????if?(arg.getClass().equals(entityClz)?||?arg?instanceof?BaseModel)?{
          ????????????????????return?JsonUtils.obj2Json(arg);
          ????????????????}
          ????????????}
          ????????}
          ????????return?null;
          ????}

          ????/**
          ?????*?save?record?log?while?OperatorType?is?SAVE
          ?????*
          ?????*?@param?opLogEntity?????????entity
          ?????*?@param?targetCls???????????class
          ?????*?@param?primaryKey??????????primaryKey
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@param?args????????????????parameter
          ?????*?@throws?Exception
          ?????*/

          ????private?void?savedDataOpLog(SysMenuOpLogEntity?opLogEntity,?Class?targetCls,?String?primaryKey,?Class?defaultServiceClass,?Object[]?args)?throws?Exception?{
          ????????Class?entityClz?=?getEntityClz(targetCls,?defaultServiceClass);
          ????????BaseService?baseService?=?getBaseService(targetCls,?defaultServiceClass);
          ????????for?(Object?arg?:?args)?{
          ????????????if?(arg.getClass().equals(entityClz))?{
          ????????????????if?(StrUtil.isNotBlank(primaryKey))?{
          ????????????????????Field?declaredField?=?entityClz.getDeclaredField(primaryKey);
          ????????????????????declaredField.setAccessible(true);
          ????????????????????Object?primaryKeyValue?=?declaredField.get(arg);
          ????????????????????//if?primary?key?is?not?null?that?means?edit,?otherwise?is?add
          ????????????????????if?(null?!=?primaryKeyValue)?{
          ????????????????????????//query?data?by?primary?key
          ????????????????????????Object?o?=?baseService.queryId(primaryKeyValue);
          ????????????????????????opLogEntity.setBeforeJson(JsonUtils.obj2Json(o));
          ????????????????????}
          ????????????????}
          ????????????????opLogEntity.setAfterJson(JsonUtils.obj2Json(arg));
          ????????????}
          ????????}
          ????}

          ????/**
          ?????*?set?parameter?which?edit?data
          ?????*
          ?????*?@param?opLogEntity?????????entity
          ?????*?@param?targetCls???????????class
          ?????*?@param?primaryKey??????????primaryKey
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@param?args????????????????parameter
          ?????*?@throws?Exception
          ?????*/

          ????private?void?setOpLogEntity(SysMenuOpLogEntity?opLogEntity,?Class?targetCls,?String?primaryKey,?Class?defaultServiceClass,?Object[]?args)?throws?Exception?{
          ????????Map?saveMap?=?autoQueryEditedData(targetCls,?primaryKey,?defaultServiceClass,?args);
          ????????if?(null?!=?saveMap)?{
          ????????????if?(saveMap.containsKey(ASPECT_LOG_OLD_DATA))?{
          ????????????????opLogEntity.setBeforeJson(saveMap.get(ASPECT_LOG_OLD_DATA));
          ????????????}
          ????????????if?(saveMap.containsKey(ASPECT_LOG_NEW_DATA))?{
          ????????????????opLogEntity.setBeforeJson(saveMap.get(ASPECT_LOG_NEW_DATA));
          ????????????}
          ????????}
          ????}

          ????/**
          ?????*?query?data?for?edit?and?after?edit?operate
          ?????*
          ?????*?@param?targetCls???????????class
          ?????*?@param?primaryKey??????????primaryKey
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@param?args????????????????parameter
          ?????*?@return?map?which?data
          ?????*?@throws?Exception
          ?????*/

          ????private?Map?autoQueryEditedData(Class?targetCls,?String?primaryKey,?Class?defaultServiceClass,?Object[]?args)?throws?Exception?{
          ????????if?(StrUtil.isBlank(primaryKey))?{
          ????????????throw?new?Exception();
          ????????}
          ????????Map?map?=?new?HashMap<>(16);
          ????????Class?entityClz?=?getEntityClz(targetCls,?defaultServiceClass);
          ????????BaseService?baseService?=?getBaseService(targetCls,?defaultServiceClass);
          ????????for?(Object?arg?:?args)?{
          ????????????if?(arg.getClass().equals(entityClz))?{
          ????????????????Field?declaredField?=?entityClz.getDeclaredField(primaryKey);
          ????????????????declaredField.setAccessible(true);
          ????????????????Object?primaryKeyValue?=?declaredField.get(arg);
          ????????????????//query?the?data?before?edit
          ????????????????if?(null?!=?primaryKeyValue)?{
          ????????????????????//query?data?by?primary?key
          ????????????????????Object?o?=?baseService.queryId(primaryKeyValue);
          ????????????????????map.put(ASPECT_LOG_OLD_DATA,?JsonUtils.obj2Json(o));
          ????????????????????map.put(ASPECT_LOG_NEW_DATA,?JsonUtils.obj2Json(arg));
          ????????????????????return?map;
          ????????????????}
          ????????????}
          ????????}
          ????????return?null;
          ????}

          ????/**
          ?????*?return?JSON?data?which?add?operate
          ?????*
          ?????*?@param?targetCls???????????class
          ?????*?@param?args????????????????parameter
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return?add?data?which?will?be?added
          ?????*?@throws?Exception
          ?????*/

          ????private?String?genAddData(Class?targetCls,?Class?defaultServiceClass,?Object[]?args)?throws?Exception?{
          ????????List?parameter?=?new?ArrayList<>();
          ????????for?(Object?arg?:?args)?{
          ????????????if?(arg?instanceof?HttpServletRequest)?{
          ????????????}?else?{
          ????????????????parameter.add(arg);
          ????????????}
          ????????}
          ????????return?JsonUtils.obj2Json(parameter);
          ????}

          ????/**
          ?????*?query?delete?data?before?delete?operate
          ?????*
          ?????*?@param?targetCls???????????class
          ?????*?@param?primaryKey??????????primaryKey
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@param?ids?????????????????ids
          ?????*?@return?delete?data?which?will?be?deleted
          ?????*?@throws?Throwable
          ?????*/

          ????private?String?autoQueryDeletedData(Class?targetCls,?String?primaryKey,?Class?defaultServiceClass,?Object[]?ids)?throws?Throwable?{
          ????????if?(StrUtil.isBlank(primaryKey))?{
          ????????????throw?new?OriginException(TipEnum.LOG_ASPECT_PRIMARY_KEY_NOT_EXIST);
          ????????}
          ????????//get?service
          ????????BaseService?baseService?=?getBaseService(targetCls,?defaultServiceClass);
          ????????//get?entity
          ????????Class?entityClz?=?getEntityClz(targetCls,?defaultServiceClass);
          ????????//query?deleted?data?by?primary?key
          ????????Query?query?=?new?Query();
          ????????WhereOperator?whereOperator?=?new?WhereOperator(entityClz);
          ????????Set?set?=?new?HashSet<>(Arrays.asList((Object[])?ids[0]));
          ????????whereOperator.and(primaryKey).in(set.toArray());
          ????????query.addWhereOperator(whereOperator);
          ????????List?list?=?baseService.queryList(query);
          ????????return?JsonUtils.obj2Json(list);
          ????}


          ????/**
          ?????*?return?service?by?targetCls
          ?????*
          ?????*?@param?targetCls???????????current?controller?class
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return?service?instance
          ?????*?@throws?Exception
          ?????*/

          ????private?BaseService?getBaseService(Class?targetCls,?Class?defaultServiceClass)?throws?Exception?{
          ????????//根據類名拿到對應的?service?名稱
          ????????String?serviceName?=?getServiceName(targetCls,?defaultServiceClass);
          ????????BaseService?baseService;
          ????????if?(null?!=?defaultServiceClass)?{
          ????????????baseService?=?(BaseService)?ApplicationContextProvider.getBean(serviceName,?defaultServiceClass);
          ????????}?else?{
          ????????????Class?type?=?targetCls.getDeclaredField(serviceName).getType();
          ????????????baseService?=?(BaseService)?ApplicationContextProvider.getBean(serviceName,?type);
          ????????}
          ????????return?baseService;
          ????}

          ????/**
          ?????*?return?service?name
          ?????*
          ?????*?@param?targetCls???????????current?controller?class
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return?service?name
          ?????*/

          ????private?String?getServiceName(Class?targetCls,?Class?defaultServiceClass)?{
          ????????if?(null?!=?defaultServiceClass?&&?Object.class?!=?defaultServiceClass)?{
          ????????????return?StrUtil.left(defaultServiceClass.getSimpleName(),?1).toLowerCase()?+?defaultServiceClass.getSimpleName().substring(1);
          ????????}
          ????????return?StrUtil.left(targetCls.getSimpleName(),?1).toLowerCase()?+?targetCls.getSimpleName().substring(1).replace("Controller",?"Service");
          ????}


          ????/**
          ?????*?return?entity?class
          ?????*
          ?????*?@param?targetCls???????????current?controller?class
          ?????*?@param?defaultServiceClass?default?service?class
          ?????*?@return?entity?class
          ?????*?@throws?Exception
          ?????*/

          ????private?Class?getEntityClz(Class?targetCls,?Class?defaultServiceClass)?{
          ????????try?{
          ????????????Class?type;
          ????????????if?(null?!=?defaultServiceClass?&&?Object.class?!=?defaultServiceClass)?{
          ????????????????type?=?defaultServiceClass;
          ????????????}?else?{
          ????????????????type?=?targetCls.getDeclaredField(getServiceName(targetCls,?null)).getType();
          ????????????}
          ????????????String?entityName?=?type.getName().replace("service",?"entity").replace("Service",?"Entity");
          ????????????Class?entityClz?=?Class.forName(entityName);
          ????????????return?entityClz;
          ????????}?catch?(Exception?e)?{
          ????????????log.error("獲取?class?失敗");
          ????????}
          ????????return?null;
          ????}


          ????/**
          ?????*?require?path
          ?????*
          ?????*?@param?targetMethod?target?method
          ?????*?@param?defaultPath??default?require?path
          ?????*?@return?require?path
          ?????*/

          ????private?String?getPath(Method?targetMethod,?String?defaultPath)?{
          ????????String?path?=?defaultPath;
          ????????PostMapping?postMapping?=?targetMethod.getAnnotation(PostMapping.class);
          ????????GetMapping?getMapping?=?targetMethod.getAnnotation(GetMapping.class);
          ????????RequestMapping?requestMapping?=?targetMethod.getAnnotation(RequestMapping.class);
          ????????if?(null?!=?postMapping)?{
          ????????????path?=?postMapping.value()[0];
          ????????}?else?if?(null?!=?getMapping)?{
          ????????????path?=?getMapping.value()[0];
          ????????}?else?if?(null?!=?requestMapping)?{
          ????????????path?=?requestMapping.value()[0];
          ????????}
          ????????return?path;
          ????}

          }


          上面的代碼中我們定義了一個切面指定需要攔截的包名和注解,因為涉及到很多業(yè)務相關的代碼,所以不能完整的提供出來,但是整個思路就是這樣的,在每種操作類型前后將需要記錄的數據查詢出來進行記錄。代碼很長主要是用來獲取相應的參數值的,大家使用的時候可以根據自己的需要進行取舍。比如在新增操作的時候,我們將新增的數據進行記錄下來;編輯的時候將編輯前的數據查詢出來和編輯后的數據一起保存起來,刪除也是一樣的,在刪除前將數據查詢出來保存到日志表中。

          同樣導出和下載都會記錄相應信息,整個操作類型的代碼如下:

          package?com.api.annotation;

          /**
          ?*?

          ?*?Function:

          ?*?Author:@author 子悠

          ?*?Date:2020-11-17 22:11

          ?*?Desc:

          ?*/
          public?interface?OperationType?{
          ????/**
          ?????*?新增
          ?????**/
          ????String?ADD?=?"add";
          ????/**
          ?????*?刪除
          ?????**/
          ????String?DELETE?=?"delete";
          ????/**
          ?????*?使用實體參數修改
          ?????**/
          ????String?EDIT?=?"edit";
          ????/**
          ?????*?查詢
          ?????**/
          ????String?SELECT?=?"select";

          ????/**
          ?????*?新增和修改的保存方法,使用此類型時必須配置主鍵字段名稱
          ?????**/
          ????String?SAVE?=?"save";

          ????/**
          ?????*?導出
          ?????**/
          ????String?EXPORT?=?"export";

          ????/**
          ?????*?下載
          ?????**/
          ????String?DOWNLOAD?=?"download";

          }

          后續(xù)在使用的時候只需要在需要的方法上加上注解,填上相應的參數即可@OperationLog(desc = "查詢單條記錄", path = "/data")

          總結

          注解一個我們天天再用的東西,雖然不難,但是我們卻很少自己去寫注解的代碼,通過這篇文章能給大家展示一下注解的使用邏輯,希望對大家有幫助。Spring 中的各種注解本質上也是這種邏輯都需要定義使用和解析。很多時候我們可以通過自定義注解去解決很多場景,比如日志,緩存等。


          喜歡就三連


          關注 Stephen,一起學習,一起成長。


          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                    福利视频久草 | 日本激情视频在线播放 | 干批视频免费 | 99在线观看免费视频 | 国产麻豆黄色有码 |