代理模式
一、代理模式简介
- 代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.
- 这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能,符合了设计模式的开闭原则,即在对既有代码不改动的情况下进行功能的扩展。
二、代理方式
问题描述
- 存在房东,只会卖房一种行为
- 存在房客,想租房
- 创建中介类,使得中介可以代理房东进行卖房的行为,并负责前后的手续办理,收费过程
2.1 静态代理模式
2.1.1 角色分析
- 抽象角色:一般会使用接口或抽象类来就解决
- 真实角色:被代理的角色,一般只负责核心的行为
- 代理角色:去代理真实角色,代理真实角色的核心行为,并添加一些附属操作来增强其功能
- 客户:访问代理对象的人
2.1.2 测试代码
/**
* @author Ni187
* 租房接口
*/
public interface Rent {
/**
* 租房
*/
void rent();
}
/**
* @author Ni187
* 房东类,实现租房接口,进行租房
*/
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
/**
* @author Ni187
* 中介,房东代理
*/
public class Proxy implements Rent{
private Host host;
public Proxy(Host host){
this.host = host;
}
@Override
public void rent() {
this.see();
this.host.rent();
this.signContract();
this.fare();
}
private void see(){
System.out.println("中介带你看房");
}
private void signContract(){
System.out.println("中介带你签合同");
}
private void fare(){
System.out.println("中介收取费用");
}
}
class Test{
public static void main(String[] args) {
Host host = new Host();
new Proxy(host).rent();
}
}
结果:
中介带你看房
房东出租房子
中介带你签合同
中介收取费用
2.1.3 静态代理模式图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hi7BnV7n-1580661342924)(E6145326D43348478CD44DE5ADFA17CE)]
2.1.4 静态代理模式好处
- 使得真实角色的操作更加纯粹,不用关注其它业务
- 公共业务交给代理,是现业务的分工
- 公共业务发生扩展的时候,方便集中管理
2.1.5 静态代理模式缺点
- 一个真实角色会产生至少一个代理类,代码量会翻倍,可以使用动态代理来
2.2 动态代理模式
问题描述
- 有用户服务接口UserService,包含增删改查四种功能行为
- 有UserService实现类,实现具体的功能
- 创建代理类来对实现类的每个功能添加一个日志记录功能,记录执行了什么方法
2.2.1 角色分析
- 与静态代理角色相同
2.2.2 与静态代理的区别
- 动态代理是动态生成的,并不是事先写好的
2.2.3 动态代理模式分类
- 基于接口的动态代理
- JDK动态代理
- 基于类
- cglib
- java字节码实现
- javasist
2.2.4 需要使用的基础类
(1)java.lang.reflect.Proxy
- Proxy提供了用于创建对象的静态方法,这些对象充当接口实例但允许自定义方法调用
- 代理类是在运行时创建的类,它实现指定的接口列表,称为代理接口 。 代理实例是代理类的实例。 每个代理实例都有一个关联的调用处理程序对象,该对象实现了接口InvocationHandler 。 通过其代理接口之一对代理实例的方法调用将被分派到实例的调用处理程序的invoke方法,传递代理实例,标识被调用方法的java.lang.reflect.Method对象,以及包含参数的类型Object的数组。 调用处理程序适当地处理编码的方法调用,并且它返回的结果将作为代理实例上的方法调用的结果返回
(2)java.lang.reflect.InvocationHandler
- InvocationHandler是由代理实例的调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序。 在代理实例上调用方法时,方法调用将被编码并调度到其调用处理程序的invoke方法。
2.3 测试代码
- 基于接口的动态代理模式
/**
* @author Ni187
* 用户服务功能接口
*/
public interface UserService {
/**
* 添加用户数据
*/
void add();
/**
* 删除用户数据
*/
void delete();
/**
* 更新用户数据
*/
void update();
/**
* 查询用户数据
*/
void query();
}
/**
* @author Ni187
* UserService实现类
*/
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("查询用户数据");
}
}
/**
* @author Ni187
* 动态代理生成类
*/
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理接口对象
*/
private Object target;
/**
* 生成得到动态代理类
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces() ,this);
}
public void setTarget(Object target){
this.target = target;
}
/**
* 处理方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行"+method.getName()+"方法前--"+ new Date());
Object result = method.invoke(target, args);
System.out.println("执行"+method.getName()+"方法后--"+ new Date());
System.out.println("===========================================");
return result;
}
}
/**
* @author Ni187
* 测试类
*/
public class Test {
public static void main(String[] args) {
ProxyInvocationHandler proxyCreator = new ProxyInvocationHandler();
proxyCreator.setTarget(new UserServiceImpl());
UserService userServiceProxy = (UserService) proxyCreator.getProxy();
userServiceProxy.add();
userServiceProxy.delete();
userServiceProxy.update();
userServiceProxy.query();
}
}
结果:
执行add方法前--Wed Jan 22 23:16:30 CST 2020
添加用户数据
执行add方法--Wed Jan 22 23:16:30 CST 2020
===========================================
执行delete方法前--Wed Jan 22 23:16:30 CST 2020
删除用户数据
执行delete方法--Wed Jan 22 23:16:30 CST 2020
===========================================
执行update方法前--Wed Jan 22 23:16:30 CST 2020
更新用户数据
执行update方法--Wed Jan 22 23:16:30 CST 2020
===========================================
执行query方法前--Wed Jan 22 23:16:30 CST 2020
查询用户数据
执行query方法--Wed Jan 22 23:16:30 CST 2020
===========================================
2.4 静态代理优点
- 拥有静态代理的全部优点
- 动态代理类代理一个接口
- 一个动态代理类可以代理多个类,只要实现同一个接口(一类业务)