spring使用时,在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的。
一般方法
- 把这两个方法分开到不同的类中;
- 把注解加到类名上面;
- 使用TransactionTemplate
@Autowired private TransactionTemplate transactionTemplate; transactionTemplate.execute(transactionStatus -> { return null; });
特殊方法
修改配置,修改调用:较复杂,不建议使用,特殊情况例外
- <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
- 方法调用处改为
((AbstractTemplete) AopContext.currentProxy()).book(paramHeader); - 方法调用者的方法不能为final,不然报:
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available. - 被调用者的调用的方法必须为public
spring aop 通过获取代理对象实现事务切换 - CSDN博客
原因分析
https://blog.csdn.net/clementad/article/details/47339519
JDK代理
同一个类中的方法调用,调用时,调用的是原对象的方法。
import java.lang.reflect.*;
public class MyProxy {
public interface IHello {
void sayHello1();
void sayHello2();
}
static class Hello implements IHello {
public void sayHello1() {
System.out.println("Hello world 1!!");
sayHello2();
}
public void sayHello2() {
System.out.println("Hello world 2!!");
}
}
//自定义InvocationHandler
static class HWInvocationHandler implements InvocationHandler {
//目标对象å
private Object target;
public HWInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
//执行相应的目标方法
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}
public static void main(String[] args) throws Exception {
//生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//获取动态代理类
Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(), IHello.class);
//获得代理类的构造函数,并传入参数类型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
//通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
//通过代理对象调用目标方法
iHello.sayHello1();
}
}
执行结果:
------插入前置通知代码-------------
Hello world 1!!
Hello world 2!!
------插入后置处理代码-------------
为何sayHello2前后没有通知代码?
以下设置生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
我们看下:$Proxy0类
package com.sun.proxy;
import blog.aop.proxy.cglib.MyProxy.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public final class $Proxy0 extends Proxy implements IHello {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
private static Method m4;
static {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("blog.aop.proxy.cglib.MyProxy$IHello").getMethod("sayHello1");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m4 = Class.forName("blog.aop.proxy.cglib.MyProxy$IHello").getMethod("sayHello2");
}
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void sayHello1() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sayHello2() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
主要看下sayHello1()方法的实现
super.h.invoke(this, m3, (Object[])null);
其中:
1. h为之前在` IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));` 中设置的new HWInvocationHandler(new Hello())
2. this为$Proxy0类
3. m3为:blog.aop.proxy.cglib.MyProxy$IHello.sayHello1
执行时会执行HWInvocationHandler 类的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
//执行相应的目标方法
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
其中Object rs = method.invoke(target, args);会执行new Hello()类的sayHello1方法
可以看出,
最终执行的是Hello 类的sayHello1,对象也是Hello对角,代理只一次,不会对sayHello1中的方法再次代理。
引用
深度剖析JDK动态代理机制 - MOBIN - 博客园
JAVA设计模式-动态代理(Proxy)源码分析 - 张橙子 - 博客园