当代理对象方法互相调用时,被调用的方法不能代理。
从JDK 分析原因,不用担心,超级简单,cglib复杂一点,不过也就复杂一点点。
目录结构如下
Target.java定义俩方法
package t1.propagation;
public interface Target {
public void a();
public void b();
}
子类继承方法,并实现互相调用
public class TargetImp implements Target {
@Override
public void a() {
System.out.println("我是a");
b();
}
@Override
public void b() {
System.out.println("我优秀");
}
}
main()方法
public static void main(String[] args) {
//生成代理对象class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Target t1= (Target) Proxy.newProxyInstance(Test.class.getClassLoader(), TargetImp.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("hello");
method.invoke(new TargetImp(), args);
return null;
}
});
t1.a();
}
结果如下:
hello
我是a
我优秀
如果很明显b()方法没有被代理,查看class文件
当前项目,忽略无用代码
public final class $Proxy0 extends Proxy implements Target {
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void b() throws {
//h= InvocationHandler
//直接调用invoke方法
super.h.invoke(this, m3, (Object[])null);
}
public final void a() throws {
//h= InvocationHandler
//直接调用invoke方法
super.h.invoke(this, m4, (Object[])null);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("t1.propagation.Target").getMethod("b");
m4 = Class.forName("t1.propagation.Target").getMethod("a");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
生成的代理对象就是在方法内部调用了我们自定义的InvocationHandler的invoke方法,this是当前代理对象。
在invoke方法里我们是直接调用的TargetImp的b()方法,并不是调用代理对象的b()方法,自然也不能代理了。
你会不会想到,传入的代理对象的作用是什么,如果我们用代理对象直接调用代理方法不就可以了,可以,但是这明显是不符合逻辑的,代码直接就写死了。
不知道你记不记得ThreadLocal,线程有个ThreadLocalMap,ThreadLocal的方法set和get等,先获取当前线程的ThreadLocalMap,把当前ThreadLocal作为key,你要保存的Object 作为value。
我们可以利用ThreadLocal保存代理对象,实现如下:
Tools.java
public class Tools {
static ThreadLocal threadLocal=new ThreadLocal();
//添加代理对象
public static void put(Object o){
threadLocal.set(o);
}
//获取代理对象
public static Object get(){
return threadLocal.get();
}
}
TargetImp.java
public class TargetImp implements Target {
@Override
public void a() {
System.out.println("我是a");
//获取代理对象
//原为b()
//Target是接口,这里别写错
((Target)Tools.get()).b();
}
@Override
public void b() {
System.out.println("我优秀");
}
}
main()方法:
public static void main(String[] args) {
//生成代理对象class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Target t1= (Target) Proxy.newProxyInstance(Test.class.getClassLoader(), TargetImp.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//添加代理对象
Tools.put(proxy);
System.out.println("hello");
method.invoke(new TargetImp(), args);
return null;
}
});
t1.a();
}
结果:
hello
我是a
hello
我优秀
代理成功了。
lz参考的是spring,如果你不了解spring,请忽略:
和spring的实现原理相同,把我们什么的Tools.get 换成Aop.currentProxy()
public static Object currentProxy() throws IllegalStateException {
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException(
"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
}
return proxy;
}
原理相同,别忘了exposeProxy()设置为true,事务也一样
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//使用cglib,默认使用jdk
boolean proxyTargetClass() default false;
//解决方法互相调用
boolean exposeProxy() default false;
}