原文链接:https://blog.csdn.net/u014365523/article/details/119849844
问题描述
在同一个类中调用添加注解的sendMessage方法,注解失效。
//this.sendMessage(); 失效
purchaseCommonService.sendMessage(messageVo);
@MessageLogAnnotation(messageContent = "您有新的交接单信息,请处理", messageType = MessageTypeConstant.PRIVATE_MESSAGE_TYPE, businessURL = "", messageField = "receiverId",messageFieldName ="receiver")
public void sendMessage(MessageVo messageVo) {
System.out.println("发送消息至" + messageVo.getReceiver());
}
原因分析:
在Spring中经常使用自定义注解或是spring已经封装
好的注解,通过AOP的方式是实现代码复用,避免重复劳动。而Spring实现AOP是通过动态代理来实现的(默认有接口的情况下使用JDK的动态代理,也可以通过配置proxyTargetClass来制定使用CGLib,没有接口的情况下使用CGLib).
但是无论哪一种代理,都是在原有方法的外面包一层,通过方法外的代理层来实现AOP的逻辑。
也就是说我们首先调用的是AOP代理对象而不是目标对象。但是我们使用this.sendMessage()时,this表示的是当前对象,而不是代理对象,因此注解失效。
所以,解决代理无效的根本方法就是获取到代理对象,使用代理对象进行调用
解决方案:
1. 方法一:
最简单的方法就是将调用的方法sendMessage()方法放在另外一个类中进行调用,而不是在一个类中同类调用,就不会出现问题了。
2. 方法二:
从ApplicationContext中直接获取
获取ApplicationContext有多种方法:
方法2.1 使用AopContext.currentProxy()
public class A {
public void serviceB() {
......
//此处调用的就是代理后的方法
((A)AopContext.currentProxy()).serviceA();
......
}
}
使用AopContext.currentProxy()注意必须在程序启动时开启EnableAspectJAutoProxy注解,设置代理暴露方式为true,如下面所示:
/**
* EnableAspectJAutoProxy注解两个参数作用分别为:
*
* 一个是控制aop的具体实现方式,为true的话使用cglib,为false的话使用java的Proxy,默认为false。
* 第二个参数控制代理的暴露方式,解决内部调用不能使用代理的场景,默认为false。
*
*
*/
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@SpringBootApplication
public class SpringAopApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAopApplication.class, args);
}
}
方法2.2 使用ApplicationContextAware
通过spring生命周期,直接将ApplicationContext注入进来
public class A implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void serviceB() {
......
//此处调用的就是代理后的方法
applicationContext.getBean(A.class).serviceA();
......
}
}
3. 方法三:
在实现类中注入自身,这个有点取巧。还是以上面serviceB()为例,我们如果调用this.serviceA(),this指向对象本身、不会指向代理后的对象,因此肯定不可以,但我们可以让spring提供给我们代理后的对象:
@Service
public class Child implements IChild {
//通过spring将代理后对象注入到self变量
@Autowired
private Child child;
@Override
public Map eat() throws GendorException {
String name = child.name();//此处调用的就是代理后的方法
System.out.println(name);
return null;
}
@SysLog
public String name (){
return "zhangjiguo";
}
}