动态代理其本质是设计模式中的代理模式,主要由抽象角色、代理角色和真实角色组成:
其中,Subject角色负责定义RealSubject和Proxy角色应该实现的接口;RealSubject角色用来真正完成业务服务功能;Proxy角色负责将自身的request请求,调用RealSubject对应的request功能来实现业务功能,自己不真正做业务。
以下是Java静态代理的实现代码。
//抽象角色
interface Subject{
public void doSomething();
}
//真实角色
class RealSubject implements Subject{
public void doSomething() {
System.out.println( "call doSomething()" );
}
}
//代理角色
class SubjectProxy implements Subject{
//代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
Subject subimpl = new RealSubject();
public void doSomething(){
System.out.println("before"); //调用目标对象之前可以做相关操作
subimpl.doSomething();
System.out.println("after");//调用目标对象之后可以做相关操作
}
}
public class Test{
public static void main(String[] args) throws Exception {
Subject sub = new SubjectProxy();
sub.doSomething();
}
}
可以看到,SubjectProxy实现了Subject接口(和RealSubject实现相同接口),并持有的是Subject接口类型的引用。这样调用的依然是doSomething方法,只是实例化对象的过程改变了,结果来看,代理类SubjectProxy可以自动为我们加上了before和after等我们需要的动作。
但是静态代理有一个缺点,如果将来需要实现一个新的接口,就需要在代理类里再写该接口的实现方法,对导致代理类的代码变得臃肿;另一方面,当需要改变抽象角色接口时,无疑真实角色和代理角色也需要改变。而动态代理可以解决上述问题。
interface Subject {
public void doSomething();
}
class RealSubject implements Subject {
public void doSomething() {
System.out.println( "call doSomething()" );
}
}
class ProxyHandler implements InvocationHandler{
private Object tar;
//绑定委托对象,并返回代理类
public Object bind(Object tar) {
this.tar = tar;
//绑定该类实现的所有接口,取得代理类
return Proxy.newProxyInstance(tar.getClass().getClassLoader(),
tar.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy , Method method , Object[] args)throws Throwable//不依赖具体接口实现
{
Object result = null;//被代理的类型为Object基类
//这里就可以进行所谓的AOP编程了
//在调用具体函数方法前,执行功能处理
result = method.invoke(tar,args);
//在调用具体函数方法后,执行功能处理
return result;
}
}
public class Test{
public static void main(String args[]) {
ProxyHandler proxy = new ProxyHandler();
//绑定该类实现的所有接口
Subject sub = (Subject) proxy.bind(new RealSubject());
sub.doSomething();
}
}
在调用过程中使用了通用的代理类包装了RealSubject实例,然后调用了Jdk的代理工厂方法实例化了一个具体的代理类。最后调用代理的doSomething方法,还有附加的before、after方法可以被任意复用(只要我们在调用代码处使用这个通用代理类去包装任意想要需要包装的被代理类即可)。当接口改变的时候,虽然被代理类需要改变,但是我们的代理类却不用改变了