精华内容
下载资源
问答
  • Java中的静态代理、通用动态代理类的实现以及动态代理的原理剖析

    代理模式和静态代理

    在开发中,代理模式是常用的模式之一,一般来说我们使用的代理模式基本上都是静态代理,实现模式大致如下 : 


    我们以网络代理为例,简单演示一下静态代理的实现 : 

    // 网络接口
    interface Network {
    	public void surfTheInternet();
    
    	public void gotoFacebook();
    }
    
    // 普通网络
    class CommonNetwork implements Network {
    	@Override
    	public void surfTheInternet() {
    		System.out.println("直接上网,随便看点什么...");
    	}
    
    	@Override
    	public void gotoFacebook() {
    		System.out.println("上facebook,被墙了,没法弄啊!!!");
    	}
    
    }
    
    // 网络代理
    class NetworkProxy implements Network {
            Network mNetwork = new CommonNetwork();
    	@Override
    	public void surfTheInternet() {
    		System.out.println("连上代理");
                    mNetwork.surfTheInternet();
    	}
    
    	@Override
    	public void gotoFacebook() {
    		System.out.println("上facebook, 即使被墙了,使用网络代理也能上!!!");
                    mNetwork.gotoFacebook();
    	}
    }
    main函数 : 

    	public static void main(String[] args) {
    		Network myNetwork = new CommonNetwork();
    		myNetwork.surfTheInternet();
    		myNetwork.gotoFacebook();
    		
    		myNetwork = new NetworkProxy() ;
    		myNetwork.gotoFacebook();
    	}
    输出 : 

    直接上网,随便看点什么...
    上facebook,被墙了,没法弄啊!!!
    上facebook, 即使被墙了,使用网络代理也能上!!!
    总之,代理对象就是把被代理对象包装一层,在其内部做一些额外的工作,比如用户需要上facebook, 而普通网络无法直接访问,网络代理帮助用户先翻墙,然后再访问facebook。这就是代理的作用了。

    动态代理

         上面的网络代理示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。JDK 5中引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者,这种做法实际上相当于对方法调用进行了拦截,这样我们就可以在调用invoke的前后做自己想做的事。熟悉AOP的人对这种使用模式应该不陌生。但是这种方式不需要依赖AspectJ等AOP框架。下面我们看一个示例 : 

    新增一个被代理类 :

    /**
     * 社会化组件, 带有授权、分享功能
     * @author mrsimple
     *
     */
    public interface Socialize {
    	// 授权
    	public void doAothorize();
    	// 分享
    	public void share();
    }
    
    public class SocializeImpl implements Socialize {
    
    	@Override
    	public void doAothorize() {
    		System.out.println("doAothorize");
    	}
    
    	@Override
    	public void share() {
    		System.out.println("share");
    	}
    
    }
     

    通用动态代理类:

    /**
     * 动态代理通用类, 实现InvocationHandler接口。
     * 
     * @author mrsimple
     *
     */
    public class CommonProxy implements InvocationHandler {
    
    	/**
    	 * 目标对象
    	 */
    	private Object mTarget;
    
    	/**
    	 * 方法调用日志
    	 */
    	private List<Method> mHistories = new ArrayList<Method>();
    
    	/**
    	 * 注入被代理的对象
    	 * @param obj
    	 */
    	private CommonProxy(Object obj) {
    		mTarget = obj;
    	}
    
    	/**
    	 * 执行真正代码之前做的事
    	 * @param md
    	 * @param args
    	 */
    	private void before(Method md, Object[] args) {
    		System.out.println("** 执行" + md.getName() + "函数之前, 记录到日志系统 **");
    	}
    
    	/*
    	 * 调用任何函数都会以invoke函数为入口, proxy参数是被代理的真实对象,method为被调用的函数,arg为参数
    	 */
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) {
    
    		// before
    		before(method, args);
    		// 将调用记录保存起来
    		mHistories.add(method);
    		try {
    			// 执行真正的函数调用
    			return method.invoke(mTarget, args);
    		} catch (IllegalArgumentException e) {
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			e.printStackTrace();
    		} catch (InvocationTargetException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    	/**
    	 * 通过代理创建对象, 返回的必须是接口类型
    	 * 
    	 * @param obj
    	 *            目标接口类型
    	 * @return
    	 */
    	@SuppressWarnings("unchecked")
    	public static <T> T createProxy(T obj) {
    
    		return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
    				.getClass().getInterfaces(), new CommonProxy(obj));
    
    	}
    
    	/**
    	 * 
    	 * @return
    	 */
    	public List<Method> getHistories() {
    		return mHistories;
    	}
    
    }
    上面就是一个通用的动态代理类。创建代理对象的工厂方法createProxy中的Proxy类是至关重要的,参数也有点不好理解,我们来分析一下。

    Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
    				.getClass().getInterfaces(), new CommonProxy(obj));
    通过Proxy.newProxyInstance函数来创建代理对象,我们看看这个函数的声明(去掉了一些相关说明): 

        /**
         * Returns an instance of a proxy class for the specified interfaces
         * that dispatches method invocations to the specified invocation
         * handler.  This method is equivalent to:
         *
         * @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
         */
        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)

    可以看到,第一个参数是被代理对象的ClassLoader, 参数2是被代理对象所有接口列表的Class数组,参数三为InvocationHandler对象,就是实现了InvocationHandler接口的类,对应上面的CommonProxy类型。看我们的实现 : 

    	public static <T> T createProxy(T obj) {
    
    		return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
    				.getClass().getInterfaces(), new CommonProxy(obj));
    
    	}
    被代理对象obj通过参数传递进来,我们通过obj.getClass().getClassLoader()获取ClassLoader对象,然后通过obj.getClass().getInterfaces()获取它实现的所有接口,然后将obj包装到实现了InvocationHandler接口的CommonProxy对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。

    下面看看这个动态代理类的使用:

    	public static void main(String[] args) {
    		// 获得对象
    		Socialize mController = CommonProxy
    				.createProxy(new SocializeImpl());
    		// 调用方法
    		mController.doAothorize();
    		mController.share();
    
    		// 获取动态代理对象,然后输出调用历史记录
    		CommonProxy proxy = (CommonProxy) Proxy
    				.getInvocationHandler(mController);
    		for (Method md : proxy.getHistories()) {
    			System.out.println("调用了: " + md.getName());
    		}
    		
    		// 通用的动态代理类, 返回的是接口类型
    		Network network = CommonProxy.createProxy(new CommonNetwork());
    		network.surfTheInternet();
    	}
    输出 : 

    ** 执行doAothorize函数之前, 记录到日志系统 **
    doAothorize
    ** 执行share函数之前, 记录到日志系统 **
    share
    调用了: doAothorize
    调用了: share
    ** 执行surfTheInternet函数之前, 记录到日志系统 **
    直接上网,随便看点什么...

    可以看到,我们可以通过CommonProxy代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理吧。

    动态代理原理剖析

    下面我们来简单的分析一下动态代理的实现原理,主要的类是Proxy以及它的newProxyInstance函数。其实动态代理的原理大致是这样的,获取被代理对象的所有接口,因此可以通过反射获取到被代理对象所有的函数。JVM内部会构建一个继承自Proxy类,且实现了被代理对象所有接口的$Proxy+数字(例如$Proxy1)的类,这些工作都是通过Proxy.newProxyInstance函数实现,并且返回该$ProxyX类的对象。调用$ProxyX类对象的相关函数时,它会将其转发到InvocationHandler实现类中的invoke函数,因此我们可以在此进行其他额外的操作。下面,我们给main函数加上一些代码,如下 : 
    	public static void main(String[] args) {
    		
    		// 通用的动态代理类, 返回的是接口类型
    		Network network = CommonProxy.createProxy(new CommonNetwork());
    		network.surfTheInternet();
    		
    		// 输出network对象的类名
    		System.out.println(network.getClass().getName());
    		// 输出network所属类的所有函数
    		for (Method md : network.getClass().getDeclaredMethods()) {
    			System.out.println("函数 : " + md.getName());
    		}
    		
    		// 输出network所属类的所有函数
    		for (Class<?> cls : network.getClass().getInterfaces()) {
    			System.out.println("实现的接口 : " + cls.getName());
    		}
    		
    		// 输出network所属类的父类
    		System.out.println(network.getClass().getGenericSuperclass());
    		
    	}
    输出 : 
    直接上网,随便看点什么...
    com.thingking.in.java.proxy.$Proxy1
    函数 : equals
    函数 : toString
    函数 : hashCode
    函数 : surfTheInternet
    函数 : gotoFacebook
    实现的接口 : com.thingking.in.java.proxy.Network
    class java.lang.reflect.Proxy
    可以看到network所属的类是com.thingking.in.java.proxy.$Proxy1,其中.$Proxy之前是我的demo的报名,$Proxy1是network的类名。然后这个类实现的函数有equals, toString, hashCode, surfTheInternet, gotoFacebook这几个函数,前三个都是Object类的方法,后两个是Network接口的方法。实现的接口是Network,其父类是jdk中的java.lang.reflect.Proxy。输出的结果正好验证了我们上面所说的。
    下面是Proxy.newProxyInstance方法的简单模拟实现 ( 实际情况当然没有那么简单 ) : 

    [java]  view plain copy 在CODE上查看代码片 派生到我的代码片
    1. import java.io.File;  
    2. import java.io.FileWriter;  
    3. import java.lang.reflect.Constructor;  
    4. import java.lang.reflect.Method;  
    5. import java.net.URL;  
    6. import java.net.URLClassLoader;  
    7. import javax.tools.JavaCompiler;  
    8. import javax.tools.StandardJavaFileManager;  
    9. import javax.tools.ToolProvider;  
    10. import javax.tools.JavaCompiler.CompilationTask;  
    11.   
    12. public class Proxy {  
    13.     public static Object newProxyInstance(ClassLoader loader,Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM  
    14.         String methodStr = "";  
    15.         String rt = "\r\n";  
    16.         //利用反射,获得infce接口中方法,本例中就是获得Moveable接口的方法move  
    17.         Method[] methods = infce.getMethods();  
    18.         //拼接infce中所有方法字符串,用来重写infce中的方法,本例中拼出来就是重写的move方法  
    19.         for(Method m : methods) {  
    20.             methodStr += "@Override" + rt +   
    21.                          "public void " + m.getName() + "() {" + rt +  
    22.                          "    try {" + rt +  
    23.                          "    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +  
    24.                         /*方法最核心的代码,h是构造$Proxy1时传入的handler,本例中就是LogHandler对象,new Object[] { null}是move方法需要的参数,本例不需要,故为空。这一步将会使我们编写的处理类逻辑LogHandler的invoke方法得到调用。从而达到我们最初要在move方法前加日志逻辑的的目的,下面要做的就是把我们拼好的字符串生成类并load到内存就可以了这样就实现了动态生成代理类并加自己想加的逻辑*/  
    25.                          "    h.invoke(this, md,new Object[] { null});" + rt +  
    26.                          "    }catch(Exception e) {e.printStackTrace();}" + rt +  
    27.                           
    28.                          "}";  
    29.         }  
    30.           
    31.         String src =   
    32.             "package com.bjsxt.proxy;" +  rt +  
    33.             "import java.lang.reflect.Method;" + rt +  
    34.             //这里动态实现infce接口,本例中就是Moveable,构造方法中让Proxy持有处理类Handler的引用  
    35.             "public class $Proxy1 implements " + infce.getName() + "{" + rt +  
    36.             "    public $Proxy1(InvocationHandler h) {" + rt +  
    37.             "        this.h = h;" + rt +  
    38.             "    }" + rt +  
    39.               
    40.               
    41.             "    com.bjsxt.proxy.InvocationHandler h;" + rt +  
    42.             //这里是需要重写Moveable中的方法,见上面该字符串的拼接过程                
    43.             methodStr +  
    44.             "}";  
    45.         String fileName =   
    46.             "d:/src/com/bjsxt/proxy/$Proxy1.java";  
    47.         File f = new File(fileName);  
    48.         FileWriter fw = new FileWriter(f);  
    49.         fw.write(src);  
    50.         fw.flush();  
    51.         fw.close();  
    52.           
    53.         //compile编译上面拼好的字符串  
    54.         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();  
    55.         StandardJavaFileManager fileMgr = compiler.getStandardFileManager(nullnullnull);  
    56.         Iterable units = fileMgr.getJavaFileObjects(fileName);  
    57.         CompilationTask t = compiler.getTask(null, fileMgr, nullnullnull, units);  
    58.         t.call();  
    59.         fileMgr.close();  
    60.           
    61.         //load into memory and create an instance加载进内存并创建对象  
    62.         URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};  
    63.         URLClassLoader ul = new URLClassLoader(urls);  
    64.         Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");  
    65.         System.out.println(c);  
    66.           
    67.         Constructor ctr = c.getConstructor(InvocationHandler.class);  
    68.         Object m = ctr.newInstance(h);  
    69.           
    70.         //利用反射,这里就返回一个实现了infce接口也就是本例中Moveable接口的代理对像$Proxy1  
    71.         return m;  
    72.     }  
    73. }  
    需要注意的是这里的Proxy类和JDK中java.lang.reflect.Proxy并不相同。只是他们的原理大致相同,实现的细节并不同。JDK中生成$Proxy1并不是拼字符串,而是直接生成二进制码。
    展开全文
  • 动态代理与AOP----5 推理动态代理类内部结构II 动态代理类运行原理 ----------- android培训、java培训、java...这次我们继续讨论InvocationHandler接口的实现类如何与动态代理类进行结合以及动态代理类的运行原理

    动态代理与AOP----5

    推理动态代理类内部结构II

    动态代理类运行原理

    ----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

    上次课讨论了动态代理类的内部基本结构以及InvocationHandler的来历。这次我们继续讨论InvocationHandler接口的实现类如何与动态代理类进行结合以及动态代理类的运行原理。

    1.    推理动态代理类内部结构II

    1). 推断动态代理类的内部结构3-----从融合接口InvocationHandler类和动态代理类角度考虑

    (1). InvocationHandler实现类怎样融合到动态代理类中

    [1]. 假设InvocationHandler的实现类(设为MyInvocationHandler类)代码如下

    public class MyInvocationHandler {
        private ArrayList target =new ArrayList();
    public invoke(Object proxy, Method method,Object...args){
           //交叉业务
           method.invoke(target, args);
           //交叉业务
        }
    }
    

    现在的问题就是:独立出来的InvocationHandler的实现子类MyInvocationHandler如何起到原来$Proxy类中invoke的作用?

    [2]. InvocationHandler实现子类$Proxy的结合

    {1}. InvocationHandler实现子类$Proxy结合的可行性

    $Proxy0是动态产生的,不存在真实的文件系统的字节码文件。所以不能直接动态代理类$Proxy0直接结合

    {2}. InvocationHandler实现子类$Proxy直接父类Proxy类结合的可行性

    由于$Proxy类的直接父类Proxy不是动态产生的字节码文件,所以可以想办法将动态代理类的直接父类Proxy类InvocationHandler的实现子类进行组合。

    {2}1. 以继承的方式组合这两个类么?不可以,因为两个类之间不具备父子关系,所以不能写成继承关系。

    {2}2. 如果让Proxy类直接实现InvocationHandler接口,可以么?也不行。因为Proxy是Java的核心类,所有的方法都是写好的。但是InvocationHandler的invoke方法就是用来存放用户自定义的交叉业务的存储区域,所以也不能把Proxy类作为InvocationHandler的实现子类。

    {2}2. 那就把InvocationHandler接口的引用聚合的方式组装到Proxy中。也就是将InvocationHandler类型的引用以Proxy成员变量的形式和Proxy类组合。

    Proxy动态生成的动态代理类的子类的时候,动态代理类就会自动继承父类InvocationHandler属性了。

    因此注意到Proxy源代码中,InvocationHandler类型的属性访问修饰符就是protected类型不是private类型

    public class Proxy implements java.io.Serializable {
        //...
    protected InvocationHandler h;
    //...
    }

    (2). 初始化Proxy类的InvocationHandler成员的方式

    [1]. 常用初始化类的成员变量的方式

    {1}. 采用显示初始化方式

    就在声明成员变量的位置直接进行赋值。

    注意】这种方式用到Proxy的InvocationHandler类型的成员属性上并不可取!因为如果直接进行显示初始化的话,必须去对InvocationHandler的实现子类进行new但是Proxy类在定义的时候也不知道InvocationHandler的子类是谁,所以这种显示初始化的方式不可以

    {2}. 在指定的构造方法中通过对构造方法传参进行指定成员变量的初始化

    通过子类构造调用父类构造可以实现子类 (动态代理类实例) 实例在继承了父类的InvocationHandler的基础之上又通过调用的父类构造方法实现了对InvocationHandler属性的初始化。这种方式可行,下面详细阐述。

    (3). 实例化动态代理类需要调用的父类Proxy的构造方法解析

    [1]. 子类构造方法的特点【知识点回顾

    {1}. 任何子类构造方法直接或者间接在其第一行调用了父类的构造方法

    {1}1. 如果父类的所有的构造方法中非私有化空参的构造方法,并且子类构造方法第一行也没有显示指定,那子类构造第一行一定是调用父类非私有化空参构造方法

    {1}2. 如果父类的构造方法中不满足{1}1中的条件,此时子类构造方法必须显示在其第一行指定调用的父类构造方法

    [2]. Proxy构造方法解释

    {2}. $Proxy0的父类Proxy的构造方法有两个

    {2}1. private无参构造

    private Proxy() {}

    这个构造方法私有化完全是为了成全Proxy想成为工具类的愿望斯必须做的。但是这样一来,这个构造方法对Proxy的子类就是不可见的。所以$Proxy0的构造方法的第一行就必须指定一个父类其他的非私有的构造方法才可以。因此有了下面的第二个构造方法。

    {2}2. protected有参构造

    protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {this.h = h;}
    

    因此推断的动态代理类的构造方法必须写成:

    protected $Proxy0(InvocationHandler h) {
        super(h);
    }
    

    分析】这样当Proxy类通过自身的newProxyInstance(ClassLoaderloader, Class<?>[] interfaces, InvocationHandler h) 或者直接通过反射的形式proxyClass.getConstructor(newInvocationHandler(){

    });的方式动态生成代理类的实例的时候,就会在自身的构造方法中调用super(h);以达到对Proxy的InvocationHandler的属性进行初始化的效果。

    (4). 总结java.lang.reflect.Proxy的作用

    [1]. 以工具类的身份出现:所有的public方法全部静态化并且空参构造私有化

    ----主要作用直接通过getProxyClass( )生成没有字节码的子类Class对象

    [2]. 以动态代理类的直接父类出现

    ----主要作用存储动态代理类要调用的含有交叉业务代码目标类对象的invoke方法。

    2.    动态代理类的工作原理

    知道了InvocationHandler实现子类的如何在动态代理类中起作用之后,可以分析一下动态代理类运行的原理了。

    1). 用代理类实例运行的原理图


    图中的圈1,圈2,圈3,圈4分别代表动态代理类的运行步骤的四个步骤

    2). 动态代理类的运行步骤

    (1). 将InvocationHandler实现子类的实例聚合到Proxy的成员属性上

    聚合方式有两种:

    [1]. 向动态代理类Class对象getConstructor()方法传入InvocationHandler子类对象

    通过Proxy类的getProxyClass()方法获取动态代理类Class对象

    [2]. 直接Proxy.newProxyInstance()方法传入InvocationHandler子类对象

    结论】生成了动态的代理类内存字节码并且存储交叉业务+ 对目标类方法的调用的代码聚合到了动态代理类实例对象中。

    (2). 客户端Client方法调用代理类实例某个某几个方法

    (3). 代理类实例的各个方法会将客户端对自己方法的请求指派给从父类继承的InvocationHandler引用invoke方法

    (4). InvocationHandler引用invoke方法将对代理类各个方法的请求转发给目标类对象相应的方法

    ----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

    展开全文
  • Java动态代理作用及好处

    万次阅读 2018-07-15 17:49:02
    Java动态代理的优势是实现无侵入式的代码扩展;也就是方法的增强,让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。 1.动态代理是设计模式中...

    Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。

    1.动态代理是设计模式中的代理模式:

    定义:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。

    2.静态代理
    • 静态代理类:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。
    • 静态代理类通常只代理一个类。
    • 静态代理事先知道要代理的是什么。
    3.动态代理
    • 动态代理类:在程序运行时,通过反射机制动态生成。
    • 动态代理类通常代理接口下的所有类。
    • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
    • 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
    • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。
    展开全文
  • JDK动态代理为什么不能代理类

    千次阅读 2019-10-15 07:55:17
    戳蓝字「TopCoder」关注我们哦!关于JDK动态代理,想必小伙伴都知道,它只能代理接口!但是有的小伙伴可能会有疑问,为什么它只能代理接口,不能代理类呢?这里借助某位技...

    戳蓝字「TopCoder」关注我们哦!

    关于JDK动态代理,想必小伙伴都知道,它只能代理接口!但是有的小伙伴可能会有疑问,为什么它只能代理接口,不能代理类呢?这里借助某位技术大佬的话--”源码面前,了无秘密“,下面咱们就一起看下JDK动态代理源码实现,最后再探讨下JDK动态代理为什么不能代理类,OK,话不多说,Let's go~

    本文分为两部分来分析JDK动态代理,首先看下JDK动态代理使用,然后分析动态代理源码实现及探讨下其为什么不能代理类。(ps:已经熟悉JDK动态代理基础概念及使用的小伙伴可以跳过第一部分)

    JDK动态代理使用

    Java在JDK1.3后引入的动态代理机制,使我们可以在运行期动态的创建代理类。使用JDK动态代理需要有四个角色:被代理的类,被代理类的接口,织入器,和InvocationHandler,而织入器使用接口(通过生成字节码机制)生成一个代理类,然后在这个代理类中织入代码(被代理的类是AOP里所说的目标,InvocationHandler是切面)。

    动态代理是在运行期间通过接口生成代理类的,与静态代理相比更加灵活,但是也有一定的限制,第一是代理对象必须实现一个接口,否则会报异常,因为人家原理就是根据接口来生成代理对象的。第二是有性能问题,因为是通过反射来实现调用的,所以比正常的直接调用来得慢,并且通过生成类文件也会多消耗部分方法区空间,可能引起Full GC。

    一个JDK动态代理的简单使用:

    public static interface Hello {    void hi(String msg);}public static class HelloImpl implements Hello {    @Override    public void hi(String msg) {        System.out.println("hello " + msg);    }}
    /** * 代理类 */@Data@NoArgsConstructor@AllArgsConstructorpublic static class HelloProxy implements InvocationHandler { private Object proxied = null;
    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("hello proxy"); return method.invoke(proxied, args); }}
    public static void main(String[] args) { Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class[]{Hello.class}, new HelloProxy(new HelloImpl()));
    System.out.println(hello.getClass()); // class com.sun.proxy.$Proxy0 hello.hi("world");}

    通过调用Proxy静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器,一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler的一个实现类。动态代理可以将所有调用重定向到调用处理器,因此通常会调用处理器的构造器传递一个”实际”对象的引用,从而将调用处理器在执行中介任务时,将请求转发。

    上述示例代码我们看到了method.invoke,这不是反射么,动态代理和反射到底是个什么关系?动态代理底层是基于反射来实现的,这么说也不算错但稍微有些不全面。以上述代码为例,首先是写一个InvocationHandler实现类并调用Proxy.newProxyInstance生成代理类,然后在InvocationHandler实现类中通过反射调用对应的代理方法。最后看下上述代码的调用栈信息:在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用。在调用超过 15 次之后,委派实现便会将委派对象切换至动态实现。这个动态的字节码是在Java运行过程中通过ASM自动生成的,它将直接使用 invoke 指令来调用目标方法。由于本文重点是讨论动态代理,所以针对反射就不再详细讨论了。

    动态代理原理

    以上述示例代码为例,默认情况下在运行时程序自动生成proxy class,如果我们想看下自动生成proxy clas到底长什么样该如何办呢?可以通过参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true(或者执行System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");)来保存成对应的class文件,比如示例代码中生成的class文件如下(在IDEA中查看源码):

    //// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//
    package com.sun.proxy;
    import com.youzan.owl.goingmerry.util.ProxyMain.Hello;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;
    public final class $Proxy0 extends Proxy implements Hello { private static Method m1; private static Method m3; private static Method m2; private static Method m0;
    public $Proxy0(InvocationHandler var1) throws { super(var1); }
    public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
    public final void hi(String var1) throws { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
    public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
    public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
    static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.youzan.owl.goingmerry.util.ProxyMain$Hello").getMethod("hi", Class.forName("java.lang.String")); 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()); } }}

    下面开始分析JDK动态代理生成proxy class流程源码,以上述示例代码为例,从Proxy.newProxyInstance()方法开始,由于篇幅有限,以下代码分析只贴出主要流程代码。首先加载被代理类的接口的class,然后通过执行方法java.lang.reflect.Proxy.ProxyClassFactory#apply创建对应proxy class:

    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } /* * 判断不是接口则抛异常,说明了JDK动态代理只能对接口代理 * 不过到这里还不能说明为什么不能对类进行代理 */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //... }
    String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } }
    if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package // public static final String PROXY_PACKAGE = "com.sun.proxy"; // 默认生成的proxy class 包路径 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; }
    /* * Choose a name for the proxy class to generate. * 自动生成一个proxy class,序号+1 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; // proxyClassNamePrefix = "$Proxy";
    /* * Generate the specified proxy class. * 开始生成动态proxy class,这里使用了ProxyGenerator类 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }}

    关于ProxyGenerator.generateProxyClass(),感兴趣的同学可以自行阅读对应代码,其内部主要就是按照固定“模板”按照字节码格式生成对应class字节码,大致就是实现要代理接口的所有public方法及固定的方法(hashCode、equals和toString),然后调用InvocationHandler的invoke方法,也就是走到示例代码中的HelloProxy类(class HelloProxy implements InvocationHandler)。

    其实我们还可以在程序中直接调用ProxyGenerator.generateProxyClass()来生成动态代理类,如下所示:

    String name = "$Proxy00";byte[] data = ProxyGenerator.generateProxyClass("com.sun.proxy." + name, new Class[] { Hello.class } );FileOutputStream out = new FileOutputStream("/tmp/" + name + ".class" );out.write(data);out.close();

    这里可以对比下$Proxy00.class和JDK动态代理生成的$Proxy0.class文件,二者除了类名之外,代码都是相同的。

    最后来看下JDK动态代理为什么不能代理类,对照着生成的$Proxy0.class文件,如下所示一起来分析下:

    public final class $Proxy0 extends Proxy implements Hello {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;
    public $Proxy0(InvocationHandler var1) throws { super(var1); }
    public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
    public final void hi(String var1) throws { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }

    类Proxy的作用简单来说就是保存下用于自定义的InvocationHandler,便于在方法代理时执行自定义InvocationHandler的逻辑。由于$Proxy0已经继承了类Proxy,所以不能再extends一个类了,所以只能implements一个接口了。


    参考资料:

    1. https://mp.weixin.qq.com/s/58CkiW1ZjqiwLWctJbBVFg

    2. https://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

     推荐阅读 


    展开全文
  • 代理模式与Java动态代理类

    千次阅读 2006-09-03 12:49:00
    对代理模式与Java动态代理类的理解1. 代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端
  • 关于java动态代理作用,小白理解

    千次阅读 多人点赞 2018-08-24 16:20:07
    关于动态代理这个词,嗯......太高深了,这里先咳嗽两声。 我今天买了两本书,一本英语,一本数学,书可以读,看。 public interface Book { void readTheBook(); } 英语和数学,虽然是书,但内容不一样。 ...
  • 作者:Intopass ...来源:知乎 著作权归作者所有。商业转载请联系作者获得...① 首先你要明白静态代理作用 我们有一个字体提供,有多种实现(从磁盘,从网络,从系统) public interface FontProvider { Font...
  • 浅析动态代理类实现过程

    千次阅读 2007-02-07 14:52:00
    代理模式代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用代理模式一般涉及到的...
  • 动态代理是许多框架底层实现的基础,比如Spirng的AOP等,其实弄清楚了动态代理的实现原理,它就没那么神奇了,下面就来通过案例和分析JDK底层源码来揭秘她的神秘面纱,让她一丝不挂地呈现在我们面前,邪恶了。...
  • java代理模式及动态代理类

    千次阅读 2005-03-08 14:22:00
    :该类即为动态代理类作用类似于上例中的 ProxySubject ,其中主要包含以下内容: Protected Proxy(InvocationHandler h) :构造函数,估计用于给内部的 h 赋值。 Static Class getProxyClass (ClassLoader ...
  • Java 动态代理作用是什么?

    千次阅读 2016-09-12 12:00:22
    首先你要明白静态代理作用我们有一个字体提供,有多种实现(从磁盘,从网络,从系统) public interface FontProvider { Font getFont(String name); } public abstract class ProviderFactory { public...
  • 对代理模式与Java动态代理类的理解

    千次阅读 2007-01-23 20:49:00
    1. 代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用代理模式一般涉及...
  • 动态代理

    千次阅读 2019-03-23 19:29:52
    动态代理实例: public class ProxyTest { public static void main(String[] args) { //1.创建真实对象 Lenovo lenovo = new Lenovo(); /* 动态代理的注意事项: 1.动态代理对接口有效. 2.动态代理...
  • 对代理模式与Java动态代理类的理解

    千次阅读 2005-01-25 16:32:00
    1. 代理模式代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用代理模式一般涉及到...
  • 一个实现了InvocationHandle的代理类,然后通过Proxy类下的静态方法newProxyInstance进行织入产生一个融合了代理类和被代理类的真正的代理对象。 就我的理解而言,动态代理就是在执行的被代理对象的方法前后...
  • 动态代理由浅入深

    2018-09-04 18:45:02
    动态代理作用:可以在不改变原有的基础上对方法进行增强。执行代理对象的任何方法,就需要执行invocationHandler的invoke方法。本文对以上内容进行了举例分析。
  • 动态代理和Threadlocal 一个代理类返回指定的接口,将方法调用指定的调用处理程序的代理类的实例。 返回的是一个代理类,由指定的类装载器的定义和实现指定接口指定代理实例调用处理程序 最近用到一个工具类,里面...
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...
  • Spring AOP的作用动态代理模式

    千次阅读 2019-02-26 17:16:40
    AOP即面向切面编程。AOP是基于代理模式的。 代理模式: 当我们需要修改一个类,在类中加入代码时,为了不破坏这个类的封装性。...用代理模式就是,建立一个代理类,在调用saveUser()方法之前再加...
  • JDK动态代理

    千次阅读 2019-12-06 15:47:58
    JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。只支持接口的代理。 动态代理是通过JDK的Proxy和一个调用处理器InvocationHandler来实现的,通过Proxy来...
  • 动态代理 代理模式 AOP:面向切面的编程 动态代理实现 动态代理案例1—-实现编码过滤 动态代理案例2—细粒度的权限控制 加载器 全盘负责委托机制 自定义加载器 泛型反射
  • 动态代理及JDK动态代理源码分析

    千次阅读 2014-04-14 23:16:41
    JAVA的动态代理机制可以动态的创建代理并动态的处理代理方法调用,只要简单指定一组接口及为拖累对象,就能动态的获取代理类
  • spring 动态代理

    千次阅读 2019-03-15 21:38:59
    概括: spring aop的实现原理: 动态代理 cglib代理 动态代理 代理模式: 静态代理 ... 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消...
  • 拿到被代理类的引用,并获取所有接口信息(反射获取) jdk proxy.newInstance生成新的类,实现接口方法 动态生成java代码,增强逻辑代码也写入 编译java代码生成class文件 加载并运行新的class jdk动态代理调用...
  • 实例理解JDK动态代理和Cglib动态代理及其区别 深入理解设计模式之代理模式 代理商代理化妆品生产理解JDK动态代理 代理商代理汽车制造理解Cglib动态代理

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 154,565
精华内容 61,826
关键字:

动态代理类的作用