系列文章:
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
@Aspect
public 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());
}
}
复制代码
评论