Java 自定义注解, 以及@interface @Target @Retention @Around @Before @After ProceedingJoinPoint JoinPoint 等用法
注解应用非常广泛,我们自定义注解能简化开发各种各种业务
一、关键字解释
(1) 定义注解时,关键字
@interface 来表示注解类的类型
@Target @Retention 这两个具体值,需要分析注解的应用场景来确定;
对于“调用方法时打印入参和出参”的例子,是在调用调用方法时,则使用RetentionPolicy.RUNTIME, 且作用于方法上,则使用ElementType.METHOD;
(2) 实现注解时,关键字
@Before、@After、@AfterReturning 方法的入参是JoinPoint类型
@Around 方法的入参是ProceedingJoinPoint类型
ProceedingJoinPoint继承了JoinPoint类型,并扩展出了proceed()方法,执行proceed()也就是执行原始的函数具体业务方法,可以通过JoinPoint对象获取一些请求上的参数,比如request,parms等;
执行顺序:
@Around >> @Before >> 被添加注解的方法业务执行 >> @After >> @AfterReturning
特别注意:
注解中,可以存在@Around、@Before 、@After或、@AfterReturning任意组合
注解中,存在@Around ,也存在@Before 、@After或、@AfterReturning时、会先执行@Around;
在@Around中,执行参数的ProceedingJoinPoint.proceed(), 会触发@Before 、“被添加注解的方法业务执行” 、@After、@AfterReturning的执行,
在@Around中,不执行参数的ProceedingJoinPoint.proceed(), 不会触发@Before 、“被添加注解的方法业务执行”,特别注意;所以正常逻辑中,ProceedingJoinPoint.proceed()都应该触发执行。
@Around 的返回值应该为 “被添加注解的方法业务执行” 的返回值。通过获取Object bizData = ProceedingJoinPoint.proceed(), 也可以对返回值加工再返回;
三、代码实现
(1) 定义注解
package com.xxxxx.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MethodParam {
/**
* 自定义方法, 日志描述信息, 可有可无
*
* @return
*/
String description() default "";
/**
* 自定义方法1, 日志描述信息, 可有可无
*
* @return
*/
String description1() default "";
/**
* 自定义方法2, 日志描述信息, 可有可无
*
* @return
*/
String description2() default "";
}
其中description() description1() description2() 都是自定义方法(相当于调用注解时的传参),当业务需要就声明,不需要就无需声明
(2) 实现注解
package com.xxxxx.annotation.annotationAspect;
import com.xxxxx.annotation.MethodParam;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class MethodParamAspect {
/**
* 声明切点
*/
@Pointcut(value = "@annotation(com.xxxxx.annotation.MethodParam)")
public void doPointCut() {
System.out.print("执行doPointCut \n");
}
/**
* 调用方法前后执行
* @param joinPoint
*/
@Around(value = "doPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.print("执行doAround \n");
//获取注解入参
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
MethodParam methodParamNew = method.getAnnotation(MethodParam.class);
String description = methodParamNew.description();
String description1 = methodParamNew.description1();
String description2 = methodParamNew.description2();
//被添加注解的方法业务执行
Object bizData = joinPoint.proceed();
//处理其他业务
return bizData;
}
/**
* 调用方法之【前】执行
* @param joinPoint
*/
@Before(value = "doPointCut()")
public void doBefore(JoinPoint joinPoint){
System.out.print("执行doBefore \n");
//处理其他业务
}
/***
* 调用方法之【后】执行
*
* @param joinPoint
*/
@After(value = "doPointCut()")
public void doAfter(JoinPoint joinPoint){
System.out.print("执行doAfter \n");
//处理其他业务
}
/***
* @After执行之【后】执行
*
* @param joinPoint
* @param res 具体业务的返回值 (可有可无)
*/
@AfterReturning(value = "doPointCut()",returning = "res")
public void doAfterReturning(JoinPoint joinPoint, String res){
System.out.print("执行doAfterReturning \n");
//处理其他业务
}
}
(3) 使用注解
package com.xxxxx.component;
import com.xxxxx.annotation.MethodParam;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component
public class OrderComponent {
@MethodParam(description = "aaa", description1 = "bbb", description2 = "ccc")
public String testAnnotation(String val1, String val2){
String aa = val1.concat(val2)
.concat(String.valueOf(String.format("%04d",new Random().nextInt(9999))));
System.out.print(aa + "\n");
return aa;
}
}