成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

天天都在使用的 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內存

2022-07-26 00:00:22

HTAP系統數據庫

2014-04-17 16:42:03

DevOps

2017-12-07 15:00:00

筆記本OLED屏幕

2023-11-01 13:48:00

反射java

2023-05-29 08:11:42

@Value注解Bean

2025-01-03 08:09:15

2022-01-17 07:32:34

Java參數方法

2014-11-28 10:31:07

Hybrid APP

2023-03-16 10:49:55

2020-02-27 10:49:26

HTTPS網絡協議TCP

2019-09-16 08:40:42

2021-01-15 07:44:21

SQL注入攻擊黑客

2021-11-09 09:48:13

Logging python模塊

2023-05-10 11:07:18

2018-01-06 10:38:51

Ping抓包 ICMP協議

2021-11-26 08:07:16

MySQL SQL 語句數據庫
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩一级电影免费观看 | 久草网在线视频 | 91一区二区三区在线观看 | 国产精品一区二区三区四区五区 | 国产一区不卡 | 91久久精品国产91久久性色tv | 91视频在线观看 | 欧美一级三级 | 色综合久久天天综合网 | 国产精品2 | 国产第一页在线观看 | 日本精品一区二区三区视频 | 欧美精品a∨在线观看不卡 欧美日韩中文字幕在线播放 | 欧美激情国产精品 | 国产精品久久久久久 | 亚洲精品乱码久久久久久蜜桃91 | 高清免费在线 | 国产色网 | 欧美一级视频 | 精品亚洲一区二区三区四区五区 | 欧美一区2区三区4区公司二百 | 91久久久久 | 国产午夜精品一区二区三区在线观看 | av在线伊人| 亚洲免费在线观看 | 精品国产视频 | 激情久久av一区av二区av三区 | 一区二区中文字幕 | 国产精品免费播放 | 精品乱码一区二区 | 精品久久久久久久久久久久久久 | 久久99精品久久久久久国产越南 | 久草中文在线 | 亚洲大片 | 日韩精品成人网 | 久草在线在线精品观看 | 日韩视频―中文字幕 | 黄色成人av| 成人午夜在线 | 亚洲精品美女视频 | 成人亚洲性情网站www在线观看 |