动态代理模式到底是干啥的?
答:当我们想去修改一个类的实现,而又不能去直接修改、干预该类的代码时,我们就需要一个该类的代理类来辅助我们。
比如,我们想在每次调用某个类的save方法时,都在日志文件里写下日志信息,这时候,在不影响原来的代码的封装的前提下,我们可以手动的去创建一个该类的代理类,来代替该类帮我们实现一些附加的方法。直接看代码:
定义接口PersonDAO:
package top.xiaohang456.spring.staticProxy;
public interface PersonDAO {
void save();
}
定义接口的实现类PersonDAOImpl
package top.xiaohang456.spring.staticProxy;
public class PersonDAOImpl implements PersonDAO {
@Override
public void save() {
System.out.println("Person save success!");
}
}
OK,现在我们假设上边的类已经封装好了,而我们也不能再去直接修改它的代码了,但我们想在每次调用PersonImpl的save方法时都加上一个事务,让每次被调用之前都在日志里写下“Transaction Begin”,在每次调用过后也在日志里写一个东西,怎么办呢?我们可以定义如下一个Transaction类,专门来存储我们下要让其附加的事务:
package top.xiaohang456.spring.staticProxy;
public class Transaction {
public void beginTransaction(){
System.out.println("Transaction Begin!");
}
public void commit(){
System.out.println("Transaction commit!");
}
}
然后我们怎么办呢?这时,我们就要用到java反射机制了,首先我们要了解的一个接口就是InvocationHandler接口,这是我们定义拦截器(或者说事务调用处理器,但这可不是一个代理类,稍后我们会说这其中的区别)所必须实现的一个接口,这个接口,只定义了一个方法--invoke方法,稍后我们会说明这个方法的具体作用。先看代码:
package top.xiaohang456.spring.JDKProxy;
import top.xiaohang456.spring.staticProxy.Transaction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Interceptor implements InvocationHandler {
private Object target;
private Transaction transaction;
public Interceptor(Object target, Transaction transaction){
this.target = target;
this.transaction = transaction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
this.transaction.beginTransaction();
method.invoke(target);
this.transaction.commit();
return null;
}
}
想必看完之后你一定有很多疑惑,下边就听小菜为您一一解答。
首先,不难看出我们在这个类里定义了两个私有变量,而且我们还定义了它的含参的构造方法来初始化这两个成员变量,那么Transaction被实例化出来我们还可以理解,因为我们要调用其中的方法来帮我们完成一些辅助事务,可这个Object又为何产生呢?这个名为target的Object,就是我们的代理对象,那你可能会问:为啥我不直接定义一个PersonDAO呢?而去定义一个Object呢?这,就体现了动态代理的好处之一,不光光是PersonDAO可以使用我们所定义的拦截器,其他的类也可以用!因为Object是所有类的祖宗类吖!这个暂时不理解也没关系,我接着说您就明白了。
好,我们现在直接测试这个拦截器,先不管这个invoke方法(TrustMe!)
package top.xiaohang456.spring.JDKProxy;
import org.junit.Test;
import top.xiaohang456.spring.staticProxy.PersonDAO;
import top.xiaohang456.spring.staticProxy.PersonDAOImpl;
import top.xiaohang456.spring.staticProxy.Transaction;
import java.lang.reflect.Proxy;
public class TestJDKProxy {
@Test
public void test(){
Object target = new PersonDAOImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
PersonDAO personDAO1 = (PersonDAO) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),interceptor);
personDAO1.save();
}
}
如上就是我们所定义的测试类,我们先实例化出了我们的被代理类,和我们的事务类,在用着两个类作为参数去实例化我们的Interceptor,然后。。。
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),interceptor);
这个又是什么鬼?
这个就是我们的重头戏和切入点!
Proxy是java.lang.reflect包下的一个类,Proxy本身就是代理的意思,那它的这个newProxyInstance()方法顾名思义就是根据传入的参数来实例化出一个代理类出来,API中这个方法的定义是这样的:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数:
loader
- 定义代理类的类加载器
interfaces
- 代理类要实现的接口列表
h
- 指派方法调用的调用处理程序
上述的三个参数就是我们想要生成一个代理类所需要的三个要素,第一个为ClassLoader(类加载器),第二个为被代理类所实现的接口(我想生成你的代理类,你当然要把你所实现的接口都给我一份啦!我才去实现这些接口,并且知道你都定义了哪些方法),第三个,就是我们所定义的那个Interceptor,拦截器!
有了这三个参数,我们就能生成一个名为personDAO1的代理类了!Then,我们用这个代理类去调用save方法的时候又发生了什么呢?答案是:我们的这个代理类,会把它自己(一个Object对象)、这个被调用的方法名(此处为save)、以及所传入该方法的参数(此处没定义...save方法没定义参数),统统都,传给Interceptor的invoke方法!这就是invoke方法那三个参数的由来!现在再看回去,一切都豁然开朗了~~~
在此代理模式中,不仅代理是动态产生的(即在运行的时候生成),而且还在代理的时候,也增加了一些处理。在此处增加的处理,其实就是另一种编程思想-----面向切面编程思想(AOP Aspect Oriented Programming)。