系列文章:
Java 自定义注解
一 简介
上一篇,我们已经介绍了 Java 注解的概念,基本架构,和几个主干类,以及定义注解方法。本篇中,我们降给出一个注解定义的示例,作为实战入门的初步内容。
二 相关接口 &基本注解
除了五个基本注解,还有几个是本篇的基础,需要先做了解。
2.1 @interface
当我们用 @interface 来定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个 Annotation。定义 Annotation 时,@interface 是必须的。有一点需要注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
2.2 @Documented
类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果我们想让注解也出现在 javadoc 中,就必须使用 @Documented 注解来修饰该 Annotation。
定义 Annotation 时,@Documented 不是必须的;若没有定义,则 Annotation 不会出现在 javadoc 中。
2.3 @Target
ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。 @Target(ElementType.TYPE) 是指该 Annotation 的类型是 ElementType.TYPE。表示我们的注解,是来修饰“类、接口(包括注释类型)或枚举声明”的注解。
定义 Annotation 时,@Target 也不是必须的。如果有 @Target,那么这个 Annotation 只能用于它所指定的位置;若没有 @Target,则该 Annotation 可以用于任何地方。
2.4 @Retention
RetentionPolicy 是 Annotation 的策略属性,@Retention 的作用就是指定 Annotation 的策略属性。 @Retention(RetentionPolicy.RUNTIME) ,是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这表示编译器会将该 Annotation 信息保留在.class 文件中,并且能被虚拟机读取。
定义注解时,@Retention 也非必须。如果没有设置 @Retention,那么默认是 RetentionPolicy.CLASS。
2.5 @Inherited
这个注解所标注的 Annotation 具有继承性。例如我们定义了某个注解,名为 Annotation1,并且被标注为 @Inherited。某个类 ClassB 使用了 Annotation1,则 ClassB 具有了“具有了注解 Annotation1”;现在,Sub 继承了 ClassB,由于 Annotation1 是 @Inherited 的(具有继承性),所以,Sub 也“具有了注解 Annotation1”。
三 注解作用
说了这么多,似乎漏掉了一项重要内容,注解到底有哪些作用?简单来说,Annotation 是一个辅助类,它广泛用于 Junit、Struts、Spring 等工具和框架。
如果简单一点来做个整理,那么其作用包括以下几点:
编译检查:@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用;
反射中使用 Annotation;
根据 Annotation 生成帮助文档;如上面所说,注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中;
方便查看代码;通过 @Override, @Deprecated 等注解,我们能很方便的了解程序的大致结构。
四 示例
本节,我们将通过 Springboot 下定义一个注解来进行练习。
4.1 依赖引入
主要是引入 spring-boot-starter-aop。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
</dependencies>
复制代码
如果有发生注解不生效的情况,可能还需要增加 aspectjweaver 和 aspectjrt。
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version></dependency><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version></dependency>
复制代码
4.2 注解定义
用我们常说的示例,就是记录日志,可以定义注解如下:
package com.flamingskys.spring.annotation;
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import java.lang.annotation.Documented;
/** * @Author : flamingskys * @CreateTime : 2021/11/2 * @Description : **/
@Documented@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface LogTrack { String value() default "logTracking";}
复制代码
4.3 Aspect
package com.flamingskys.spring.annotation;
import com.flamingskys.commont.util.IpUtil;import com.flamingskys.commont.util.JsonUtil;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.List;
/** * @Author : flamingskys * @CreateTime : 2021/11/2 * @Description : **/
@Component@Aspectpublic class LogTrackAspect { private static final Logger log = LoggerFactory.getLogger(LogTrackAspect.class); //切点,路径一定要写正确了 @Pointcut(value = "@annotation(com.flamingskys.spring.annotation.LogTrack)") public void access() {
}
//进来切点世界,先经过的第一个站 @Before("access()") public void doBefore(JoinPoint joinPoint) throws Throwable { System.out.println("-aop 日志记录启动-" + new Date()); } //环绕增强,是在before前就会触发 @Around("@annotation(logTrack)") public Object around(ProceedingJoinPoint pjp, LogTrack logTrack) throws Throwable { System.out.println("-aop 日志环绕阶段-" + new Date()); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest();
// GET 请求其实可以从request里获取出参数// Map<String,String[]> map=request.getParameterMap();// System.out.println("获取参数:"+map.get("username")[0])
String url = request.getRequestURL().toString(); String ip = IpUtil.getIpAddr(request); String logTrackValue = logTrack.value(); Object[] pipArrary = pjp.getArgs(); if (pipArrary.length>1){ //多参,不是Map/JsonObject方式 List<Object> argList = new ArrayList<>(); for (Object arg : pjp.getArgs()) { // request/response无法使用toJSON if (arg instanceof HttpServletRequest) { argList.add("request"); } else if (arg instanceof HttpServletResponse) { argList.add("response"); } else { argList.add(JsonUtil.objToStr(arg)); } } Signature signature = pjp.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; // 参数名数组 String[] parameterNames = ((MethodSignature) signature).getParameterNames(); System.out.println("参数名数组:"+new ArrayList(Arrays.asList(parameterNames))); System.out.println("参数是:"+argList.toString()); System.out.println("logTrackValue:"+logTrackValue); System.out.println("url:"+url); System.out.println("ip:"+ip); return pjp.proceed();
}
Object param = pipArrary[0]; System.out.println("logTrackValue:"+logTrackValue); System.out.println("url:"+url); System.out.println("ip:"+ip); System.out.println("param:"+param.toString()); return pjp.proceed();
}
//方法正常运行结束后 @After("access()") public void after(JoinPoint joinPoint) { System.out.println("-aop 日志记录结束-" + new Date()); }
}
复制代码
评论