背景:
一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又比如找女朋友、找保姆、找工作等都可以通过找中介完成。
代理模式的定义:
代理模式的目的:
代理模式的作用:
代理模式的主要优点有:
其主要缺点是:
那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式
代理模式的主要角色如下。
其结构图如图所示。
在这里我们举一个租房的例子,用来理解代理模式
在这个例子里面,租房是抽象主题类,房产中介是代理类,房东是真实主题类,租房的人就是客户类,房东就是中介所代表的的真实对象。
根据代理的创建时期,代理模式分为静态代理和动态代理。
静态代理代码实现租房:
1.接口
//租房 public interface Rent { public void rent(); }
2.真实角色
//房东,被代理的真实角色 实现租房Rent这个接口 public class Host implements Rent{ @Override public void rent() { System.out.println("房东要把房子租出去"); } }
3.代理角色
package com.cheng.demo01; //中介代理 public class Proxy implements Rent{ //多用组合,少用继承 private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent() { host.rent(); seeHost(); charge(); } //看房,这件事中介能实现,房东不能实现 public void seeHost(){ System.out.println("中介带你看房子"); } //收中介费,这件事也只有中介能实现 public void charge(){ System.out.println("中介收你中介费"); } }
4.客户类
//要租房的人 public class Client { public static void main(String[] args) { Host host = new Host();//获得房东 //因为房东不能直接出租房子,所以我们要找中介(代理),通过代理访问房东 //代理一般会有一些附属操作,例如看房,收中介费 Proxy proxy = new Proxy(host); proxy.rent();//本质还是调用了房东的rent方法 } }
静态代理代码实现二:
1.接口,只有增删改查方法
//抽象角色 public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
2.真实角色,实现了增删改查方法
package com.cheng.demo02; import java.util.Random; //真实角色 public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } }
要求:如果在不改变原有业务代码的情况下,想要在执行接口中的方法的时候,打印出日志,这时候最好的方法就是用静态代理实现。
3.代理类
package com.cheng.demo02; //代理类 public class UserServiceProxy implements UserService{ private UserServiceImpl userService; //使用set注入对象 public void setUserService(UserServiceImpl userService){ this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //日志的方法 public void log(String msg){ System.out.println("使用了"+msg+"方法"); } }
4.客户类
//客户类 public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); } }
动态代理:在程序运行时,运用反射机制动态创建而成
动态代理分类:
基于接口的动态代理
基于类的动态代理
java动态代理机制中有两个重要的类和接口InvocationHandler
(接口)和Proxy
(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心
InvocationHandler
是由proxy代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke
方法。
invoke方法详细信息:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; }
处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
Proxy类提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数:
实现一:租房
接口
//租房 public interface Rent { public void rent(); }
真实角色
//房东,被代理的真实角色 实现租房Rent这个接口 public class Host implements Rent { public void rent() { System.out.println("房东要把房子租出去"); } }
由proxy代理实例的调用处理程序
package com.cheng.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们将会用这个类自动生成代理类,这个类(ProxyInvocationHandler)只是proxy代理实例的调用处理程序 public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理类 public Object getProxy(){ //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHose(); fare();//这两个方法会通过反射动态的加载到代理类中04 //动态代理的本质还是反射机制 Object result = method.invoke(rent,args); return result; } public void seeHose(){ System.out.println("中介带你看房子"); } public void fare(){ System.out.println("收中介费"); } }
客户类
public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理角色,暂时还没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过处理程序来实现接口对象 这里用了多态,相当于 Rent rent = new Host(); pih.setRent(host); //动态生成的代理类 Rent proxy = (Rent) pih.getProxy(); proxy.rent(); } }
测试
实现二:用动态代理优化4.1静态代理中的代码实现二
接口
//抽象角色 public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
由proxy代理实例的调用处理程序,我们可以把它封装成一个工具类
package com.cheng.demo04; import com.cheng.demo03.Rent; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们将会用这个类自动生成代理类,这只是一个处理程序 public class ProxyInvocationHandler implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } //生成代理类 public Object getProxy(){ //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象 return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质还是反射机制 log(method.getName());//用反射动态的获取方法名 Object result = method.invoke(target,args); return result; } //增加日志功能 public void log(String msg){ System.out.println("[Debug]:使用了"+msg+"方法"); } }
客户类
public class Client { public static void main(String[] args) { //获得真实角色 UserServiceImpl userService = new UserServiceImpl(); //获得代理角色,暂时没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //处理程序处理接口对象 pih.setTarget(userService); UserService proxy = (UserService) pih.getProxy(); proxy.add(); } }
相比静态代理,动态代理的优点:
例如:在上面的例子中,只要实现了UserService这个接口的类,都可以被动态代理类代理。
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
前面分析了代理模式的结构与特点,现在来分析以下的应用场景。