Java代理模式
一、代理的概念
代理(Proxy):即代替处理,类似于中间人这一角色。
传统的模式是调用方直接去调用被调用方来达到目的,这样导致的缺点是:耦合度增加。当在两者之间增加了一个代理之后,两者的耦合度会大大降低,每当调用方需要调用某个方法的时候,都回去告知代理并由代理来进行方法的调用。
二、Java代理之——静态代理
使用静态代理的时候,代理对象与目标对象要实现相同的接口,以此来保证两者具有相同的方法。代理对象持有目标对象的句柄。
代码示例:
接口:
public interface Subject {
void request();
}
目标对象:
//目标对象
public class SubjectImpl implements Subject {
public void request() {
System.out.println("----处理请求----");
}
}
代理对象:
//代理对象
public class SubjectProxy implements Subject{
//实际的目标对象
private Subject subject;
public SubjectProxy(SubjectImpl subject){
this.subject=subject;
}
public void request() {
//预处理
System.out.println("PreProcess");
subject.request();//执行目标对象的方法
//后置处理
System.out.println("PostProcess");
}
}
测试类:
public class StaticProxyDemo {
public static void main(String[] args) {
//创建实际的目标对象
Subject subject = new SubjectImpl();
//代理对象,把目标对象封装到代理对象中,建立代理关系
SubjectProxy proxy = new SubjectProxy(subject);
proxy.request();//执行的是代理的方法
}
}
总结:
优点:结构简单,容易理解。能够在不修改目标对象的基础上,实现功能扩展。
缺点:当目标对象每次添加一个新的方法之后,都需要在代理对象内创建同样的方法,并对其进行包装,当方法多了的时候,代码改动相对来说会比较大且繁琐。
三、Java代理之——动态代理
动态代理:对目标对象的方法每次被调用,进行动态拦截。
在调用代理对象的方法时,实际上是调用目标对象的方法,每次调用代理对象的方法时,都会被拦截然后送到代理处理器里面去,代理处理器都是事先InvocationHandler这个接口的
流程:
代理处理器:
- 持有目标对象的句柄
- 实现InvocationHandler接口
- 实现Invoke方法
- 所有的代理对象方法调用,都会被转发到Invoke方法来
- Invoke的形参method,就是指代理对象方法的调用
- 在Invoke内部,可以根据method(根据方法、形参这些区别),使用目标对象不同的方法来响应请求
代码案例:
接口:
public interface Subject {
void request();
}
目标对象:
public class SubjectImpl implements Subject{
@Override
public void request() {
System.out.println("目标对象处理请求!");
}
}
代理处理器对象:
public class ProxyHandler implements InvocationHandler {
//实际的目标对象
private Subject subject;//持有真实的目标对象的句柄
public ProxyHandler(Subject subject) {
this.subject = subject;
}
//当代理对象调用任何一个方法的时候,此方法都会被调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//输出代理类的名字
System.out.println(proxy.getClass().getName()); //com.sun.proxy.$Proxy0
System.out.println("预处理");
Object result = method.invoke(subject,args);
System.out.println("后置处理");
//返回结果
return result;
}
}
测试类:
public class StaticProxyDemo {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
SubjectProxy proxy = new SubjectProxy(subject);
proxy.request();
}
}
总结:
Java动态代理的核心:
- 代理对象:不需要自己产生,由Java的Proxy这个类自动的创建出来,只要给定具体的接口,就会自己产生出这个接口的子对象。
- 代理处理器对象:需要自己定义并且实现Invoke方法
代理对象:
-
根据给定的接口,有Proxy自生成的对象
-
类型com.sun.proxy.$Proxy0,继承自java.lang.reflect.Proxy
-
通常和目标对象实现相同的接口(可另实现其他接口)
-
实现多个接口
-
接口排序非常重要
-
当调用多个接口里面的同名方法,则会默认调用第一个接口的方法
-
代码案例:
接口类:
public interface Cook {
void doWork();
}
public interface Driver {
void doWork();
}
目标对象:
public class CookImpl implements Cook{
@Override
public void doWork() {
System.out.println("执行Cook_doWork");
}
}
代理处理器对象:
public class ProxyHandler implements InvocationHandler {
private Cook cook;
public ProxyHandler(Cook cook) {
this.cook = cook;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象类型:"+proxy.getClass().getName());
System.out.println("调用方法"+method+"参数:"+ Arrays.deepToString(args));
Object result = method.invoke(cook,args);
return result;
}
}
测试类:
public class MultipleInterfaceDemo {
public static void main(String[] args) throws Exception {
Cook cook = new CookImpl();
ClassLoader loader = cook.getClass().getClassLoader();
ProxyHandler handler = new ProxyHandler(cook);
/*
//生成代理类型
Class<?> proxyClass = Proxy.getProxyClass(loader,
new Class[]{Cook.class, Driver.class});//给定两个接口
Object proxy = proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
*/
Object proxy = Proxy.newProxyInstance(loader,
new Class[]{Cook.class, Driver.class},
handler);
Cook c = (Cook) proxy;
c.doWork();
Driver d = (Driver) proxy;
d.doWork();
}
}
执行结果:
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
执行结果:
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
总结:通过结果可以看到,虽然代理对象转成了Driver并调用Driver的doWork方法,但是因为Cook和Driver两个接口都有同名方法,根据默认的规则,调用的是第一个接口的这个方法。因此,最终调用的是Cook的doWork方法。