动态代理分为两种,JDK动态代理和cglib动态代理。上一篇我们讨论了静态代理,最后留了2个问题:
1.当被代理的对象不存在或未知时,代理对象如何创建呢?
2.在实际问题中可能存在很多的类需要使用代理对象,而一个静态代理对象只能代理一个类,这样就会反复的创建相似的代理类,造成类的数量成倍增长,这个问题又该怎么解决呢?
JDK动态代理就是为了解决这两个问题而来的,同时它还有很多其他特点,带着问题我们细细说。
一、特点
和静态代理相同的是,JDK动态代理同样具有以下4个类:
1.接口类:定义被代理对象所具有的功能方法
2.被代理类:需要继承上面的接口并做具体实现
3.代理类(代理类的调用处理程序):用于代理被代理类的功能,它也需要继承上面的接口。与静态代理不同,在JDK动态代理中,这个类并不是正真意义上的代理类,它只是代理类的调用处理程序,而真实的代理类在客户类中。它需要做3件事:
- 实现InvocationHandler接口并实现该接口中的invocat()方法(难点)
- 它需要创建一个未初始化的真实的被代理对象和有参构造,用于传入真实的被代理对象,(注意,代理类的调用处理程序并不直接拥有真实的代理对象,而是通过有参构造的方式传递真实的被代理对象)
- 在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个参数
- ClassLoader:类加载对象,定义了由那个类加载对象生成代理类进行加载,也就是代理对象代理的是哪一个类
- Class<?>[]:一个接口对象数组,表示最终要被代理的真实的类所实现的接口(数组)(如果我们提供了这样这样一组接口,也就是声明了代理类实现了这个接口,代理类也就可以调用接口中所有的方法)
- InvocationHandler:(用于分派方法调用的调用处理程序,表示动态代理最终要被代理的真实的类的对象,该类必须实现)简单说就是代理对象的调用处理程序类的对象。
这样,代理类对象就创建好了,注意,它返回的是一个接口类型的对象,再调用接口中的方法实现代理的目的。