动态代理方法互相调用

  • Post author:
  • Post category:其他


当代理对象方法互相调用时,被调用的方法不能代理。

从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;

}



版权声明:本文为qq_44989936原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。