Java中怎么自定义注解
这篇文章将为大家详细讲解有关Java中怎么自定义注解,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
Java基础,注解与自定义注解
Java 注解Annotation,是 JDK5.0 引入的一种注释机制。
一、自带注解
在学习自定义注解前,先了解一下Java内部定义的一套注解:共有7个,3个在java.lang中,剩下的四个在java.lang.annotation中。
作用在类或者方法上:
@Override 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。@Deprecated 标记已过时方法。不推荐使用@SuppressWarnings 指示编译器去忽略注解中声明的警告
作用在其他注解上
@Retention 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。@Documented 标记这些注解是否包含在用户文档中@Target 标记这个注解应该是哪种 Java 成员@Inherited 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
额外添加的3个注解:
@SafeVarargs Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface Java 8 开始支持,标识一个匿名函数或函数式接口。@Repeatable Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
二、注解结构
Annotation有三个重要的主干类,分别为:Annotation、ElememtType、RetentionPolicy
1、Annotation:
public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); Class extends Annotation> annotationType();}
2、ElementType:
其指定Annotation的类型,如METHOD ,则该Annotation只能修饰方法
public enum ElementType { TYPE, //类、接口、枚举 FIELD, //字段、枚举常量 METHOD, //方法 PARAMETER, //参数 CONSTRUCTOR, //构造方法 LOCAL_VARIABLE, //局部变量 ANNOTATION_TYPE, //注释类型 PACKAGE, //包 TYPE_PARAMETER, TYPE_USE}
3、RetentionPolicy:
public enum RetentionPolicy { SOURCE, //Annotation仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 CLASS, //编译器将Annotation存储于类对应的.class文件中。默认行为 RUNTIME //编译器将Annotation存储于class文件中,并且可由JVM读入 }
RUNTIME:注释将由编译器记录在类文件中,并由VM在运行时保留,因此它们可以被反射读取。
CLASS:注释将由编译器记录在类文件中,但不需要在运行时由VM保留。
4、例子
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Repeatable(ComponentScans.class)public @interface ComponentScan{ }
@interface
@interface定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。而不是声明了一个interface。(@interface和interface是不同的)。
定义 Annotation 时,@interface 是必须的。
注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
@Document
类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。
@Target
ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。
@Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,ComponentScan 是来修饰"类、接口(包括注释类型)或枚举声明"的注解。
@Retention
RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。
注意:定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。
三、自定义Annotation
我们下面自定义一个Log日志注解:一个 在 controller的method上的注解,该注解会将用户对这个方法的操作 记录到数据库中
3.1、操作类型
package com.lee.common.enums;/** * 业务操作类型 * */public enum BusinessType{ /** * 其它 */ OTHER, /** * 新增 */ INSERT, /** * 修改 */ UPDATE, /** * 删除 */ DELETE, /** * 授权 */ GRANT, /** * 导出 */ EXPORT, /** * 导入 */ IMPORT, /** * 强退 */ FORCE, /** * 生成代码 */ GENCODE, /** * 清空数据 */ CLEAN,}
3.2、自定义注解
package com.lee.common.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import com.lee.common.enums.BusinessType;import com.lee.common.enums.OperatorType;/** * 自定义操作日志记录注解 */@Target({ ElementType.PARAMETER, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Log{ /** * 模块 */ public String title() default ""; /** * 功能 */ public BusinessType businessType() default BusinessType.OTHER; /** * 操作人类别 */ public OperatorType operatorType() default OperatorType.MANAGE; /** * 是否保存请求的参数 */ public boolean isSaveRequestData() default true;}
3.3、日志处理Aspect
package com.lee.framework.aspectj;import java.lang.reflect.Method;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.servlet.HandlerMapping;import com.alibaba.fastjson.JSON;import com.lee.common.annotation.Log;import com.lee.common.core.domain.model.LoginUser;import com.lee.common.enums.BusinessStatus;import com.lee.common.enums.HttpMethod;import com.lee.common.utils.ServletUtils;import com.lee.common.utils.StringUtils;import com.lee.common.utils.ip.IpUtils;import com.lee.common.utils.spring.SpringUtils;import com.lee.framework.manager.AsyncManager;import com.lee.framework.manager.factory.AsyncFactory;import com.lee.framework.web.service.TokenService;import com.lee.system.domain.SysOperLog;/** * 操作日志记录处理 * * @author lee */@Aspect@Componentpublic class LogAspect{ private static final Logger log = LoggerFactory.getLogger(LogAspect.class); // 配置织入点 @Pointcut("@annotation(com.lee.common.annotation.Log)") public void logPointCut() { } /** * 处理完请求后执行 * * @param joinPoint 切点 */ @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) { handleLog(joinPoint, null, jsonResult); } /** * 拦截异常操作 * * @param joinPoint 切点 * @param e 异常 */ @AfterThrowing(value = "logPointCut()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Exception e) { handleLog(joinPoint, e, null); } protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) { try { // 获得注解 Log controllerLog = getAnnotationLog(joinPoint); if (controllerLog == null) { return; } // 获取当前的用户 LoginUser loginUser = SpringUtils.getBean(TokenService.class).getLoginUser(ServletUtils.getRequest()); // *========数据库日志=========*// SysOperLog operLog = new SysOperLog(); operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); // 请求的地址 String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); operLog.setOperIp(ip); // 返回参数 operLog.setJsonResult(JSON.toJSONString(jsonResult)); operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); if (loginUser != null) { operLog.setOperName(loginUser.getUsername()); } if (e != null) { operLog.setStatus(BusinessStatus.FAIL.ordinal()); operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); } // 设置方法名称 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); operLog.setMethod(className + "." + methodName + "()"); // 设置请求方式 operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog); // 保存数据库 AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); } catch (Exception exp) { // 记录本地异常日志 log.error("==前置通知异常=="); log.error("异常信息:{}", exp.getMessage()); exp.printStackTrace(); } } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param log 日志 * @param operLog 操作日志 * @throws Exception */ public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) throws Exception { // 设置action动作 operLog.setBusinessType(log.businessType().ordinal()); // 设置标题 operLog.setTitle(log.title()); // 设置操作人类别 operLog.setOperatorType(log.operatorType().ordinal()); // 是否需要保存request,参数和值 if (log.isSaveRequestData()) { // 获取参数的信息,传入到数据库中。 setRequestValue(joinPoint, operLog); } } /** * 获取请求的参数,放到log中 * * @param operLog 操作日志 * @throws Exception 异常 */ private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception { String requestMethod = operLog.getRequestMethod(); if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs()); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } else { Map, ?> paramsMap = (Map, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); } } /** * 是否存在注解,如果存在就获取 */ private Log getAnnotationLog(JoinPoint joinPoint) throws Exception { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(Log.class); } return null; } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray) { String params = ""; if (paramsArray != null && paramsArray.length > 0) { for (int i = 0; i < paramsArray.length; i++) { if (!isFilterObject(paramsArray[i])) { Object jsonObj = JSON.toJSON(paramsArray[i]); params += jsonObj.toString() + " "; } } } return params.trim(); } /** * 判断是否需要过滤的对象。 * * @param o 对象信息。 * @return 如果是需要过滤的对象,则返回true;否则返回false。 */ public boolean isFilterObject(final Object o) { return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse; }}
3.4、注解的使用
/** * 修改推荐位 */@Log(title = "推荐位", businessType = BusinessType.UPDATE)@PutMapping("/edit")public AjaxResult edit(@RequestBody GsRecommPosition gsRecommPosition){ Long gsRecommPositionId = gsRecommPosition.getGsRecommPositionId(); boolean condition = recommPositionHasCategory(gsRecommPositionId); return toAjax(gsRecommPositionService.updateGsRecommPosition(gsRecommPosition));}/** * 删除推荐位 */@Log(title = "推荐位", businessType = BusinessType.DELETE)@DeleteMapping("delete/{gsRecommPositionId}")public AjaxResult remove(@PathVariable Long gsRecommPositionId){ boolean condition = recommPositionHasCategory(gsRecommPositionId); return toAjax(gsRecommPositionService.deleteGsRecommPositionById(gsRecommPositionId));}
四、JoinPoint解析
1、JoinPoint
对象封装了SpringAop切面方法中的信息
方法名 | 功能 |
---|---|
Signature getSignature(); | 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs(); | 获取传入目标方法的参数对象 |
Object getTarget(); | 获取被代理的对象 |
Object getThis(); | 获取代理对象 |
joinPoint.getSignature().getName(); // 获取目标方法名joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属类的简单类名joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组joinPoint.getTarget(); // 获取被代理的对象joinPoint.getThis(); // 获取代理对象自己//获取自定义注解的信息MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();CustomLog customLog = methodSignature.getMethod().getAnnotation(CustomLog.class);
2、ProceedingJoinPoint
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中
Object proceed() throws Throwable //执行目标方法 Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
关于Java中怎么自定义注解就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。