JDK与CGlib动态代理(实例说明JDK动态代理执行过程)

 1.什么是动态代理


所谓代理模式,就是指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。这种模式有什么用呢?它可以在原对象的基础上增强原对象的功能,比如在原对象调用一个方法的前后进行日志、事务操作等。Spring AOP就使用了代理模式。
而动态代理模式就是指在程序运行期间根据需要动态地创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和CGlib动态代理两大类,本文主要对JDK动态代理进行探讨。
 

2.JDK动态代理实例


建立项目如下:
 1.ICalculatorService接口源码:

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);
}

2.CalculatorService实现类源码:

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

3.ProxyFactory动态代理类源码:

public class ProxyFactory {//动态(程序运行时实现和目标类相同接口的java类)代理()

	public static Object get(CalculatorService calculatorService) {
		return Proxy.newProxyInstance(calculatorService.getClass().getClassLoader(), new Class[] {ICalculatorService.class}, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过重写InvocationHandler()类中的invoke方法,来设定动态代理类需要在被代理类的基础上添加哪些功能
				System.out.println(method.getDeclaringClass().getName());
				String name = method.getName();
				System.out.println(calculatorService.getClass().getName()+":The "+name+" method begins.");
				System.out.println(calculatorService.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
				Object result = method.invoke(calculatorService, args);//被代理类的目标方法
                //这里虽然calculatorService是接口的实现类,但其实是多态,呈现的是被代理类中的方法
				System.out.println(calculatorService.getClass().getName()+":Result of the "+name+" method:"+result);
				System.out.println(calculatorService.getClass().getName()+":The "+name+" method ends.");
				return result;
			}
		});//产生一个动态class类,
	}

4.Test测试类源码:

public static void main(String[] args) {
	System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    //生成动态类的编译文件,以便进行进一步分析
	ICalculatorService calculatorService = (ICalculatorService) new ProxyFactory().get(new CalculatorService());//获取代理对象
	int result = calculatorService.add(1, 1);//调用代理对象进行测试
	System.out.println("-->"+result);
}

我们由main方法一步步分析:

第一行:

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

这一行其实是生成了一个动态类的编译文件,我们可以在com.sun.proxy文件夹中找到,对其进行反编译后得到了动态类的实例化对象(由于main方法中只讨论了add方法,为方便理解,在此将实例化对象中的其他部分删去):

import com.jd.calculator.ICalculatorService;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements 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 
    {
        try
        {
            
            m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] {
                Integer.TYPE, Integer.TYPE
            });
           
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

第二行:(同时也是最重要的一行)

ICalculatorService calculatorService = (ICalculatorService) new ProxyFactory().get(new CalculatorService());

这一行调用了ProxyFactory()中的get方法,其实就是由Proxy的newProxyInstance方法获得了动态代理类。

接下来进入Proxy中的newProxyInstance方法进行进一步分析:

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.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            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;
                    }
                });
            }
            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);
        }
    }

其实这么多代码中只有这几行是对我们理解起来有帮助的:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});

 

 分别为: 

  • 第一行告诉了我们这个方法的三个参数。

1.指定类的类加载器(这里我们其实使用了ProxyFactory的类加载器,事实上用什么无所谓)

2.代理类需要实现的接口(这里我们传入了被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)(注:代理类和被代理类实现的接口必须相同)

3.invocation handler,用来处理方法的调用。这里传入我们自己实现的handler,即ProxyFactory中的那个内部类

  • 第二行意味着创建了一个动态代理类
  • 第三行意味着得到了代理类对象的构造方法,这个构造方法的参数由constructorParams指定,而参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class }
  • 第四行意味着通过刚才获得的构造方法创建了一个代理类的对象,这里的参数就使用了我们自己实现的handler

调用了构造方法,就意味着使用了刚才我们的得到的代理类对象的构造方法:

public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

佐证了构造方法是使用我们自己实现的handler作为参数的这一事实。

接下来回到main方法看第三行:

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

这里调用了刚刚创建的代理类对象,观察相关源码:

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 
    {
        try
        {
            
            m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] {
                Integer.TYPE, Integer.TYPE
            });
//try后面的部分于理解意义不大,故略去

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

知道这里其实调用了我们定义的handler的invoke方法。三个对象分别是这个实现类对象自己,m3对应的被代理类的方法以及计算所需参数。其中的Integer和.intValue()等都是数据类型转换相关内容,于理解并无影响。

因此最后可以获得结果:

 3.CGlib动态代理

CGlib的动态代理方法如下:(接口与被代理类与上例相同)

public class Test {
	
	private static Callback callback = new MethodInterceptor() {
		@Override
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			String name = method.getName();
			System.out.println(obj.getClass().getName()+":The "+name+" method begins.");
			System.out.println(obj.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
			Object result = proxy.invokeSuper(obj, args);//目标方法
			System.out.println(obj.getClass().getName()+":Result of the "+name+" method:"+result);
			System.out.println(obj.getClass().getName()+":The "+name+" method ends.");
			return result;
		}
	};
	
	private static Object get() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(CalculatorService.class);
		enhancer.setCallback(callback);
		return enhancer.create();//创建动态类对象
	}
	
	
	public static void main(String[] args) {
		ICalculatorService calculatorService = (ICalculatorService)get();//获取代理对象
		int result = calculatorService.add(1, 1);
		System.out.println("-->"+result);
	}
}

4.JDK动态代理与CGlib动态代理的区别

1.JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2.JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法,被代理类和代理类是兄弟关系;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CGlib是一个针对Java字节码的代码生成库,它可以在运行时扩展Java类和实现接口的功能。它通过生成目标类的子类来实现代理,从而实现动态代理的功能。 在使用CGlib动态代理时,首先需要添加CGlib库的依赖。然后,创建一个Enhancer对象,并设置被代理类作为目标类。接下来,可以通过调用Enhancer对象的方法来设置回调函数和拦截器等。最后,使用Enhancer对象的create方法生成代理类的实例。 以下是一个简单的示例代码,演示了如何使用CGlib动态代理: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class Main { public static void main(String[] args) { // 创建Enhancer对象 Enhancer enhancer = new Enhancer(); // 设置被代理类 enhancer.setSuperclass(TargetClass.class); // 设置回调函数 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在方法调用前进行拦截处理 System.out.println("Before method: " + method.getName()); // 调用被代理类的方法 Object result = proxy.invokeSuper(obj, args); // 在方法调用后进行拦截处理 System.out.println("After method: " + method.getName()); return result; } }); // 生成代理类的实例 TargetClass proxy = (TargetClass) enhancer.create(); // 调用代理类的方法 proxy.doSomething(); } } class TargetClass { public void doSomething() { System.out.println("Doing something..."); } } ``` 在上面的示例中,我们创建了一个名为TargetClass的被代理类。然后使用CGlib动态代理生成了一个代理类的实例,并通过调用代理类的方法来间接调用被代理类的方法。在方法调用前后,我们可以通过设置回调函数来添加额外的处理逻辑。 需要注意的是,CGlib动态代理是通过继承来实现的,因此对于final类和方法无法进行代理。另外,由于CGlib是基于字节码操作实现的,所以在生成代理类的过程中会比较耗时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值