java的动态代理

Posted 宇宙唯心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java的动态代理相关的知识,希望对你有一定的参考价值。

一 前言

在学习spring时候我们知道spring两大思想是IoC和AOP,IoC的依赖注入就不用说了,而对于Spring的核心AOP来说,其底层原理其实就是java的动态代理机制。

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler(interface),一个是Proxy(class).

InvocationHandler:

每一个动态代理类都必须实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler。当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

 

Proxy:

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

 

 

二 实例说明

接口:

public interface SubjectProvider {
    String getSubject(String prefix);
}

 

实现类1:

public class QmqSubjectProvider implements SubjectProvider {

    @Override
    public String getSubject(String prefix) {
        return prefix + " qmqSubject";
    }
}

 

实现类2:

public class QconfigSubjectProvider implements SubjectProvider {
    
    @Override
    public String getSubject(String prefix) {
        return prefix + " qconfigSubject";
    }
}

 

代理类模版:

public class ProxyHandler implements InvocationHandler {
    private Object target;

    public Object bind(Object target) {
        this.target = target;

        System.out.println("----" + target.getClass().getName());
        System.out.println("----" + target.getClass().getInterfaces()[0]);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeMethod();
        return method.invoke(target, args);
    }

    private void beforeMethod() {
        System.out.println("before method...");
    }
}

 

 

客户端主方法:

public class ProxyMain {

    public static void main(String[] args) {
        ProxyHandler proxyHandler = new ProxyHandler();
        // qmq
        SubjectProvider qmqSubjectProvider = (SubjectProvider) proxyHandler.bind(new QmqSubjectProvider());
        System.out.println(qmqSubjectProvider.getSubject("1"));
        // qconfig
        SubjectProvider qconfigSubjectProvider = (SubjectProvider) proxyHandler.bind(new QconfigSubjectProvider());
        System.out.println(qconfigSubjectProvider.getSubject("1"));
    }
}

运行结果:

----com.balfish.hotel.train.proxy.QmqSubjectProvider
----interface com.balfish.hotel.train.proxy.SubjectProvider
before method...
1 qmqSubject
----com.balfish.hotel.train.proxy.QconfigSubjectProvider
----interface com.balfish.hotel.train.proxy.SubjectProvider
before method...
1 qconfigSubject

 

 

三 原理分析

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this) 这句,传入了代理对象,代理对象要实现的接口,且传入了InvocationHandler类的子类(代理类,本例this代指ProxyHandler), 返回值为com.sun.proxy.$Proxy0, 这个代理对象是jvm运行时动态生成的一个对象,它并不是InvocationHandler类型,也不是我们定义的接口类型。
当调用 qmqSubjectProvider.getSubject("1") 时, 会跳转到代理对象关联到的handler的invoke方法去执行,而bind方法又接受了其真实对象,那么可以进行后续的调用。前后都可以插入相应的逻辑
 
 
源码分析:
newProxyInstance源码
 1     @CallerSensitive
 2     public static Object newProxyInstance(ClassLoader loader,
 3                                           Class<?>[] interfaces,
 4                                           InvocationHandler h)
 5         throws IllegalArgumentException
 6     {
 7         Objects.requireNonNull(h);
 8 
 9         final Class<?>[] intfs = interfaces.clone();
10         final SecurityManager sm = System.getSecurityManager();
11         if (sm != null) {
12             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
13         }
14 
15         /*
16          * Look up or generate the designated proxy class.
17          */
18         Class<?> cl = getProxyClass0(loader, intfs);
19 
20         /*
21          * Invoke its constructor with the designated invocation handler.
22          */
23         try {
24             if (sm != null) {
25                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
26             }
27 
28             final Constructor<?> cons = cl.getConstructor(constructorParams);
29             final InvocationHandler ih = h;
30             if (!Modifier.isPublic(cl.getModifiers())) {
31                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
32                     public Void run() {
33                         cons.setAccessible(true);
34                         return null;
35                     }
36                 });
37             }
38             return cons.newInstance(new Object[]{h});
39         } catch (IllegalAccessException|InstantiationException e) {
40             throw new InternalError(e.toString(), e);
41         } catch (InvocationTargetException e) {
42             Throwable t = e.getCause();
43             if (t instanceof RuntimeException) {
44                 throw (RuntimeException) t;
45             } else {
46                 throw new InternalError(t.toString(), t);
47             }
48         } catch (NoSuchMethodException e) {
49             throw new InternalError(e.toString(), e);
50         }
51     }
View Code

  Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事. 
        (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0. $Proxy0类实现了interfaces的接口,并继承了Proxy类. 
        (2)实例化$Proxy0并在构造方法中把ProxyHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:

1 class Proxy{  
2     InvocationHandler h=null;  
3     protected Proxy(InvocationHandler h) {  
4         this.h = h;  
5     }  
6     ...  
7 }  
View Code

        来看一下这个继承了Proxy的$Proxy0的源代码: 

public final class $Proxy0 extends Proxy implements SubjectProvider {  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  
  
    static {  
        try {  
            m1 = Class.forName("java.lang.Object").getMethod("equals",  
                    new Class[] { Class.forName("java.lang.Object") });  
  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                    new Class[0]);  
  
            m3 = Class.forName("***.SubjectProvider").getMethod("getSubject",  
                    new Class[0]);  
  
            m2 = Class.forName("java.lang.Object").getMethod("toString",  
                    new Class[0]);  
  
        } catch (NoSuchMethodException nosuchmethodexception) {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        } catch (ClassNotFoundException classnotfoundexception) {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    } //static  
  
    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  
  
    @Override  
    public final boolean equals(Object obj) {  
        try {  
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final int hashCode() {  
        try {  
            return ((Integer) super.h.invoke(this, m0, null)).intValue();  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    public final void getSubject() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    @Override  
    public final String toString() {  
        try {  
            return (String) super.h.invoke(this, m2, null);  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
}

 

 

四 适用场景

以上是关于java的动态代理的主要内容,如果未能解决你的问题,请参考以下文章

Java设计模式-代理模式之动态代理(附源代码分析)

(java反射-JDK动态代理)+CGLIB动态代理

Java动态代理

Java动态代理

Java动态代理

动态代理