思路概括
利用面向切面的代理模式(aop),将 凡是被 自定义注解 标注的方法/类 ,都被代理。在元方法中写正常的业务逻辑,在代理类中利用反射 写上注解的功能。
ps: 非spring项目 自定义注解功能 也可由代理来实现。
demo环境
springboot + aop ,使用业务行为记录功能
aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
编码
自定义注解
package li.ql.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author liql
* @date 2021/4/12
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
//自定义注解 相应的功能 利用反应在代理类中书写
String value() default "";
}
定义切面 +功能
以自定义的注解为切入点
package li.ql.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author liql
* @date 2021/9/11
*/
@Aspect //@Aspect注释告诉Spring这是个切面类
@Component
@Slf4j
public class ActionAspect3 {
//以注解为切入点
@Pointcut("@annotation(li.ql.annotation.LogAnnotation)")
public void logPointCut(){}
@Around("logPointCut()")
public void beforeMehtod(ProceedingJoinPoint pj) throws Throwable {
log.info("自定义的切面3 上环绕通知");
log.info("在这里利用反射可以书写 注解的功能");
pj.proceed();
log.info("在这里利用反射可以书写 注解的功能");
log.info("自定义切面3 下环绕通知");
}
}
最后 在需要用到注解的类/方法上注上注解即可使用
自定义一个日志记录功能注解
package li.ql.aspect;
import li.ql.annotation.LogAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author liql
* @date 2021/4/12
*/
@Aspect //@Aspect注释告诉Spring这是个切面类
@Component
@Slf4j
public class ActionAspect2 {
//@Pointcut 声明切入点 这个切入点是个注解
//如果注解上没有其它内容,只要进入这个切面,及代理成功,就可以写业务了
//如果注解上还有其它的内容 则需要通过反射 获取主街上的内容
@Pointcut("@annotation(li.ql.annotation.LogAnnotation)")
public void logPointCut(){}
@Around(value = "logPointCut()") //在logPointCut()这个切入点方法执行前 需要做的事
public void beforeMethod(ProceedingJoinPoint pj) throws Throwable {//ProceedingJoinPoint pj 参数只支持around 环绕通知
//环绕通知 ,如果不调用 pj.proceed(); 让被代理的切入点继续执行就会阻塞
log.info("第二个切面拦截注解 Aronud环绕通知第一次做的事");
//先获取方法 才能检查方法上有没有携带注解
Signature signature = pj.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
System.out.println("methodSignature="+methodSignature.toString());
//检查方法上是否有注解 先要获取目标类,然后根据目标方法名和类型 获取类的方法的反射 ,然后才能获取注解,
// 省略获取类方法的反射,则拿不到注解
// LogAnnotation annotation = methodSignature.getClass().getDeclaredAnnotation(LogAnnotation.class);// 这个不行
// LogAnnotation annotation = methodSignature.getClass().getDeclaredAnnotation(LogAnnotation.class);// 拿不到
Object targetClass = pj.getTarget();
System.out.println(methodSignature.getName()+","+methodSignature.getParameterTypes());
Method declaredMethod = targetClass.getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
//这下才能拿到注解
LogAnnotation annotation = declaredMethod.getAnnotation(LogAnnotation.class);
log.info("获取的注解:{}",annotation);
//上面那么多都是为了拿到这个value 踩写的
String value = annotation.value();
// System.out.println("注解上的值:"+value);
log.info("aop获得的注解上的值:{}",value);
pj.proceed(); //让被拦截的方法继续执行下去
log.info(" 第二个切面 Aronud环绕通知第二次做的事");
}
@After(value = "logPointCut()") //在logPointCut()这个切入点方法执行后 需要做的事
public void afterMethod(){
log.info("切入点执行后 做的事");
}
}
版权声明:本文为qq_45812181原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。