在有些情况下,一个客户不能或者不想直接访问另一个对象,这是需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要到火车站买,可以在12306或者火车票代售点买,还有请律师代理打官司。
代理模式的定义与特点
代理模式的定义:由于某些原因需要给谋对象提供一个代理以控制该对象的访问。这是,访问对象不合适或者不能直接用于目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。
- 代理对象可以扩展目标对象的功能
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合性。
主要缺点:
- 在客户端和目标对象之间增加代理对象,活造成请求处理速度变慢;
- 增加了系统的复杂性。
代理模式的结构与实现
代理模式的结构比较简单,主要是通过定义一个集成主题的代理来包含真实主题,从而实现对真实主题的访问,下面分析其基本结构个实现方法。
1.模式的结构
代理模式的主要角色是:
1) 抽象主题(Subject)类:通过接口或者抽象类声明真实主题和代理对象实现的业务方法。
2)真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3) 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,他可以访问、控制或扩展真实主题的功能。
结构图:
2.模式的实现
代理模式的实现代码如下:
package proxy;
public class ProxyTest
{
public static void main(String[] args)
{
Proxy proxy=new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject
{
void Request();
}
//真实主题
class RealSubject implements Subject
{
public void Request()
{
System.out.println("访问真实主题方法...");
}
}
//代理
class Proxy implements Subject
{
private RealSubject realSubject;
public void Request()
{
if (realSubject==null)
{
realSubject=new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest()
{
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest()
{
System.out.println("访问真实主题之后的后续处理。");
}
}
程序运行结果如下:
访问真实主题之前的预处理。
访问真实主题方法…
访问真实主题之后的后续处理。
代理模式分为两种实现方式,一种是静态代理,另一种是个大框架都喜欢的动态代理。
代理模式的UML图:
静态代理
Subject 接口的实现:
public interface Subject {
void visit();
}
实现Subject 接口的两个类:
public class RealSubject implements Subject {
private String name = "byhieg";
@Override
public void visit() {
System.out.println(name);
}
}
public class ProxySubject implements Subject{
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void visit() {
subject.visit();
}
}
具体调用如下:
public class Client {
public static void main(String[] args) {
ProxySubject subject = new ProxySubject(new RealSubject());
subject.visit();
}
}
同上面的代理代码,我们可以看出代理模式的特点,代理模式接受一个Subject 接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,若果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
动态代理
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类节后过多的问题。动态代理的实现方式,是通过反射来实现的,借助Java自带的Java.lang.reflect.Proxy
,通过固定的规则生成。
其步骤如下:
1)编写一个委托类的接口,即静态代理的(Subject接口)
2)实现一个真正的委托类,即静态代理的(RealSubject 类)
3)创建一个动态代理类,实现InvocationHandler
接口,并重写该 invoke
方法
4)在测试类中,生成动态代理的对象
第一、二步和静态代理一样,第三步如下:
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
第四步,创建动态代理的对象
Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new Class[]{Subject.class}, proxy);
subject.visit();
创建动态代理对象,需要借助Proxy.newProxyInstance.该方法的是三个参数是:
1)ClassLoader loader 表示当前使用的appClassloader
2) Class<?>[] interfaces 表示目标对象实现的一组接口
3)InvocationHandler h 表示当前的InvocationHandler 实现实例对象。
代理类和委托类有相同的接口,一个代理的对象以一个委托类的对象关联。dialing类的对象本身并不真正实现服务,二十通过调用委托类的对象的相关方法来提供特定服务。
JDK动态代理反射 :
1)proxy类: 类的静态方法用来生成动态代理的实例
2)innovationhandler 接口有一个incoke 方法,用来几种处理在动态代理类对象的方法调用,通常在改方法中实现对委托类的代理访问,每次生成动态代理对象时都要指定一个对应的调用处理器。
CGlib动态代理:
jdk代理机制只能代理实现了接口的类,而没有实现接口的类不能用jdk动态代理。CGlib是针对类来实现代理,他的原理是对指定的目标类生成一个子类,并且覆盖其中方法实现增强,因为采用的是继承,所以不能对final修饰的类进行代理,methodinterceptor