JDK动态代理的执行过程分析

一、 源代码

  1. 目标类:CalculatorService
    package com.jd.calculator;
    
    public class CalculatorService implements ICalculatorService {
     
    	@Override
    	public int add(int a, int b) {
    		int result = a+b;
    		return result;
    	}
     
    	@Override
    	public int sub(int a, int b) {
    		int result = a-b;
    		return result;
    	}
     
    	@Override
    	public int mul(int a, int b) {
    		int result = a*b;
    		return result;
    	}
     
    	@Override
    	public int div(int a, int b) {
    		int result = a/b;
    		return result;
    	}
    }
    
  2. 目标类所实现的接口:ICalculatorService
    package com.jd.calculator;
     
    public interface ICalculatorService {
     
    	int add(int a,int b);
    	
    	int sub(int a,int b);
    	
    	int mul(int a,int b);
    	
    	int div(int a,int b);
    }
    
  3. 测试类:Test
    package com.jd.test;
     
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
     
    import com.jd.calculator.CalculatorService;
    import com.jd.calculator.ICalculatorService;
     
    public class Test {
    	
    	//动态(程序运行时实现和目标类相同接口的java类)代理
    	
    	CalculatorService calculatorService;
    	
    	public Test(CalculatorService calculatorService) {
    		this.calculatorService = calculatorService;
    	}
    	
     
    	InvocationHandler h = new InvocationHandler() {//h为匿名内部类对象
    		@Override
    		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    				
    			System.out.println(proxy.getClass().getName());
    			System.out.println(method.getDeclaringClass().getName());
    			String name = method.getName();
    			System.out.println(this.getClass().getName()+":The "+name+" method begins.");
    			System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
    			Object result = method.invoke(calculatorService, args);//目标方法
    			System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
    			System.out.println(this.getClass().getName()+":The "+name+" method ends.");
    			return result;
    		}
    	};
    	
    	public Object get() {  //产生一个动态class类,返回一个动态代理类的对象
    		return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);
    	}
    	
    	public static void main(String[] args) {
    		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//编译文件
    		Test test = new Test(new CalculatorService());
    		ICalculatorService calculatorService = (ICalculatorService) test.get();//获取代理对象例对象
    		System.out.println(calculatorService.getClass().getName());
    		int result = calculatorService.add(1, 1);//执行动态类中的add方法,并返回int值
    		System.out.println("-->"+result);
    	}
    }
    
  4. 程序执行结果

    com.sun.proxy. P r o x y 0 c o m . s u n . p r o x y . Proxy0 com.sun.proxy. Proxy0com.sun.proxy.Proxy0
    com.jd.calculator.ICalculatorService
    com.jd.test.Test$1:The add method begins.
    com.jd.test.Test$1:Parameters of the add method: [1,1]
    com.jd.test.Test$1:Result of the add method:2
    com.jd.test.Test$1:The add method ends.
    –>2


二、 执行过程分析

1. 生成使用JDK创建动态代理对象的class文件(即$Proxy0.Class)

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

2. 为Test类中的成员变量calculatorService赋值

Test test = new Test(new CalculatorService());

3. 执行Test中的get()方法

通过 Proxy类中的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)产生一个动态类,并且返回一个动态类的实例对象。

ICalculatorService calculatorService = (ICalculatorService) test.get();

分析具体执行过程:

  1. 找到get()方法,进行简单剖析
    public Object get() {
    	return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);
    }
    

    Proxy:动态代理类
    newProxyInstance:动态代理类中的方法
    newProxyInstance方法有三个参数:
    ①loader: 用哪个类加载器去加载代理对象——>此例子用Test类加载器
    ②interfaces:动态代理类需要实现的接口——>实现的接口是ICalculatorService
    ③ h:动态代理方法在执行时,会调用h里面的invoke方法去执行

  2. 进入到newProxyInstance()方法中,进行详细的分析
     @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
     
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
     
            /*
             * Look up or generate the designated proxy class.
             */
    (1)     Class<?> cl = getProxyClass0(loader, intfs);
     
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
     
    (2)         final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
    (3)         return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    
    
    如代码中表示的序号分析主要程序:
    1. Class<?> cl = getProxyClass0(loader, intfs); ——>生成指定接口ICalculatorService的class对象的代理类
    2. final Constructor<?> cons = cl.getConstructor(constructorParams);
      final InvocationHandler ih = h;
      ——>获取代理类的构造函数对象
      参数constructorParames为传入的参数,即为常量值:
      private static final Class<?>[] constructorParams = { InvocationHandler.class };
    3. return cons.newInstance(new Object[]{h}); ——>根据代理类的构造函数对象来创建代理类对象并返回

4. 验证得到的代理对象calculatorService和invoke()中传匿名内部类中传入的proxy是相同的

System.out.println(calculatorService.getClass().getName());

5. 执行动态类对象中的add方法,并返回int值

int result = calculatorService.add(1, 1);

分析具体执行过程:

通过Java反编译器,可以得到编译后的java代码表示的class文件(即动态代理类),下面是为了容易理解,删去部分代码后得到的:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
 
package com.sun.proxy;
 
import com.jd.calculator.ICalculatorService;
import java.lang.reflect.*;
 
public final class $Proxy0 extends Proxy
    implements ICalculatorService //继承了Proxy类和实现ICalculatorService接口
{
 
    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }
 
    public final int add(int i, int j)
    {  //接口代理方法
        try
        {
            return ((Integer)super.h.invoke(this, m3, new Object[] {
                Integer.valueOf(i), Integer.valueOf(j)
            })).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
 
    private static Method m3;
    static 
    {//对变量进行一些初始化工作
            m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] {
                Integer.TYPE, Integer.TYPE
            });
        }
    }
}
  1. 如下代码为动态类的构造函数,其参数正是InvocationHandler实例,Proxy.newProxyInstance()方法就是通过这个构造函数来创建代理实例。

    public $Proxy0(InvocationHandler invocationhandler){
    	super(invocationhandler);
    }
    
  2. 点击进入super方法,可以知道第三步中返回的代理类对象中执行的有参构造方法,就是这个代理类中的有参构造方法;这个构造方法为父类Proxy中的h赋值,得到了一个代理对象。

     protected InvocationHandler h;
     
        /**
         * Prohibits instantiation.
         */
        private Proxy() {
        }
     
        /**
         * Constructs a new {@code Proxy} instance from a subclass
         * (typically, a dynamic proxy class) with the specified value
         * for its invocation handler.
         *
         * @param  h the invocation handler for this proxy instance
         *
         * @throws NullPointerException if the given invocation handler, {@code h},
         *         is {@code null}.
         */
        protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }
    
  3. 此代理类$Proxy0继承了Proxy类和实现ICalculatorService接口,可知与CalculatorService类实现的是同一接口,即可以说是代理类与目标类是兄弟同级关系;且通过第三步赋值知道calculatorService是动态代理对象。

  4. 分析执行代理类中的add()方法

    1. add()方法中

      return (Integer)super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j)
      

      由于上面调用有参构造方法$Proxy0(InvocationHandler invocationhandler)

      给这个类的父类Proxy中invocationHandler赋的值为invocationHandler对象,所以这里super.h是invocationHandler

      super.h.invoke相当于invocationHandler.invoke,也就是调用Test.java匿名内部类invocationHandler中的invoke()方法。

    2. 下面对传入的参数进行解释:
      this:就是调用invoke方法的对象,calculatorService的值,也就是代理对象;
      m3:就是method,也就是代码最下面静态代码块中加载的ICalculatorService接口中的add方法;
      传入的常量值i,j : 在这里是先将二者转换成了Integer类型;

  5. 分析add()方法中涉及执行的invoke()方法

    InvocationHandler h = new InvocationHandler() {
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println(proxy.getClass().getName());
    		System.out.println(method.getDeclaringClass().getName());
    		String name = method.getName();
    		System.out.println(this.getClass().getName()+":The "+name+" method begins.");
    		System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
    		Object result = method.invoke(calculatorService, args);//目标方法
    		System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
    		System.out.println(this.getClass().getName()+":The "+name+" method ends.");
    		return result;
    	}
    };
    

    分析:

    invoke三个参数:
    proxy:就是代理对象,newProxyInstance方法的返回对象
    method:调用的方法
    args: 方法中的参数

    ①method.invoke(calculatorService, args); ——>相当于调用的calculatorService中的add方法,将得到的结果返回给result;

    ②匿名内部类的invoke方法执行结束,代理类中的super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j);这部分代码执行结束。

    ③由于匿名内部类的invoke方法是Object类,所以要将其转换成Integer类,再调用intValue(),将其转换成int类型。

    ④至此代理类中的add()方法结束,并将结果返回给Test中main方法第五行里面的result。


三、 JDK动态代理和CGLib动态代理的区别

1. 动态代理:

在程序运行期间JVM根据需要通过反射等机制动态地创建代理类及其代理对象。

注意:

  1. 代理类是在程序运行期间创建,可以在classpath目录(在Eclipse中,Java工程中的bin目录;Web工程中的build目录)中看到该文件,
  2. 代理类和委托类的关系是在程序运行时确定的。

2. 动态代理优点

  1. 静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
  2. 动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。

3. JDK动态代理

程序执行时使用java.lang.reflect包中Proxy类与InvocationHandler接口动态地生成一个实现代理接口的匿名代理类及其对象,无论调用代理对象哪个方法,最终都会执行invoke方法。

4. CGLib动态代理

程序执行时通过ASM(开源的Java字节码编辑库,操作字节码)jar包动态地为被代理类生成一个代理子类,通过该代理子类创建代理对象,由于存在继承关系,所以父类不能使用final修饰。

5. JDK动态代理与CGLib动态代理区别

  1. JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
  2. JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值