一、什么是代理模式
Proxy模式又叫做代理模式,是是结构型设计模式之一,它可以为其它对象提供一种代理(Proxy)以控制对这个对象的访问。所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互过程中(交互前后),进行某种特别的处理。
简单的说,代理模式就像一种中介。举个例子,本来自己买车,则需要自己找车源,还有一系列检查过程,特别麻烦。但是如果你找中介公司买车,他们会做好这一系列过程,此时你只需要挑选自己喜欢的车,不用考虑车的质量。
二、代理模式的分类
按照创建时期可以分为静态代理和动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。
三、代理模式的角色和职责
subject(抽象主题角色):真实主题角色与代理主题角色的共同接口
RealSubject(真实主题角色):定义了代理角色所代表的真实对象
Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象
四、代理模式适用场景
想让一个类具有日记功能,但是不想在这个类上加;想让一个类具有事务功能,但是不想在类上加,这就可以使用动态代理模式对类进行增强;
附加信息:
mybatis优雅使用日记的时候,使用了动态代理对PreparedStatement等进行了这样那样的增强;
ConnectionLogger:Connection,负责打印连接信息和SQL语句,并创建PreparedStatementLogger
PreparedStatementLogger:增强了PreparedStatement,增加了负责打印参数信息,并创建ResultSetLogger
ResultSetLogger:增强了ResultSet,增加了负责打印数据结果信息
五、代理模式编码
1、静态代理
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
接口:Subject.java
package com.xue.proxy;
/**
* 卖书接口
* @author xuexue
*
*/
public interface Subject {
/**
* 卖书
*/
void sail();
}
被代理的类 RealSubject.java
package com.xue.proxy;
/**
* 需要被代理的实现类
* @author xuexue
*
*/
public class RealSubject implements Subject {
@Override
public void sail() {
System.out.println("卖书");
}
}
代理类 ProxySubject.java
package com.xue.proxy;
/**
* 代理类
* @author xuexue
*
*/
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
/*
* 代理RealSubject类
* 增加功能,检查和售后
*/
@Override
public void sail() {
System.out.println("卖书之前检查书包完整性");
subject.sail();
System.out.println("卖书之后确保售后服务");
}
}
测试类 Test.java
package com.xue.proxy;
public class Test {
public static void main(String[] args) {
//创建需要被代理的对象
Subject realSubject = new RealSubject();
//卖书
realSubject.sail();
System.out.println("--------------------------------------");
//创建代理对象,传入被代理对象realSubject
ProxySubject proxySubject = new ProxySubject(realSubject);
//代理之后卖书
proxySubject.sail();
}
}
输出
2、动态代理
实现InvocationHandler接口
重写invoke方法
Proxy.newProxyInstance()
对于被代理的对象必须要实现接口
优点:创建一个代理类(MyInvocation)的对象,只需传入被代理对象即可。无需反复创建代理类对象。
注意Proxy.newProxyInstance()方法接受三个参数:
ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
接口:Subject.java
package com.xue.proxy;
/**
* 卖书接口
* @author xuexue
*
*/
public interface Subject {
/**
* 卖书
*/
void sail();
}
被代理类 RealSubject.java
package com.xue.proxy;
/**
* 需要被代理的实现类
* @author xuexue
*
*/
public class RealSubject implements Subject {
@Override
public void sail() {
System.out.println("卖书");
}
}
代理类 MyInvocation
package com.xue.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类
* @author xuexue
*
*/
public class MyInvocationimplements InvocationHandler {
//需要代理的对象
private Object object;
public Object newProxyInstance(Object object) {
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces() , this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
System.out.println("卖书之前检查书包完整性");
result = method.invoke(object, args);
System.out.println("卖书之后确保售后服务");
return result;
}
}
测试类 Test.java(必须是接口来接收Subject)
package com.xue.proxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建需要被代理的对象
RealSubject realSubject = new RealSubject();
//创建代理对象
MyInvocation proxyHandler = new MyInvocation();
//通过反射得到对被代理对象的代理类
Subject proxyInstance = (Subject) proxyHandler.newProxyInstance(realSubject);
proxyInstance.sail();
}
}
输出