代理模式
proxy
代理模式可以分为静态代理和动态代理
静态代理
租房例子
角色
大话设计模式角色结构图
- 抽象角色:一般会使用接口或抽象类来解决(租房)
- 真实角色(被代理对象或目标对象):被代理的角色(房东)
- 代理角色(代理对象):代理真实角色,代理真实角色后,会做一些附属操作(中介)
- 客户:访问代理对象的人(我们,租房的人)
代理模式租房代码
结构图
抽象角色(接口)
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:抽象角色 租房
*/
public interface AbstractRole {
//出租
void rent();
}
真实角色
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:真实角色 房东
*/
public class RealRole implements AbstractRole{
@Override
public void rent() {
System.out.println("房东出租房屋......");
}
}
代理角色
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:代理角色 中介
*/
public class ProxyRole implements AbstractRole{
//通过接口聚合 房东
private AbstractRole houseMaster;
public ProxyRole(AbstractRole houseMaster) {
this.houseMaster = houseMaster;
}
//帮房东出租房子
@Override
public void rent() {
seeHouse();
qianHeTong();
houseMaster.rent();
}
private void seeHouse(){//看房
System.out.println("帮客户看房子");
}
private void qianHeTong(){//签合同
System.out.println("跟客户签合同");
}
}
客户端
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:
*/
public class Client {
public static void main(String[] args) {
//房东
RealRole houseMaster = new RealRole();
//中介
ProxyRole zhongjie = new ProxyRole(houseMaster);
zhongjie.rent();
}
}
/*
帮客户看房子
跟客户签合同
房东出租房屋......
*/
我看不到房东,但我依旧通过代理(中介)租到的房东的房子,这就是所谓的代理模式 中间加了一层代理角色
我没有直接调用房东的rent方法而是通过代理角色去调用
总结
使用的关键
- 真实角色,代理角色 要实现同一个接口(抽象角色)
- 接口要聚合代理角色,通过构造器将真实角色传给代理角色,方便调用
静态代理优缺点
优点:
-
不修改真实角色功能的情况下,可以使用代理角色去扩展功能 (比如代码中的扩展功能: 看房子和签合同)
-
可以使真实角色更加纯粹,不用去关注一些公共业务(房东只用管租房不用去签合同,看房之类)
-
公共业务交给代理角色,实现了业务的分工
-
公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色会产生一个代理角色,代码量翻倍,开发效率变低
- 接口增加方法时,真实角色与代理角色都要维护
动态代理
- 动态代理的角色和静态代理一样
- 动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
- 动态代理分为俩类:一类是基于类的动态代理,一类是基于接口的动态代理
- 基于接口的动态代理——JDK动态代理
- 基于类的动态代理——cglib
JDK动态代理
JDK动态代理需要了解俩个类
核心:Proxy (代理)与 InvocationHandler(调用处理程序)
Proxy
InvocationHandler(调用处理程序)
角色代码
抽象角色
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:抽象角色 租房
*/
public interface AbstractRole {
//出租
void rent();
}
真实角色
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:
*/
public class RealRole implements AbstractRole {
@Override
public void rent() {
System.out.println("房东出租房屋......");
}
}
动态生成代理角色
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:
*/
public class ProxyFactory implements InvocationHandler {
//真实角色 被代理类
private Object realRole;
public ProxyFactory(Object realRole) {
this.realRole = realRole;
}
//动态获得代理类
public Object getProxy(){
/*
Proxy.newProxyInstance方法参数如下
ClassLoader loader: 加载真实角色的类加载器
Class<?>[] interfaces: 真实角色实现的接口(获取要代理的抽象角色)
InvocationHandler h: 调用处理程序,调用接口方法时会来调用 InvocationHandler的invoke方法
(在invoke方法中通过反射调用接口方法,还可以在invoke方法中增加其他方法功能,类似静态代理中的代理角色实现的接口方法)
* */
return Proxy.newProxyInstance(realRole.getClass().getClassLoader()
,realRole.getClass().getInterfaces()
//因为ProxyFactory实现了InvocationHandler接口所以填this 事件处理时按照实现的invoke执行
,this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* proxy:代理角色实例
* method:代理角色调用的接口方法
* args:method方法的参数
* */
seeHouse();
Object result = method.invoke(realRole, args);
qianHeTong();
return result;
}
private void seeHouse(){//看房
System.out.println("帮客户看房子");
}
private void qianHeTong(){//签合同
System.out.println("跟客户签合同");
}
}
客户端测试
/**
* @author Tc.l
* @Date 2021/1/26
* @Description:
*/
public class Client {
public static void main(String[] args) {
//房东
RealRole houseMaster = new RealRole();
//动态代理得到代理角色
ProxyFactory proxyFactory = new ProxyFactory(houseMaster);
AbstractRole proxy = (AbstractRole) proxyFactory.getProxy();
//调用代理角色的方法
proxy.rent();
}
}
/*
帮客户看房子
房东出租房屋......
跟客户签合同
*/
总结
JDK动态代理好处(静态代理的好处它都有):
- 使真实角色更纯粹,不去关注其他公共事情
- 公共的业务由代理来完成,实现了业务的分工
- 公共业务发生扩展时变得更加集中和方便
- JDK动态代理可以代理多个类,代理的是接口(一类业务)
Cglib动态代理
有时候真实角色只是一个单独对象未实现接口,这是可以使用Cglib动态代理
Cglib动态代理在内存中构建一个真实角色的子类对象从而实现对真实角色的功能扩展,也被称为子类代理
特点: 真实角色(被代理类)不用实现接口
角色代码
导入Cglib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
真实角色
public class RealRole {
public void rent(){
System.out.println("出租房子.....");
}
}
cglib动态获得代理类
public class ProxyFactory implements MethodInterceptor {
//真实角色 被代理类
private Object realRole;
public ProxyFactory(Object realRole) {
this.realRole = realRole;
}
//获得代理角色(代理类)
public Object getProxyInstance(){
//1.使用工具类
Enhancer enhancer = new Enhancer();
//2.设置真实角色为代理角色的父类
enhancer.setSuperclass(realRole.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建代理角色
return enhancer.create();
}
private void seeHouse(){//看房
System.out.println("帮客户看房子");
}
private void qianHeTong(){//签合同
System.out.println("跟客户签合同");
}
//类似JDK动态代理中 InvocationHandler 的invoke
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
seeHouse();
Object result = method.invoke(realRole, args);
qianHeTong();
return result;
}
}
客户端测试租房
public class Client {
public static void main(String[] args) {
//真实角色
RealRole houseMaster = new RealRole();
//cglib动态代理 获得代理类
ProxyFactory proxyFactory = new ProxyFactory(houseMaster);
RealRole proxy = (RealRole) proxyFactory.getProxyInstance();
//租房
proxy.rent();
}
}
/*
帮客户看房子
出租房子.....
跟客户签合同
*/
结构图
总结
Cglib获得代理角色:
- 获得代理角色要实现
MethodInterceptor
- 使用工具类
Enhancer
获得代理类(设置父类,设置回调,创建) - 重写
MethodInterceptor
接口的intercept
方法,可以在该方法中扩展其他功能
真实角色不用实现接口,Cglib是基于类的动态代理,代理类是被代理类的子类(代理角色是真实角色的子类)