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;}
}