<sup id="qjuuc"></sup>
  • <td id="qjuuc"><rp id="qjuuc"><center id="qjuuc"></center></rp></td>
    • <menuitem id="qjuuc"></menuitem>
      • <menuitem id="qjuuc"><tfoot id="qjuuc"><em id="qjuuc"></em></tfoot></menuitem>
      • <menuitem id="qjuuc"></menuitem>
        <strike id="qjuuc"><rp id="qjuuc"></rp></strike>
        • <fieldset id="qjuuc"></fieldset>
            <tt id="qjuuc"><tfoot id="qjuuc"></tfoot></tt>
          • 當前位置:聯升科技 > 技術資訊 > 開發(fā)技術 >

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

            2020-11-20    作者:鴨血粉絲    來源:Java極客技術    閱讀: 次

            本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲 。轉載本文請聯系Java極客技術公眾號。
            Hello,大家好,我是阿粉,Java 的注解相信大家天天都在用,但是關于注解的原理,大家都了解嗎?這篇文章通過意見簡單的示例給大家演示一下注解的使用和原理。
            Java 元注解
            注解(Annotation)是一種可以放在 Java 類上,方法上,屬性上,參數前面的一種特殊的注釋,用來注釋注解的注解叫做元注解。元注解我們平常不會編寫,只需要添加到我們自己編寫的注解上即可,。
            Java 自帶的常用的元注解有@Target,@Retention,@Documented,@Inherited 分別有如下含義
            @Target:標記這個注解使用的地方,取值范圍在枚舉 java.lang.annotation.ElementType:TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE。
            @Retention :標識這個注解的生命周期,取值范圍在枚舉 java.lang.annotation.RetentionPolicy,SOURCE,CLASS,RUNTIME,一般定義的注解都是在運行時使用,所有要用 @Retention(RetentionPolicy.RUNTIME);
            @Documented:表示注解是否包含到文檔中。
            @Inherited :使用@Inherited定義子類是否可繼承父類定義的Annotation。@Inherited僅針對@Target(ElementType.TYPE)類型的annotation有效,并且僅針對class的繼承,對interface的繼承無效。
            定義注解
            上面介紹了幾個元注解,下面我們定義一個日志注解來演示一下,我們通過定義一個名為OperationLog 的注解來記錄一些通用的操作日志,比如記錄什么時候什么人查詢的哪個表的數據或者新增了什么數據。編寫注解我們用的是 @interface 關鍵字,相關代碼如下:
            package com.api.annotation; 
            import java.lang.annotation.*; 
            /** 
             * <br> 
             * <b>Function:</b><br> 
             * <b>Author:</b>@author 子悠<br> 
             * <b>Date:</b>2020-11-17 22:10<br> 
             * <b>Desc:</b>用于記錄操作日志<br> 
             */ 
            @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.*; 
            /** 
             * <br> 
             * <b>Function:</b><br> 
             * <b>Author:</b>@author 子悠<br> 
             * <b>Date:</b>2020-11-17 14:40<br> 
             * <b>Desc:</b>aspect for operation log<br> 
             */ 
            @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<String, String> 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<String, String> autoQueryEditedData(Class<?> targetCls, String primaryKey, Class<?> defaultServiceClass, Object[] args) throws Exception { 
                    if (StrUtil.isBlank(primaryKey)) { 
                        throw new Exception(); 
                    } 
                    Map<String, String> 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<Object> 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<Object> 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; 
            /** 
             * <br> 
             * <b>Function:</b><br> 
             * <b>Author:</b>@author 子悠<br> 
             * <b>Date:</b>2020-11-17 22:11<br> 
             * <b>Desc:</b>無<br> 
             */ 
            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 中的各種注解本質上也是這種邏輯都需要定義使用和解析。很多時候我們可以通過自定義注解去解決很多場景,比如日志,緩存等。


            相關文章

            我們很樂意傾聽您的聲音!
            即刻與我們取得聯絡
            成為日后肩并肩合作的伙伴。

            行業(yè)資訊

            聯系我們

            13387904606

            地址:新余市仙女湖區(qū)仙女湖大道萬商紅A2棟

            手機:13755589003
            QQ:122322500
            微信號:13755589003

            江西新余網站設計_小程序制作_OA系統(tǒng)開發(fā)_企業(yè)ERP管理系統(tǒng)_app開發(fā)-新余聯升網絡科技有限公司 贛ICP備19013599號-1   贛公網安備 36050202000267號   

            微信二維碼
            五月天婷婷在线观看历史|国产欧美日韩免费一区二区|亚洲成a∨人片在无码|欧美日韩不卡一区二区三区中文字|每日更新国产精品视频|成年人在线观看视频免费|亚洲A片一区二区三区在线观看
            <sup id="qjuuc"></sup>
          • <td id="qjuuc"><rp id="qjuuc"><center id="qjuuc"></center></rp></td>
            • <menuitem id="qjuuc"></menuitem>
              • <menuitem id="qjuuc"><tfoot id="qjuuc"><em id="qjuuc"></em></tfoot></menuitem>
              • <menuitem id="qjuuc"></menuitem>
                <strike id="qjuuc"><rp id="qjuuc"></rp></strike>
                • <fieldset id="qjuuc"></fieldset>
                    <tt id="qjuuc"><tfoot id="qjuuc"></tfoot></tt>