今天写的这个博客是用于,在Spring项目中如何实现自定义注解。
现在我的项目中需要在API工程中记录用户的行为日志。由于行为日志中有行为描述等等的对于每一个接口不同的数据。有两种实现方法:
第一种就是写一个公共方法,然后在每个Controller中手动传参调用,这样冗余代码比较多,而且现在接口都完成了,要每个接口加这样的代码,会很烦。
第二种就是实现在方法上注解。这就需要我们实现自己的自定义注解了。
下面是我的实现方法。
首先我们需要创建一个自定义注解类:
package com.***.utils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
/**
* 自定义行为日志注解
* Created by yefuliang on 2017/11/2.
*/
@Target({ElementType.METHOD})//
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//说明该注解将被包含在javadoc中
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface LogAnnotation {
/**
* 行为描述,数据类型为String类型
* @return
*/
String behaviorDes() default "";
}
这个类非常简单,关于里面的一些注解什么的网上也有很多解释,为了方便大家,我找一些解释放在下面,以便于阅读:
首先最重要的就是@interface这个注解,这个注解就是表明这是一个注解类。我们可以看看其他的一些Spring注解什么的,每个注解类也都有这个注解。这里就不多说了。
然后我们可以看看类上面的几个元注解(至于元注解是什么,网上有很多解释,这里就不多说了):
- @Target
这个注解就是表明该注解类能够作用的范围,也就是能够注解在哪,比如 类、方法、参数等。
下面是他的一些参数:
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
里面的参数是可以多选的,使用方法比如@Target({ElementType.METHOD,ElementType.TYPE})。 - @Retention
这个注解是保留说明,也就是表明这个注解所注解的类能在哪里保留,他有三个属性值:
RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
从上面可以看出一般使用的事第三个属性,其余两个属性,说实话 我也不清楚什么情况下使用这两种。 - @Documented
@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中。 - @Order
@Order标记定义了组件的加载顺序,这个标记包含一个value属性。属性接受整形值。如:1,2 等等。值越小拥有越高的优先级。Ordered.HIGHEST_PRECEDENCE这个属性值是最高优先级的属性,它的值是-2147483648,对应的最低属性值是Ordered.LOWEST_PRECEDENCE,它的值是2147483647。
至于这个类里面的方法,就是注解时需要传入的参数了。
以下面的的说明一下
String behaviorDes() default “”;
很简单 我们可以分成三部分,String \ behaviorDes()\default “”
我们先看第二个,这个是我们注解时需要传入的参数值的名称,比如上述的用的事behaviorDes,那么我们在注解时就应该这样注解@LogAnnotation(behaviorDes=”测试”),当然如果你只有一个参数需要传的,那么建议各位使用value(),这样注解时会默认传入的,这样我们就可以向其他注解一样@LogAnnotation(“测试”)。
第一部分的String 是代表这个注解的参数类型。
第三部分是需要给注解一个默认值,因为如果如果开发人员没有传入值,而且没有默认值的话,会报错。
注解类说完了,那么我们需要一个注解实现类用于实现注解需要实现的功能,那么注解实现类,我是用的事基于AOP的实现类,
首先 我们需要AOP的jar包,maven的pom.xml配置一下代码
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
这里需要注意一下,上面的代码没有版本号,因为我现在的项目有一个同一的版本管理,所以这里不需要加,当然各位看官可以根据自己的需要选择。
注:由于我使用的项目是SpringBoot项目,好像SpringBoot项目是默认开启面向切面编程的,所以这里不需要配置什么,但是如果是Spring MVC项目的话,就需要在配置文件中开启切面编程了,
<aop:aspectj-autoproxy proxy-target-class="true"/>
若不加上上面的配置,会导致方法运行时流程走不到下面的实现类中。
然后我们需要建一个实现类:
package com.***.utils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 自定义日志注解实现类
* Created by yefuliang on 2017/11/2.
*/
@Aspect
@Component
public class LogAnnotationAnalytic {
@Before("execution(* com.---.---..*.*(..)) && @annotation(logAnnotation)")
public void logAnnotation(final JoinPoint joinPoint, LogAnnotation logAnnotation ) {
try {
Object[] args = joinPoint.getArgs();
System.out.println("--------------------------注解成功--------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先我们需要给类注解@Aspect,表明该类是一个AOP的类。
@Before(“execution(* com.—.—...(..)) && @annotation(logAnnotation)”)
这一段代码是我从网上找的,
@Before表明在方法前执行。execution(* com.—.—...(..))表明执行切入点扫描的范围,里面的路径由于隐私就用—代替了,抱歉。不过后面的...(..)都是需要原样输入的。@annotation(logAnnotation)需要你输入你实现的是哪个注解类。
当然如果你是使用Aop的@After方式,想要在执行完逻辑代码再拦截,并且你想在方法中获取逻辑代码返回的数据的话。你可以向下面一样:
@AfterReturning(returning="rvt", pointcut="execution(* com.---.---..*.*(..)) && @annotation(logAnnotation)")
public void logAnnotation(final JoinPoint joinPoint,LogAnnotation logAnnotation,String rvt) {
....
}
上面的rvt就是逻辑代码返回的数据了。
至于后面两个输入参数就是获取输入的参数Object[] args = joinPoint.getArgs();能获取请求参数的请求体参数。
LogAnnotation logAnnotation能获取注解时传入的数据,使用方法是logAnnotation.behaviorDes()就可以了。
然后我们就可以需要注解的方法或类上面使用注解了
@LogAnnotation(behaviorDes="测试")
@RequestMapping("/getCommentList")
public String getCommentList(@RequestBody JSONObject request){
String json = request.toJSONString();
return commentService2_2.getCommentList(request.toJSONString());
}
然后请求方法,能够debug出在方法执行之前首先进入到注解实现类里面,并且能过获取到传入的参数和注解时传入的数据。