码迷,mamicode.com
首页 > 编程语言 > 详细

java的动态代理

时间:2017-12-12 13:53:46      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:tac   run   argument   prot   前言   caller   子类   不用   bin   

一 前言

在学习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的动态代理

标签:tac   run   argument   prot   前言   caller   子类   不用   bin   

原文地址:http://www.cnblogs.com/balfish/p/8027154.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!