代理模式(二):JDK动态代理

动态代理分为两种,JDK动态代理和cglib动态代理。上一篇我们讨论了静态代理,最后留了2个问题:

1.当被代理的对象不存在或未知时,代理对象如何创建呢?
2.在实际问题中可能存在很多的类需要使用代理对象,而一个静态代理对象只能代理一个类,这样就会反复的创建相似的代理类,造成类的数量成倍增长,这个问题又该怎么解决呢?

JDK动态代理就是为了解决这两个问题而来的,同时它还有很多其他特点,带着问题我们细细说。

一、特点

和静态代理相同的是,JDK动态代理同样具有以下4个类:
1.接口类:定义被代理对象所具有的功能方法

2.被代理类:需要继承上面的接口并做具体实现

3.代理类(代理类的调用处理程序):用于代理被代理类的功能,它也需要继承上面的接口。与静态代理不同,在JDK动态代理中,这个类并不是正真意义上的代理类,它只是代理类的调用处理程序,而真实的代理类在客户类中。它需要做3件事:

  1. 实现InvocationHandler接口并实现该接口中的invocat()方法(难点
  2. 它需要创建一个未初始化的真实的被代理对象和有参构造,用于传入真实的被代理对象,(注意,代理类的调用处理程序并不直接拥有真实的代理对象,而是通过有参构造的方式传递真实的被代理对象)
  3. 在invocat()方法中添加代理类在被代理方法前后要实现的功能

4.客户类:也就是程序的主类,由它向代理类的调用处理程序传递真实的被代理对象,通过Proxy类中的newProxyInstance()方法创建真实的代理对象,并由它来调用被代理对象中的方法。

二、核心内容

在JDK动态代理中,最核心的内容有两点:

1. InvocationHandler接口
源码的解释是这样的:
它是由代理实例的调用处理程序 实现的接口。
每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用被编码并分派给其调用处理程序的invoke()方法。

 * {@code InvocationHandler} is the interface implemented by
 * the <i>invocation handler</i> of a proxy instance.

 * <p>Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method
 * invocation is encoded and dispatched to the {@code invoke}
 * method of its invocation handler.

也就是说,每一个代理类的调用处理程序都必须实现该接口。

2. 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类中提供的一个最常用的方法newProxyInstance()方法,该方法返回一个具有代理类的指定调用处理程序的代理实例,该代理类由指定的类加载器定义并实现指定的接口。

三、实例

简单说这么多,理论本来就是个抽象的东西,千言万语不如几行代码,我们直接看例子:

(1)接口类和被代理类和静态代理一样,不多解释

//接口
public interface Subject {
	public void request();
}
//被代理对象
public class RealSubject implements Subject{
	public RealSubject() {
	}
	@Override
	public void request() {
		System.out.println("我是工程师,这个事很简单!");
	}
}

(2)这个类是代理类的调用处理程序,每一个动态代理类的调用处理程序都必须实现invocationHandler接口

public class ProxySubject implements InvocationHandler{
	//被代理的对象(工程师)
	private Object sub;
	//构造方法,用于引入被代理的对象
	public ProxySubject(Object obj) {
		sub=obj;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Before();
		Object invoke=method.invoke(sub, args);
		After();
		return invoke;
	}

	private void Before() {
		System.out.println("Before前台。。。");//前增强
	}
	private void After() {
		System.out.println("After前台。。。");//后增强
	}

我们来看invoke()方法,源码的解释是:处理代理实例上的方法调用并返回结果。当方法在与之关联的代理实例上被调用时,该方法将在调用处理程序上被调用。
看似很复杂,其实就是用于处理代理实例的方法。打个比方,有客户来的时候前台所做的事情就是这个方法的内容,但是前台并不知道要让那个工程师做什么,但是她知道她需要找这位工程师去做,而这工程师是由客户提供的。可能解释的有些累赘,但思路就是这样了。这个原理也就是Spring AOP的原理。

(3)客户测试类,它提供了它需要的被代理对,并通过代理对象调用了被代理对象的方法

public class TestClient {
	@Test
	public void test() {
		RealSubject rs = new RealSubject();
		InvocationHandler ih= new ProxySubject(rs);//InvocationHandler是一个接口,ProxySubject类实现了该接口,
													//并传入了真实的被代理的对象
		Class <?>cls = rs.getClass();//
		//Proxy.newProxyInstance()方法的返回值是接口对象
		Subject subject = (Subject) Proxy.
				newProxyInstance(cls.getClassLoader(),
								cls.getInterfaces(),  
								ih);
		subject.request();
	}
}

我们重点来看代理类Proxy提供的newProxyInstance()方法,它有3个参数

  1. ClassLoader:类加载对象,定义了由那个类加载对象生成代理类进行加载,也就是代理对象代理的是哪一个类
  2. Class<?>[]:一个接口对象数组,表示最终要被代理的真实的类所实现的接口(数组)(如果我们提供了这样这样一组接口,也就是声明了代理类实现了这个接口,代理类也就可以调用接口中所有的方法)
  3. InvocationHandler:(用于分派方法调用的调用处理程序,表示动态代理最终要被代理的真实的类的对象,该类必须实现)简单说就是代理对象的调用处理程序类的对象。

这样,代理类对象就创建好了,注意,它返回的是一个接口类型的对象,再调用接口中的方法实现代理的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值