代理模式
为什么要学习代理模式?因为这就是Spring AOP的底层机制!【Spring AOP 和 Spring MVC】
1、什么是代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
通俗的来讲代理模式就是我们生活中常见的中介。
代理模式的分类:
- 静态代理
- 动态代理
在学习AOP之前 , 我们要先了解代理模式!
真实案例:租房
2、静态代理
静态代理角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实的角色,代理真实的角色后,我们一般会做一些附属操作
- 客户:访问代理角色的人
静态代理具体实现步骤:
抽象的接口
// 租房的接口 public interface Rent { // 房屋出租的方法 void rent(); }
真实对象
// 房东是真实的对象 public class Landlord implements Rent { @Override public void rent() { // 房东需要出租房子 System.out.println("房东要出租房子"); } }
代理对象
// 房屋中介就是代理对象 public class Proxy implements Rent { // 中介可以找到房东 private Landlord landlord; public Proxy() { } public Proxy(Landlord landlord) { this.landlord = landlord; } // 代理对象提供出租房屋的方法 @Override public void rent() { // 在出租房屋时,添加一些自己的方法 this.seeHouse(); // 代理调用房东的出租房屋方法 landlord.rent(); this.fare(); } //看房 private void seeHouse(){ System.out.println("带房客看房"); } //收中介费 private void fare(){ System.out.println("收中介费"); } }
客户端
public class Client { public static void main(String[] args) { // 房东要出租房屋 Landlord landlord = new Landlord(); // 房屋中介帮房东出租房屋 Proxy proxy = new Proxy(landlord); // 客户找到房屋中介租房 proxy.rent(); // 整个过程,客户不用接触房东,而是房屋中介找到房东 } }
分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式。程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
静态代理模式的优点:
- 可以使得我们的真实角色更加纯粹!不再去关注一些公共的事情。
- 公共的业务由代理来完成,实现了业务的分工。
- 公共业务发生扩展时变得更加集中和方便!
缺点:
- 一个真实角色,就需要一个代理角色;代码量会翻倍
- 类多了,多了代理类,工作量变大了,开发效率降低!
3、静态代理再理解
使用代理,实现我们平时写的增删改查。
编写的一个抽象角色UserService
// 抽象角色,增删改查 public interface UserService { void insert(); void delete(); void update(); void query(); }
编写一个真实的对象UserServiceImpl实现UserService
// 真实的对象,实现了增删改查方法 public class UserServiceImpl implements UserService { @Override public void insert() { System.out.println("新增一个用户"); } @Override public void delete() { System.out.println("删除一个用户"); } @Override public void update() { System.out.println("更新一个用户"); } @Override public void query() { System.out.println("查询一个用户"); } }
添加一个需求,为每个方法添加一个日志功能,如何实现?
- 思路1:为每个方法手动添加一个日志方法【太麻烦,也不符合面向对象的设计原则】
- 思路2:添加一个代理类,在原方法不改变的情况下,帮助我们添加日志!
编写一个UserServiceProxy代理类
// 代理对象,在这里面增加日志的实现 public class UserServiceProxy implements UserService { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } @Override public void insert() { userService.insert(); this.log("新增"); } @Override public void delete() { userService.delete(); this.log("删除"); } @Override public void update() { userService.update(); this.log("更新"); } @Override public void query() { userService.query(); this.log("查询"); } // 日志功能的额外方法 private void log(String msg) { System.out.println("[Debug] 这是" + msg + "的日志"); } }
测试结果
public class Client { public static void main(String[] args) { // 真实的对象 UserService service = new UserServiceImpl(); // 代理对象 UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(service); // 通过代理对象实现了日志功能 proxy.insert(); proxy.delete(); proxy.update(); proxy.query(); } }
通过上面的例子,我们已经基本了解了静态代理模式,就是:
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
AOP:纵向开发,横向开发
4、动态代理
了解完静态代理后,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类
所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理。
动态代理的概念
- 动态代理的角色和静态代理的一样
- 动态代理的代理类是动态生成的,而静态代理的代理类是我们提前写好的。
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理--cglib
- 现在用的比较多的是java字节码
javasist
来生成动态代理,可以百度一下javasist - 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
JDK的动态代理需要了解两个类
核心:InvocationHandler
和Proxy
, 可以打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
// proxy - 调用该方法的代理实例
// method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
// args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
【Proxy : 代理】
// 生成代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
具体代码实现:房屋出租
抽象的接口
public interface Rent { void rent(); }
真实的角色
public class Host implements Rent { @Override public void rent() { System.out.println("出租房屋"); } }
代理的角色
public class ProxyInvocationHandler implements InvocationHandler { // 被代理的接口 private Object target; public void setRent(Object target) { this.target = target; } // proxy: 代理类。 // method: 代理类的调用处理程序的方法对象。 // 处理代理实例上的方法调用并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 核心:本质利用反射实现! Object invoke = method.invoke(target, args); return invoke; } // 生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
客户
public class Client { public static void main(String[] args) { // 真实的角色 Rent rent = new Host(); // 代理实例的调用处理程序 ProxyInvocationHandler handler = new ProxyInvocationHandler(); // 将真实角色放置进去! handler.setRent(rent); // 动态生成对应的代理类! Rent proxy = (Rent) handler.getProxy(); proxy.rent(); } }
5、动态代理再理解
使用动态代理的方式实现增删改查
因为我们自己编写的ProxyInvocationHandler.java
是一个通用的代理类
我们使用它实现代理之前的UserService就非常简单!
抽象的对象
// 抽象角色,增删改查 public interface UserService { void insert(); void delete(); void update(); void query(); }
真实的对象
public class UserServiceImpl implements UserService { @Override public void insert() { System.out.println("新增一个用户"); } @Override public void delete() { System.out.println("删除一个用户"); } @Override public void update() { System.out.println("更新一个用户"); } @Override public void query() { System.out.println("查询一个用户"); } }
代理的对象
public class UserServiceProxy implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object invoke = method.invoke(target, args); // 执行后设置 log this.log(method.getName()); return invoke; } public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } // 添加一个log方法 private void log(String msg) { System.out.println("[Debug] 这是" + msg + "的日志"); } }
客户
public class Client { public static void main(String[] args) { // 真实的对象 UserService service = new UserServiceImpl(); // 代理对象 UserServiceProxy userServiceProxy = new UserServiceProxy(); userServiceProxy.setTarget(service); UserService proxy = (UserService) userServiceProxy.getProxy(); // 通过代理对象实现了日志功能 proxy.insert(); proxy.delete(); proxy.update(); proxy.query(); } }
我们可以发现,代理的对象类基本不用改变,只是添加了我们自己的log方法,就可以代理房屋出租,也可以代理UserService。这样我们只需要一个代理对象,就可以代理多个对象
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
- 公共的业务由代理来完成,实现了业务的分工。
- 公共业务发生扩展时变得更加集中和方便。
- 一个动态代理,一般代理某一类业务。
- 一个动态代理可以代理多个类,代理的是接口!