JDK中的动态代理

在学习Spring的时候,我们知道Spring主要有两大思想,一个是IOC控制反转,另一个就是AOP面向切面编程,AOP的原理就是java的动态代理机制。
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个是Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的。

InvocationHandler(Interface)

在这里插入图片描述
大概意思就是说每一个动态代理类都要实现InvocationHandler,当通过Proxy代理对象调用被代理对象的方法的时候,这个方法就会调用InvocationHandler的invoke方法,实现它,利用多态的动态绑定,实现动态代理!这是InvocationHandler唯一的方法

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数

当代理对象调用被代理对象方法时会把三个参数对应进去,下面有示例

Proxy(Class)

接下来我们来看看Proxy这个类:

/** 
 * {@code Proxy} provides static methods for creating dynamic proxy
 * classes and instances, and it is also the superclass of all
 * dynamic proxy classes created by those methods.
 * 此处略去好多注释...
 */

类的开头注释中写到,可以通过私有的静态方法创建动态代理对象,它也是所有需要创建动态代理对象的超类,简而言之,要实现动态代理,就要继承Proxy这个类。
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
在这里插入图片描述

    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     * 此处略去好多注释...
     */
     public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    	// 此处略去好多代码...
    	return cons.newInstance(new Object[]{h});
    }

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上(就是要去实现InvocationHandler的那个实现类)

示例

现在通过一个示例来看下动态代理模式是什么样的
声明一个接口(JDK代理的缺点,必须以实现接口的形式·,否则代理对象强制转换会出错)

public interface Animal {
	public String cry(String str);
	public void eat(String str);
}

声明接口的实现类

public class Cat implements Animal{
	@Override
	public String cry(String str) {
		System.out.println("猫"+str+"叫");
		return str;
	}
	@Override
	public void eat( String str ) {
		System.out.println("猫吃"+str);
	}
}

下一步要实现动态代理处理器接口了

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AnimalHandller implements InvocationHandler{
	private Object obj;
	public AnimalHandller(Object obj) {
		this.obj = obj;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("这里是代理方法开始");
		System.out.println("方法"+method);
		Object invoke = method.invoke(obj, args);
		System.out.println("这里是代理方法结束");
		return invoke;
	}
}

写个main方法,创建代理对象调用一下

import java.lang.reflect.Proxy;
public class ProxyClient {
	public static void main(String[] args) {
		Animal cat = new Cat();
		AnimalHandller handller = new AnimalHandller(cat);
		Animal instance =  (Animal)Proxy.newProxyInstance(ProxyClient.class.getClassLoader(), cat.getClass().getInterfaces(), handller);
		System.out.println(instance.getClass().getName());
		String cry = instance.cry("喵喵喵");
		System.out.println(cry);
		instance.eat("猫粮");
	}
}

控制台打印信息

com.sun.proxy.$Proxy0
这里是代理方法开始
方法public abstract java.lang.String com.sgcc.uap.proxy.Animal.cry(java.lang.String)
猫喵喵喵叫
这里是代理方法结束
喵喵喵
这里是代理方法开始
方法public abstract void com.sgcc.uap.proxy.Animal.eat(java.lang.String)
猫吃猫粮
这里是代理方法结束

由打印信息可见,创建的对象是代理对象,并非Animal对象
原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口是Animal类型,所以就可以将其转化为Animal类型了

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

看下方法调用
在这里插入图片描述
在这里插入图片描述
代理对象调用cry方法时,跳转到AnimalHandller动态代理处理器类执行了invoke方法

方法public abstract java.lang.String com.sgcc.uap.proxy.Animal.cry(java.lang.String)
方法public abstract void com.sgcc.uap.proxy.Animal.eat(java.lang.String)

而打印的两个method正好就是我们的Animal接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。这就是我们的java动态代理机制。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值