代理模式的定义:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问
代理模式通常分为静态代理模式和动态代理模式。
简单来理解代理模式,其实就是客户端不直接对一个对象进行操作,而是将这些操作放在一个代理类中,由代理类完成操作,客户端只负责与代理类的交互即可。这样做一是可以提高代码的安全性、而是能够降低耦合度。
代理模式通常由三个角色组成
- Subject:抽象主题角色:通常作为真实主题角色的抽象接口,视业务情况有所不同。
- Proxy:代理主题角色:代理真实主题角色,负责和客户端进行交互。功能的扩展主要也是通过代理主题角色来关联并实现。
- RealSubject:真实主题角色:具体的功能主题角色。
由于静态代理模式比较简单,在文末补充上,这里先讲下动态代理模式。
需求:
假设有一个文件操作系统,提供对文件的增删改查操作,现在要在不修改源代码的前提下(开闭原则),使用代理模式,实现扩展一个功能:将每次对文件的操作和操作的时间写入日志文件log.txt中
文件系统的各种操作无需具体实现,只是模拟即可。
动态代理模式的实现如下:
①文件的操作对象分为抽象接口和具体实现。分别在动态代理模式中充当抽象主题角色和具体主题角色
抽象主题角色:AbsFileOption
public interface AbsFileOption {
public void find(File file);//查找文件
public void update(File file,String updateContent);//修改文件
public void delete(File file);//删除文件
public void add(File file);//添加文件
}
具体主题角色:FileOption,简单的模拟实现了增删查改
public class FileOption implements AbsFileOption {
@Override
public void find(File file) {
if(file.getName().equals("文件a") || file.getName() == "文件a")
System.out.println("找到文件:"+file.getName());
else
System.out.println("未找到该文件");
}
@Override
public void update(File file, String updateContent) {
if(file.getName().equals("文件a") || file.getName() == "文件a")
System.out.println("修改文件:"+file.getName()+",修改内容:"+updateContent);
else
System.out.println("修改失败");
}
@Override
public void delete(File file) {
if(file.getName().equals("文件a") || file.getName() == "文件a")
System.out.println("删除文件:"+file.getName());
else
System.out.println("未找到该文件");
}
@Override
public void add(File file) {
if(file.getName().equals("文件a") || file.getName() == "文件a")
System.out.println("添加文件:"+file.getName());
else
System.out.println("未找到该文件");
}
}
②实现需求,创建一个写入日志的需求功能类,作为扩展对象。再创建一个代理主题角色,来实现需求中的功能扩展。
扩展对象类:WriteLog
public class WriteLog {
//当前时间
private String nowTime;
//日志文件路径
//System.getProperty("user.dir")获取的是项目的绝对路径
private String path = System.getProperty("user.dir")+"\\src\\main\\java\\demo\\log.txt";
//IO缓冲输出流
private BufferedOutputStream bos;
//将日志写入log.txt
public void writeLog(String writeContent){
nowTime = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss").format(new Date());
try{
File file = new File(path);
if(!file.exists()){
file.createNewFile();
}
bos = new BufferedOutputStream(new FileOutputStream(file,true));
bos.write((nowTime+"\t"+writeContent+"\n-----------------------------\n").getBytes());
bos.flush();
bos.close();
}catch (Exception e){
e.printStackTrace();
}
}
代理主题角色:FileLogProxy,需要实现InvocationHandler 接口(划重点)
public class FileLogProxy implements InvocationHandler {
//要代理的具体主题对象
private Object target;
//要扩展的具体主题对象
private WriteLog writeLog = new WriteLog();;
public FileLogProxy(Object object){
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeRequest(method.getName());//获取当前执行方法的方法名
Object result = method.invoke(target,args);//转发调用
afterRequest();
return result;
}
//调用动态代理前的事件,将调用的方法名写入日志中
public void beforeRequest(String methodName){
System.out.println("写入日志中...");
writeLog.writeLog("动态代理:调用方法"+methodName);
}
//调用动态代理后的事件
public void afterRequest(){
System.out.println("写入完毕");
}
}
③创建一个客户端类来测试代理模式
客户端类:Client(划重点)
public class Client {
public static void main(String[] args) {
InvocationHandler IHandler;
AbsFileOption absFO = new FileOption();//获取文件操作的真实主题对象
IHandler = new FileLogProxy(absFO);//将文件操作的真实主题对象传递给动态代理的构造方法
ClassLoader classLoader = AbsFileOption.class.getClassLoader();//获取抽象主题的类加载器对象
Class[] classes = new Class[]{AbsFileOption.class};//要代理的所有类对象集合
//创建动态代理对象,用于代理AbsFileOption类型的真实主题对象
AbsFileOption proxy = (AbsFileOption) Proxy.newProxyInstance(classLoader,classes,IHandler);
//调用动态代理对象的业务方法
proxy.find(new File("文件a"));
proxy.update(new File("文件a"),"测试修改");
proxy.delete(new File("文件a"));
proxy.add(new File("文件a"));
}
}
打印台输出如下图
然后可以打开log.txt日志文件查看写入的内容
到这里,就成功通过动态代理实现对一个类的扩展。其实理解了代理模式的思想,在再理解动态代理并不难,关键在于要理解Proxy类和InvocationHandler接口的使用方法。
静态代理模式的实现如下:
如果上面的动态代理能够搞懂,相信静态代理只会觉得更加简单。只需要修改下上面的代理主题角色类FileLogProxy和客户端测试类Client即可
FileLogProxy类如下:
public class FileLogProxy {
//维持对文件操作真实主题的引用
private AbsFileOption afo = new FileOption();
//扩展功能的真实主题
private WriteLog writeLog = new WriteLog();
public void find(File file){
afo.find(file);//不做修改,仍然使用原有的
writeLog.writeLog("静态代理:调用方法find");//写入日志文件中
}
public void update(File file,String updateContent){
afo.update(file,updateContent);
writeLog.writeLog("静态代理:调用方法update");
}
public void delete(File file){
afo.delete(file);
writeLog.writeLog("静态代理:调用方法delete");
}
public void add(File file){
afo.add(file);
writeLog.writeLog("静态代理:调用方法add");
}
}
Client类如下:
public class Client {
public static void main(String[] args) {
FileLogProxy fileLogProxy = new FileLogProxy();
fileLogProxy.find(new File("静态文件a"));
fileLogProxy.update(new File("静态文件a"),"测试修改");
fileLogProxy.delete(new File("静态文件a"));
fileLogProxy.add(new File("静态文件a"));
}
}