代理模式
概述
给某一对象提供代理对象,并由代理对象控制具体对象的引用。
就是持有被代理对象的引用,提供被代理对象的接口(public的方法),这样就可以在被代理对象被调用前后,切面式的插入自定义逻辑。
具体来说,就相当于,第三者,本来正常方法调用,就是调用者和被调用者,如果被调用的对象不想被直接调用,而希望在调用他之前先执行其他一些逻辑,那就需要引入第三者,也就是代理对象。这个时候,就不再直接调用原对象,而是先调用代理对象,这个代理对象持有一个原对象的引用,这时候,在代理对象方法中,就可以在调用原对象之前,插入一些其他逻辑。
其实顾名思义,就是跟明星经纪人一个道理,你本来想跟明星打电话,但是人家是明星,电话号码怎么能随便让你知道,所以就得跟他经纪人打,这个经纪人也就是代理,明星就是被代理对象,明星的电话,经纪人是知道的,这就相当于代理对象里面有一个字段是被代理对象一样。
那么,为什么要用代理模式呢?
-
中介隔离作用
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
-
开闭原则,增加功能:
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。
真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。
例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
有哪几种代理模式?
代理模式可以分为两种:静态代理、动态代理。
-
静态代理是直接实现接口的实现类
-
动态代理是通过类工厂动态创建实现类,再通过Java反射机制调用方法
代码示例
1 .静态代理
第一步:创建服务类接口
public interface CallPhone {
void phone();
}
第二步:实现服务接口
public class CallPhoneImpl implements CallPhone {
@Override
public void phone() {
System.out.println("我要打电话给飞飞");
}
}
第三步:创建代理类
public class CallPhoneProxy implements CallPhone{
private CallPhone callPhone;
public CallPhoneProxy(final CallPhone callPhone) {
this.callPhone = callPhone;
}
@Override
public void phone() {
System.out.println("打电话前先想好台词");
callPhone.phone();
System.out.println("哇塞打完电话很激动~");
}
}
第四步:编写测试类
public class ProxyTest {
public static void main(String[] args) {
CallPhone callPhone = new CallPhoneImpl();
callPhone.phone();
CallPhoneProxy callPhoneProxy = new CallPhoneProxy(callPhone);
callPhoneProxy.phone();
}
}
总结:
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:得要为每一个服务都创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
2.动态代理
在动态代理中,我们不再需要手动创建代理类,只需要编写一个动态处理器就可以了,真正的代理对象由JDK在运行时为我们动态创建。
第一步:编写动态处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打电话前先想好台词");
Object result = method.invoke(object, args);
System.out.println("哇塞打完电话很激动~");
return result;
}
}
第二步:编写测试类
import com.yuyao.代理模式.静态代理.CallPhone;
import com.yuyao.代理模式.静态代理.CallPhoneImpl;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
CallPhone callPhone = new CallPhoneImpl();
CallPhone proxyCallPhone = (CallPhone)Proxy.newProxyInstance(CallPhone.class.getClassLoader(), new Class[]{CallPhone.class}, new DynamicProxyHandler(callPhone));
proxyCallPhone.phone();
}
}
注意:
Proxy.newProxyInstance() 方法只接收三个参数:
-
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
-
Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
-
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
总结:
相对于静态代理,动态代理大大减少了开发任务,同时减少了对业务接口的依赖,降低了耦合度。