精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

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

[[353180]]

本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲 。轉載本文請聯系Java極客技術公眾號。 

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

Java 元注解

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

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

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

定義注解

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

  1. package com.api.annotation; 
  2.  
  3. import java.lang.annotation.*; 
  4.  
  5. /** 
  6.  * <br> 
  7.  * <b>Function:</b><br> 
  8.  * <b>Author:</b>@author 子悠<br> 
  9.  * <b>Date:</b>2020-11-17 22:10<br> 
  10.  * <b>Desc:</b>用于記錄操作日志<br> 
  11.  */ 
  12. @Target({ElementType.METHOD}) 
  13. @Retention(RetentionPolicy.RUNTIME) 
  14. @Documented 
  15. public @interface OperationLog { 
  16.  
  17.     /** 
  18.      * 操作類型 
  19.      * 
  20.      * @return 
  21.      */ 
  22.     String type() default OperationType.SELECT
  23.  
  24.     /** 
  25.      * 操作說明 
  26.      * 
  27.      * @return 
  28.      */ 
  29.     String desc() default ""
  30.  
  31.     /** 
  32.      * 請求路徑 
  33.      * 
  34.      * @return 
  35.      */ 
  36.     String path() default ""
  37.  
  38.     /** 
  39.      * 是否記錄日志,默認是 
  40.      * 
  41.      * @return 
  42.      */ 
  43.     boolean write() default true
  44.  
  45.     /** 
  46.      * 是否需要登錄信息 
  47.      * 
  48.      * @return 
  49.      */ 
  50.     boolean auth() default true
  51.    /** 
  52.      * 當 type 為 save 時必須 
  53.      * 
  54.      * @return 
  55.      */ 
  56.     String primaryKey() default ""
  57.  
  58.     /** 
  59.      * 對應 service 的 Class 
  60.      * 
  61.      * @return 
  62.      */ 
  63.     Class<?> defaultServiceClass() default Object.class; 

說明

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

使用

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

  1. package com.api.config; 
  2.  
  3. import lombok.extern.slf4j.Slf4j; 
  4. import org.aspectj.lang.ProceedingJoinPoint; 
  5. import org.aspectj.lang.annotation.Around; 
  6. import org.aspectj.lang.annotation.Aspect; 
  7. import org.aspectj.lang.annotation.Pointcut; 
  8. import org.aspectj.lang.reflect.MethodSignature; 
  9. import org.springframework.beans.factory.annotation.Autowired; 
  10. import org.springframework.core.annotation.Order
  11. import org.springframework.stereotype.Component; 
  12. import org.springframework.web.bind.annotation.GetMapping; 
  13. import org.springframework.web.bind.annotation.PostMapping; 
  14. import org.springframework.web.bind.annotation.RequestMapping; 
  15.  
  16. import javax.servlet.http.HttpServletRequest; 
  17. import java.lang.reflect.Field; 
  18. import java.lang.reflect.Method; 
  19. import java.util.*; 
  20.  
  21. /** 
  22.  * <br> 
  23.  * <b>Function:</b><br> 
  24.  * <b>Author:</b>@author 子悠<br> 
  25.  * <b>Date:</b>2020-11-17 14:40<br> 
  26.  * <b>Desc:</b>aspect for operation log<br> 
  27.  */ 
  28. @Aspect 
  29. @Component 
  30. @Order(-5) 
  31. @Slf4j 
  32. public class LogAspect { 
  33.     /** 
  34.      * Pointcut for methods which need to record operate log 
  35.      */ 
  36.     @Pointcut("within(com.xx.yy.controller..*) && @annotation(com.api.annotation.OperationLog)"
  37.     public void logAspect() { 
  38.     } 
  39.  
  40.     /** 
  41.      * record log for Admin and DSP 
  42.      * 
  43.      * @param joinPoint parameter 
  44.      * @return result 
  45.      * @throws Throwable 
  46.      */ 
  47.     @Around("logAspect()"
  48.     public Object around(ProceedingJoinPoint joinPoint) throws Throwable { 
  49.         Object proceed = null
  50.         String classType = joinPoint.getTarget().getClass().getName(); 
  51.         Class<?> targetCls = Class.forName(classType); 
  52.         MethodSignature ms = (MethodSignature) joinPoint.getSignature(); 
  53.         Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes()); 
  54.         OperationLog operation = targetMethod.getAnnotation(OperationLog.class); 
  55.         if (null != operation && operation.write()) { 
  56.             SysMenuOpLogEntity opLogEntity = new SysMenuOpLogEntity(); 
  57.             StringBuilder change = new StringBuilder(); 
  58.             if (StrUtil.isNotBlank(operation.type())) { 
  59.                 switch (operation.type()) { 
  60.                     case OperationType.ADD
  61.                         proceed = joinPoint.proceed(); 
  62.                         String addString = genAddData(targetCls, operation.defaultServiceClass(), joinPoint.getArgs()); 
  63.                         opLogEntity.setAfterJson(addString); 
  64.                         change.append(OperationType.ADD); 
  65.                         break; 
  66.                     case OperationType.DELETE
  67.                         String deleteString = autoQueryDeletedData(targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs()); 
  68.                         opLogEntity.setBeforeJson(deleteString); 
  69.                         change.append(OperationType.DELETE); 
  70.                         proceed = joinPoint.proceed(); 
  71.                         break; 
  72.                     case OperationType.EDIT: 
  73.                         change.append(OperationType.EDIT); 
  74.                         setOpLogEntity(opLogEntity, targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs()); 
  75.                         proceed = joinPoint.proceed(); 
  76.                         break; 
  77.                     case OperationType.SELECT
  78.                         opLogEntity.setBeforeJson(getQueryString(targetCls, operation.defaultServiceClass(), joinPoint.getArgs())); 
  79.                         change.append(operation.type()); 
  80.                         proceed = joinPoint.proceed(); 
  81.                         break; 
  82.                     case OperationType.SAVE: 
  83.                         savedDataOpLog(opLogEntity, targetCls, operation.primaryKey(), operation.defaultServiceClass(), joinPoint.getArgs()); 
  84.                         change.append(operation.type()); 
  85.                         proceed = joinPoint.proceed(); 
  86.                         break; 
  87.                     case OperationType.EXPORT: 
  88.                     case OperationType.DOWNLOAD: 
  89.                         change.append(operation.type()); 
  90.                         proceed = joinPoint.proceed(); 
  91.                         break; 
  92.                     default
  93.                 } 
  94.                 opLogEntity.setExecType(operation.type()); 
  95.             } 
  96.             StringBuilder changing = new StringBuilder(); 
  97.             if (StrUtil.isNotBlank(opLogEntity.getExecType())) { 
  98.                 if (operation.auth()) { 
  99.                     LoginUserVO loginUser = getLoginUser(); 
  100.                     if (null != loginUser) { 
  101.                         opLogEntity.setUserId(loginUser.getUserId()); 
  102.                         opLogEntity.setUserName(loginUser.getUserName()); 
  103.                         changing.append(loginUser.getUserName()).append("-"); 
  104.                     } else { 
  105.                         log.error("用戶未登錄"); 
  106.                     } 
  107.                 } 
  108.                 opLogEntity.setCreateTime(DateUtils.getCurDate()); 
  109.                 opLogEntity.setRemark(getOperateMenuName(targetMethod, operation.desc())); 
  110.                 opLogEntity.setPath(getPath(targetMethod, targetMethod.getName())); 
  111.                 opLogEntity.setChanging(changing.append(change).toString()); 
  112.                 menuOpLogService.save(opLogEntity); 
  113.             } 
  114.         } 
  115.         return proceed; 
  116.     } 
  117.  
  118.     /** 
  119.      * query data by userId 
  120.      * 
  121.      * @param targetCls           class 
  122.      * @param defaultServiceClass default service class 
  123.      * @return 
  124.      * @throws Exception 
  125.      */ 
  126.     private String queryByCurrentUserId(Class<?> targetCls, Class<?> defaultServiceClass) throws Exception { 
  127.         BaseService baseService = getBaseService(targetCls, defaultServiceClass); 
  128.         LoginUserVO loginUser = dspBaseService.getLoginUser(); 
  129.         if (null != loginUser) { 
  130.             Object o = baseService.queryId(loginUser.getUserId()); 
  131.             return JsonUtils.obj2Json(o); 
  132.         } 
  133.         return null
  134.     } 
  135.  
  136.     /** 
  137.      * return query parameter 
  138.      * 
  139.      * @param targetCls           class 
  140.      * @param args                parameter 
  141.      * @param defaultServiceClass default service class 
  142.      * @return 
  143.      * @throws Exception 
  144.      */ 
  145.     private String getQueryString(Class<?> targetCls, Class<?> defaultServiceClass, Object[] args) { 
  146.         if (args.length > 0) { 
  147.             Class<?> entityClz = getEntityClz(targetCls, defaultServiceClass); 
  148.             for (Object arg : args) { 
  149.                 if (arg.getClass().equals(entityClz) || arg instanceof BaseModel) { 
  150.                     return JsonUtils.obj2Json(arg); 
  151.                 } 
  152.             } 
  153.         } 
  154.         return null
  155.     } 
  156.  
  157.     /** 
  158.      * save record log while OperatorType is SAVE 
  159.      * 
  160.      * @param opLogEntity         entity 
  161.      * @param targetCls           class 
  162.      * @param primaryKey          primaryKey 
  163.      * @param defaultServiceClass default service class 
  164.      * @param args                parameter 
  165.      * @throws Exception 
  166.      */ 
  167.     private void savedDataOpLog(SysMenuOpLogEntity opLogEntity, Class<?> targetCls, String primaryKey, Class<?> defaultServiceClass, Object[] args) throws Exception { 
  168.         Class<?> entityClz = getEntityClz(targetCls, defaultServiceClass); 
  169.         BaseService baseService = getBaseService(targetCls, defaultServiceClass); 
  170.         for (Object arg : args) { 
  171.             if (arg.getClass().equals(entityClz)) { 
  172.                 if (StrUtil.isNotBlank(primaryKey)) { 
  173.                     Field declaredField = entityClz.getDeclaredField(primaryKey); 
  174.                     declaredField.setAccessible(true); 
  175.                     Object primaryKeyValue = declaredField.get(arg); 
  176.                     //if primary key is not null that means edit, otherwise is add 
  177.                     if (null != primaryKeyValue) { 
  178.                         //query data by primary key 
  179.                         Object o = baseService.queryId(primaryKeyValue); 
  180.                         opLogEntity.setBeforeJson(JsonUtils.obj2Json(o)); 
  181.                     } 
  182.                 } 
  183.                 opLogEntity.setAfterJson(JsonUtils.obj2Json(arg)); 
  184.             } 
  185.         } 
  186.     } 
  187.  
  188.     /** 
  189.      * set parameter which edit data 
  190.      * 
  191.      * @param opLogEntity         entity 
  192.      * @param targetCls           class 
  193.      * @param primaryKey          primaryKey 
  194.      * @param defaultServiceClass default service class 
  195.      * @param args                parameter 
  196.      * @throws Exception 
  197.      */ 
  198.     private void setOpLogEntity(SysMenuOpLogEntity opLogEntity, Class<?> targetCls, String primaryKey, Class<?> defaultServiceClass, Object[] args) throws Exception { 
  199.         Map<String, String> saveMap = autoQueryEditedData(targetCls, primaryKey, defaultServiceClass, args); 
  200.         if (null != saveMap) { 
  201.             if (saveMap.containsKey(ASPECT_LOG_OLD_DATA)) { 
  202.                 opLogEntity.setBeforeJson(saveMap.get(ASPECT_LOG_OLD_DATA)); 
  203.             } 
  204.             if (saveMap.containsKey(ASPECT_LOG_NEW_DATA)) { 
  205.                 opLogEntity.setBeforeJson(saveMap.get(ASPECT_LOG_NEW_DATA)); 
  206.             } 
  207.         } 
  208.     } 
  209.  
  210.     /** 
  211.      * query data for edit and after edit operate 
  212.      * 
  213.      * @param targetCls           class 
  214.      * @param primaryKey          primaryKey 
  215.      * @param defaultServiceClass default service class 
  216.      * @param args                parameter 
  217.      * @return map which data 
  218.      * @throws Exception 
  219.      */ 
  220.     private Map<String, String> autoQueryEditedData(Class<?> targetCls, String primaryKey, Class<?> defaultServiceClass, Object[] args) throws Exception { 
  221.         if (StrUtil.isBlank(primaryKey)) { 
  222.             throw new Exception(); 
  223.         } 
  224.         Map<String, String> map = new HashMap<>(16); 
  225.         Class<?> entityClz = getEntityClz(targetCls, defaultServiceClass); 
  226.         BaseService baseService = getBaseService(targetCls, defaultServiceClass); 
  227.         for (Object arg : args) { 
  228.             if (arg.getClass().equals(entityClz)) { 
  229.                 Field declaredField = entityClz.getDeclaredField(primaryKey); 
  230.                 declaredField.setAccessible(true); 
  231.                 Object primaryKeyValue = declaredField.get(arg); 
  232.                 //query the data before edit 
  233.                 if (null != primaryKeyValue) { 
  234.                     //query data by primary key 
  235.                     Object o = baseService.queryId(primaryKeyValue); 
  236.                     map.put(ASPECT_LOG_OLD_DATA, JsonUtils.obj2Json(o)); 
  237.                     map.put(ASPECT_LOG_NEW_DATA, JsonUtils.obj2Json(arg)); 
  238.                     return map; 
  239.                 } 
  240.             } 
  241.         } 
  242.         return null
  243.     } 
  244.  
  245.     /** 
  246.      * return JSON data which add operate 
  247.      * 
  248.      * @param targetCls           class 
  249.      * @param args                parameter 
  250.      * @param defaultServiceClass default service class 
  251.      * @return add data which will be added 
  252.      * @throws Exception 
  253.      */ 
  254.     private String genAddData(Class<?> targetCls, Class<?> defaultServiceClass, Object[] args) throws Exception { 
  255.         List<Object> parameter = new ArrayList<>(); 
  256.         for (Object arg : args) { 
  257.             if (arg instanceof HttpServletRequest) { 
  258.             } else { 
  259.                 parameter.add(arg); 
  260.             } 
  261.         } 
  262.         return JsonUtils.obj2Json(parameter); 
  263.     } 
  264.  
  265.     /** 
  266.      * query delete data before delete operate 
  267.      * 
  268.      * @param targetCls           class 
  269.      * @param primaryKey          primaryKey 
  270.      * @param defaultServiceClass default service class 
  271.      * @param ids                 ids 
  272.      * @return delete data which will be deleted 
  273.      * @throws Throwable 
  274.      */ 
  275.     private String autoQueryDeletedData(Class<?> targetCls, String primaryKey, Class<?> defaultServiceClass, Object[] ids) throws Throwable { 
  276.         if (StrUtil.isBlank(primaryKey)) { 
  277.             throw new OriginException(TipEnum.LOG_ASPECT_PRIMARY_KEY_NOT_EXIST); 
  278.         } 
  279.         //get service 
  280.         BaseService baseService = getBaseService(targetCls, defaultServiceClass); 
  281.         //get entity 
  282.         Class<?> entityClz = getEntityClz(targetCls, defaultServiceClass); 
  283.         //query deleted data by primary key 
  284.         Query query = new Query(); 
  285.         WhereOperator whereOperator = new WhereOperator(entityClz); 
  286.         Set<Object> set = new HashSet<>(Arrays.asList((Object[]) ids[0])); 
  287.         whereOperator.and(primaryKey).in(set.toArray()); 
  288.         query.addWhereOperator(whereOperator); 
  289.         List list = baseService.queryList(query); 
  290.         return JsonUtils.obj2Json(list); 
  291.     } 
  292.  
  293.  
  294.     /** 
  295.      * return service by targetCls 
  296.      * 
  297.      * @param targetCls           current controller class 
  298.      * @param defaultServiceClass default service class 
  299.      * @return service instance 
  300.      * @throws Exception 
  301.      */ 
  302.     private BaseService getBaseService(Class<?> targetCls, Class<?> defaultServiceClass) throws Exception { 
  303.         //根據類名拿到對應的 service 名稱 
  304.         String serviceName = getServiceName(targetCls, defaultServiceClass); 
  305.         BaseService baseService; 
  306.         if (null != defaultServiceClass) { 
  307.             baseService = (BaseService) ApplicationContextProvider.getBean(serviceName, defaultServiceClass); 
  308.         } else { 
  309.             Class<?> type = targetCls.getDeclaredField(serviceName).getType(); 
  310.             baseService = (BaseService) ApplicationContextProvider.getBean(serviceName, type); 
  311.         } 
  312.         return baseService; 
  313.     } 
  314.  
  315.     /** 
  316.      * return service name 
  317.      * 
  318.      * @param targetCls           current controller class 
  319.      * @param defaultServiceClass default service class 
  320.      * @return service name 
  321.      */ 
  322.     private String getServiceName(Class<?> targetCls, Class<?> defaultServiceClass) { 
  323.         if (null != defaultServiceClass && Object.class != defaultServiceClass) { 
  324.             return StrUtil.left(defaultServiceClass.getSimpleName(), 1).toLowerCase() + defaultServiceClass.getSimpleName().substring(1); 
  325.         } 
  326.         return StrUtil.left(targetCls.getSimpleName(), 1).toLowerCase() + targetCls.getSimpleName().substring(1).replace("Controller""Service"); 
  327.     } 
  328.  
  329.  
  330.     /** 
  331.      * return entity class 
  332.      * 
  333.      * @param targetCls           current controller class 
  334.      * @param defaultServiceClass default service class 
  335.      * @return entity class 
  336.      * @throws Exception 
  337.      */ 
  338.     private Class<?> getEntityClz(Class<?> targetCls, Class<?> defaultServiceClass) { 
  339.         try { 
  340.             Class<?> type; 
  341.             if (null != defaultServiceClass && Object.class != defaultServiceClass) { 
  342.                 type = defaultServiceClass; 
  343.             } else { 
  344.                 type = targetCls.getDeclaredField(getServiceName(targetCls, null)).getType(); 
  345.             } 
  346.             String entityName = type.getName().replace("service""entity").replace("Service""Entity"); 
  347.             Class<?> entityClz = Class.forName(entityName); 
  348.             return entityClz; 
  349.         } catch (Exception e) { 
  350.             log.error("獲取 class 失敗"); 
  351.         } 
  352.         return null
  353.     } 
  354.  
  355.  
  356.     /** 
  357.      * require path 
  358.      * 
  359.      * @param targetMethod target method 
  360.      * @param defaultPath  default require path 
  361.      * @return require path 
  362.      */ 
  363.     private String getPath(Method targetMethod, String defaultPath) { 
  364.         String path = defaultPath; 
  365.         PostMapping postMapping = targetMethod.getAnnotation(PostMapping.class); 
  366.         GetMapping getMapping = targetMethod.getAnnotation(GetMapping.class); 
  367.         RequestMapping requestMapping = targetMethod.getAnnotation(RequestMapping.class); 
  368.         if (null != postMapping) { 
  369.             path = postMapping.value()[0]; 
  370.         } else if (null != getMapping) { 
  371.             path = getMapping.value()[0]; 
  372.         } else if (null != requestMapping) { 
  373.             path = requestMapping.value()[0]; 
  374.         } 
  375.         return path; 
  376.     } 
  377.  

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

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

  1. package com.api.annotation; 
  2.  
  3. /** 
  4.  * <br> 
  5.  * <b>Function:</b><br> 
  6.  * <b>Author:</b>@author 子悠<br> 
  7.  * <b>Date:</b>2020-11-17 22:11<br> 
  8.  * <b>Desc:</b>無<br> 
  9.  */ 
  10. public interface OperationType { 
  11.     /** 
  12.      * 新增 
  13.      **/ 
  14.     String ADD = "add"
  15.     /** 
  16.      * 刪除 
  17.      **/ 
  18.     String DELETE = "delete"
  19.     /** 
  20.      * 使用實體參數修改 
  21.      **/ 
  22.     String EDIT = "edit"
  23.     /** 
  24.      * 查詢 
  25.      **/ 
  26.     String SELECT = "select"
  27.  
  28.     /** 
  29.      * 新增和修改的保存方法,使用此類型時必須配置主鍵字段名稱 
  30.      **/ 
  31.     String SAVE = "save"
  32.  
  33.     /** 
  34.      * 導出 
  35.      **/ 
  36.     String EXPORT = "export"
  37.  
  38.     /** 
  39.      * 下載 
  40.      **/ 
  41.     String DOWNLOAD = "download"
  42.  

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

總結

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

 

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2024-01-08 08:27:11

注解Bean代理

2023-06-08 11:57:15

Matter協議家庭智能

2024-08-22 08:17:55

C#工具循環

2019-09-02 08:39:02

路由器RAM內存

2017-12-07 15:00:00

筆記本OLED屏幕

2023-11-01 13:48:00

反射java

2023-05-29 08:11:42

@Value注解Bean

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系統數據庫

2025-01-03 08:09:15

2022-01-17 07:32:34

Java參數方法

2021-11-09 09:48:13

Logging python模塊

2021-01-15 07:44:21

SQL注入攻擊黑客

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS網絡協議TCP

2023-03-16 10:49:55

2019-09-16 08:40:42

2023-05-10 11:07:18

2021-11-26 08:07:16

MySQL SQL 語句數據庫

2018-01-06 10:38:51

Ping抓包 ICMP協議
點贊
收藏

51CTO技術棧公眾號

**精品中文字幕一区二区三区| 国产主播福利在线| 激情久久五月| 精品五月天久久| 亚洲欧美国产日韩综合| 日本乱理伦在线| 91丝袜美腿高跟国产极品老师| 日本一区二区三区四区视频| 永久免费看片直接| 国产精品久久久久久久久久白浆| 在线免费观看视频一区| 国产手机视频在线观看| 亚洲av成人精品毛片| 久久精品99国产精品日本| 欧美精品电影免费在线观看| 精品人妻中文无码av在线| 超碰成人福利| 欧美日韩你懂得| 日本韩国欧美在线观看| a黄色在线观看| 播五月开心婷婷综合| 国产精品日日做人人爱| 久久精品视频日本| 99精品美女| 亚洲精品一区在线观看香蕉| 香蕉视频色在线观看| 欧美最新精品| 欧美日韩国产一区中文午夜| 欧美性视频在线播放| 九九热视频在线观看| 成人性视频网站| 成人黄色激情网| 国产精品第六页| 亚洲视频1区| 欧美日韩国产999| 日韩欧美国产成人精品免费| 欧美人与拘性视交免费看| 日韩精品资源二区在线| 欧美第一页浮力影院| 一级毛片久久久| 精品高清美女精品国产区| 欧洲精品视频在线| 国产激情在线视频| 国产精品国产三级国产aⅴ原创| 国产精品亚洲综合| 亚洲国产精彩视频| 国产裸体歌舞团一区二区| 国产精品爽黄69天堂a| 在线免费观看国产精品| 久久精品导航| 欧美专区日韩视频| 国产成人亚洲精品自产在线 | 日韩精品久久理论片| 97免费在线视频| 日本三级中文字幕| 亚洲国产美女| 97精品国产aⅴ7777| 国产一级视频在线观看| 伊人成人在线| 2024亚洲男人天堂| 亚洲 日本 欧美 中文幕| 老司机一区二区三区| 欧美中文字幕视频| av手机天堂网| 久久99精品一区二区三区三区| 国产精品一区二区三区在线播放 | 国产h片在线观看| 亚洲3atv精品一区二区三区| 免费视频爱爱太爽了| 丰满的护士2在线观看高清| 午夜私人影院久久久久| 免费毛片小视频| 国精产品一区二区三区有限公司| 欧美午夜影院一区| www.午夜av| 丁香5月婷婷久久| 日韩国产在线看| 黄色三级生活片| 久久久久久久久久久久久久| 久久999免费视频| 日本网站在线免费观看| 午夜亚洲精品| 成人国产精品av| 好吊色视频一区二区| 99re亚洲国产精品| 亚洲国产另类久久久精品极度| 毛片av在线| 亚洲午夜在线视频| 男人天堂999| 亚洲精品成人一区| 亚洲精品久久久久久久久久久久久| 日本xxx在线播放| 日本不卡免费一区| 久久久久久国产精品美女| 精品人妻无码一区二区性色| 久久精品国产免费| 国产视色精品亚洲一区二区| 国产日产精品久久久久久婷婷| 亚洲人成亚洲人成在线观看图片| 欧美成人高潮一二区在线看| 电影一区电影二区| 欧美精品一区二区三区在线 | 欧州一区二区| 欧美激情2020午夜免费观看| 波多野结衣在线观看一区| 高清视频一区二区| 亚洲精品中文字幕在线| 123区在线| 56国语精品自产拍在线观看| v8888av| 午夜激情久久| 1769国产精品| 一本色道久久综合无码人妻| 99久久er热在这里只有精品15| 在线一区日本视频| 在线高清av| 欧美成人精品高清在线播放 | 亚洲欧洲中文日韩久久av乱码| www国产精品内射老熟女| 国产精品成人**免费视频| 亚洲欧洲国产伦综合| 青草草在线视频| 久久99精品国产麻豆婷婷| 蜜桃免费一区二区三区| 黄色在线观看视频网站| 8x福利精品第一导航| 亚洲狠狠婷婷综合久久久久图片| 亚洲午夜黄色| 91精品啪在线观看麻豆免费| 韩国中文字幕2020精品| 精品久久久久国产| 日本50路肥熟bbw| 欧美精品观看| 亚洲专区国产精品| a级影片在线观看| 884aa四虎影成人精品一区| 亚洲高潮女人毛茸茸| 老司机精品福利视频| 激情欧美一区二区三区中文字幕| 日本精品600av| 欧美成人免费网站| 国产午夜小视频| 成人免费电影视频| 91精品国产91久久久久麻豆 主演| 国产乱码精品一区二区三区亚洲人| 在线观看精品自拍私拍| 波多野结衣家庭主妇| 久久久精品影视| 国产偷人视频免费| 欧美日韩国产高清电影| 国产精品久久久久久搜索 | 久久国产精品久久久久| 91九色蝌蚪91por成人| 国产精品日产欧美久久久久| 国产精品一区二区羞羞答答| 欧洲乱码伦视频免费| 国产精品普通话| 中文字幕在线免费| 51精品国自产在线| 国产suv一区二区三区| 国产另类ts人妖一区二区| 妞干网这里只有精品| 欧美二区观看| 久久久视频精品| 天天干视频在线| 91搞黄在线观看| www.xx日本| 国产.欧美.日韩| 久久精品国产精品亚洲色婷婷| 老牛影视av一区二区在线观看| 欧美一级成年大片在线观看| 国产高清视频在线观看| 777午夜精品视频在线播放| 日本少妇高清视频| av亚洲精华国产精华精| www.日本xxxx| 亚洲欧美日韩高清在线| 国产精品一区二区在线观看| 亚洲精华液一区二区三区| 中文字幕av一区二区| 精品国产av一区二区三区| 性久久久久久久久| 老司机福利在线观看| 国产91高潮流白浆在线麻豆| 欧美精品色婷婷五月综合| 色欧美自拍视频| 99在线热播| 国产精品久久亚洲不卡| 久久69精品久久久久久国产越南| 污污视频在线免费看| 欧美日韩亚洲综合在线 欧美亚洲特黄一级 | 免费看黄在线看| 国产精品一区二区av日韩在线| 成人精品一区二区三区| 亚洲风情在线资源| 久久久精品电影| 日本天堂在线| 91精品国产综合久久久久久久久久| 久久一级黄色片| 中文一区在线播放| 免费日本黄色网址| 激情综合网激情| 久久精品免费一区二区| 亚洲字幕久久| 亚洲成人第一| 精品国产乱子伦一区二区| 国产女精品视频网站免费| 韩国成人二区| 欧美高清不卡在线| 免费在线毛片网站| 亚洲欧美第一页| 丰满人妻妇伦又伦精品国产| 欧美三日本三级三级在线播放| 日本在线观看中文字幕| 亚洲欧美日韩一区二区三区在线观看| 久久久久麻豆v国产精华液好用吗| 黑人精品欧美一区二区蜜桃| 黄色免费视频大全| 黄色精品一区| 青青草原国产免费| 日韩精品91| 欧美精品一区二区三区在线看午夜 | 黄色a一级视频| 成人午夜免费av| 国产精品999.| 麻豆精品一二三| 五月婷婷激情久久| 久久尤物视频| wwwxxx黄色片| 亚洲一区二区三区四区五区午夜| 国产精品igao激情视频| 午夜免费一区| 青青草综合视频| 欧美1区2区| 大桥未久一区二区三区| 99久久国产综合精品成人影院| 五月天丁香综合久久国产| 欧美精品momsxxx| 日本一区二区三区www| 小说区图片区色综合区| 激情欧美一区二区三区中文字幕| 豆花视频一区二区| 国产日韩欧美一区二区| 国产精品45p| 国内不卡一区二区三区| 风间由美性色一区二区三区四区| 国产精品swag| 欧美日韩看看2015永久免费 | 日本中文字幕久久看| 亚洲最新无码中文字幕久久| 欧洲成人免费视频| 日韩中文影院| 国产有码一区二区| 在线成人免费| 国产精品jizz视频| 亚洲ab电影| 视频一区国产精品| 希岛爱理av一区二区三区| 成人黄色片免费| 亚洲国产高清一区| 女人扒开屁股爽桶30分钟| 香蕉精品999视频一区二区| 免费大片在线观看| 久久国产精品第一页| 91插插插影院| 成人av先锋影音| 亚洲自拍偷拍一区二区| 国产欧美日韩综合精品一区二区| 激情五月深爱五月| 亚洲精品你懂的| 黄页网站免费观看| 日韩欧美a级成人黄色| 国产精品51麻豆cm传媒| 7777精品伊人久久久大香线蕉完整版| 国产婷婷一区二区三区久久| 精品黑人一区二区三区久久 | 久久女同精品一区二区| 日本人亚洲人jjzzjjz| 亚洲丝袜另类动漫二区| 国产污视频在线观看| 一本大道久久a久久综合| 亚洲天堂aaa| 精品精品欲导航| 国产无套粉嫩白浆在线2022年| 久久午夜a级毛片| 美女扒开腿让男人桶爽久久软| 国产国语videosex另类| 国产美女亚洲精品7777| 精选一区二区三区四区五区| 精品国产一区二区三区久久久蜜臀 | 欧美日韩国产一区二区| 亚洲综合网av| 亚洲精品国精品久久99热| 毛片av在线| 4444欧美成人kkkk| 免费看日产一区二区三区| 欧美精品亚洲| 亚洲国产高清一区二区三区| 五月天婷婷亚洲| 91麻豆视频网站| 久久久久久av无码免费网站| 91久久精品日日躁夜夜躁欧美| www.久久伊人| 色yeye香蕉凹凸一区二区av| av中文字幕在线观看第一页| 国产精品永久免费| 香蕉久久夜色精品国产更新时间| 国内精品国产三级国产99| 日韩**一区毛片| 亚洲国产精品成人综合久久久| 国产精品久久久久婷婷二区次| 国产原创视频在线| 日韩欧美精品在线视频| 在线观看av的网站| 日本成人精品在线| 久久亚洲道色| 高清无码一区二区在线观看吞精| 日韩电影一区二区三区四区| 日本黄色免费观看| 亚洲伊人色欲综合网| 97免费观看视频| 中文字幕在线日韩| 日本综合久久| 欧美日韩国产高清视频| 99精品久久| 日本美女视频网站| 一区二区三区国产精品| 国产美女免费看| 日韩亚洲精品电影| 国产国产一区| 日本一区视频在线| 性欧美精品高清| 女~淫辱の触手3d动漫| 亚洲v精品v日韩v欧美v专区| 亚洲成人中文字幕在线| 蜜臀久久99精品久久久无需会员| 欧美高清影院| 亚洲一区二区不卡视频| 琪琪一区二区三区| 在线观看亚洲大片短视频| 在线中文字幕不卡| 国产一区二区三区福利| 日本高清视频一区| 国产一区二区三区日韩精品| 无码人妻丰满熟妇区毛片| 91色九色蝌蚪| 国产精品久久久久久人| 亚洲人高潮女人毛茸茸| 神马久久资源| 亚洲欧洲精品在线| 精品影视av免费| 91aaa在线观看| 日韩欧美在线一区二区三区| 影院在线观看全集免费观看| 亚洲综合小说区| 亚洲午夜久久久久久尤物| 日韩综合第一页| 欧美日韩国产丝袜另类| 国产九九在线| 成人动漫网站在线观看| 午夜精品久久| 91av在线免费| 欧美在线观看视频一区二区 | 欧美男gay| 91高清国产视频| 一区二区三区成人| 五月天丁香视频| 国产精品久久久久久av| 亚洲国产不卡| 星空大象在线观看免费播放| 一本一道久久a久久精品综合蜜臀| 国产高清在线| 91在线短视频| 亚洲综合不卡| 成人黄色短视频| 亚洲精品一线二线三线| **欧美日韩在线观看| 一级做a爰片久久| 成人国产精品免费观看动漫| 国产美女激情视频| 久久精品视频网站| 日本国产精品| 污视频网址在线观看| 午夜精品视频在线观看| 91最新在线| 国产日韩精品推荐| 久久99久久久欧美国产| 久久久久久久久99| 亚洲天堂av在线免费观看| 国产日本亚洲| 欧美少妇性生活视频| 亚洲另类在线视频| 国产视频二区在线观看| 亚洲自拍偷拍网址| 老**午夜毛片一区二区三区| 免费人成年激情视频在线观看| 亚洲美腿欧美激情另类| 日韩一级淫片| 男生操女生视频在线观看| 精品久久久久久久久久国产|