您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么使用的Java 注解”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么使用的Java 注解”吧!
Java 元注解
注解(Annotation)是一種可以放在 Java 類上,方法上,屬性上,參數前面的一種特殊的注釋,用來注釋注解的注解叫做元注解。元注解我們平常不會編寫,只需要添加到我們自己編寫的注解上即可,。
Java 自帶的常用的元注解有@Target,@Retention,@Documented,@Inherited 分別有如下含義
鴻蒙官方戰略合作共建——HarmonyOS技術社區
@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; } }
上面的代碼中我們定義了一個切面指定需要攔截的包名和注解,因為涉及到很多業務相關的代碼,所以不能完整的提供出來,但是整個思路就是這樣的,在每種操作類型前后將需要記錄的數據查詢出來進行記錄。代碼很長主要是用來獲取相應的參數值的,大家使用的時候可以根據自己的需要進行取舍。比如在新增操作的時候,我們將新增的數據進行記錄下來;編輯的時候將編輯前的數據查詢出來和編輯后的數據一起保存起來,刪除也是一樣的,在刪除前將數據查詢出來保存到日志表中。
同樣導出和下載都會記錄相應信息,整個操作類型的代碼如下:
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"; }
后續在使用的時候只需要在需要的方法上加上注解,填上相應的參數即可@OperationLog(desc = "查詢單條記錄", path = "/data")
到此,相信大家對“怎么使用的Java 注解”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。