aop切面编程的时候,类内部调用出现注解失效的情况
问题描述: 我们在使用AOP的时候,有时候会出现注解失效的情况,而常见的原因就是类内部的方法调用,这里结合具体的示例代码来讨论这个问题。(刚开始写博客,有错误和不足之处,欢迎大家的批评和指正)
下面是示例代码:
自定义注解:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopTest {
String name() default "";
}
切面处理:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class AopAspect {
@Pointcut("@annotation(com.myaop.AopTest)")
public void aopTestPointCut(){
}
@Around("aopTestPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
AopTest annotation = method.getAnnotation(AopTest.class);
System.out.println("进入切面,开始执行 : " + annotation.name());
return point.proceed();
}
}
从上面代码可以看出,方法添加注解后,在调用时会先执行around()方法中的内容,也就是先输出 : “进入切面,开始执行 : ” 。
下面测试一下:
import org.springframework.stereotype.Service;
@Service
public class TestService {
@AopTest(name="A事件")
public void methodA(){
System.out.println("正在执行A事件。。。。。。。。");
}
@AopTest(name="B事件")
public void methodB(){
System.out.println("正在执行B事件。。。。。。。。");
}
public void methodC(){
System.out.println("正在执行C事件。。。。。。。。");
methodA();
}
@AopTest(name="B事件")
public void methodD(){
System.out.println("正在执行D事件。。。。。。。。");
methodF();
}
public void methodF(){
System.out.println("正在执行F事件。。。。。。。。");
}
public void methodG(){
System.out.println("正在执行G事件。。。。。。。。");
methodA();
}
}
新建一个Controller来测试一下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MainTestController {
@Autowired
private TestService testService;
@RequestMapping("/test")
@ResponseBody
public void test1(){
testService.methodA();
}
}
当调用 testService.methodA()时,会先进入切面,结果如下:
这是我们最常用的使用场景。接下来讨论有可能出现注解失效的场景,分三种情况来讨论:
A(有注解) ------调用----- B(无注解)
A(无注解) ------ 调用 ---- B(有注解)
A(有注解) ------ 调用------ B(有注解)
A和B都是类里面的方法,我们这里讨论的是内部方法调用的情况。先看第一种 A(有注解) ------调用----- B(无注解):
controller 中调用 testService.methodD(); 结果如下:
从结果可以看出,只有加了注解的方法才会进入切面。因为spring生成bean的时候扫描了有注解的方法,如果扫描到注解会生成代理类,通过代理类加入了对切面的处理。
class proxy$TestService{
TestService testService = new TestService();
void methodD(){
startTransaction();
testService.methodD();
}
void methodF(){
//由于methodF()没有注解,所以不会启动transaction,而是直接调用TestService的实例的methodF()方法
testService.methodF();
}
}
第二种执行testService.methodC();A(无注解) ------ 调用 ---- B(有注解)
这里可以看到,当我们controller在进行调用A时,如果A无注解,B有注解,这时B上面的注解会失效。因为在方法内部调用时,会使用原生的bean去调用,而不会生成的代理类去调用,所以B的注解会失效。第三种:A(有注解) ------ 调用------ B(有注解)
如果A有注解,B有注解,这时B上面的注解也会失效。因为在方法内部调用时,会使用原生的bean去调用,而不会生成的代理类去调用,所以B的注解也会失效。
解决这种原因导致注解失效的最好的方法就是将方法拆分为不同的类,避免内部调用。