代理模式
一.静态代理
痛点
假如我们想对方法进行一个增强, 需要修改源代码. 假如有100个方法, 我们需要修改100个方法的源代码来实现方法增强.
/**
* 增删改查方法增强例子
* @author : Shen Hanbo
* @date : 2020/11/9 16:01
*/
public class CrudServiceImpl implements CrudService {
public void insert() {
System.out.println("增强新增前置处理");
System.out.println("新增");
System.out.println("增强新增后置处理");
}
public void delete() {
System.out.println("增强删除前置处理");
System.out.println("删除");
System.out.println("增强删除后置处理");
}
public void update() {
System.out.println("增强更新前置处理");
System.out.println("更新");
System.out.println("增强更新后置处理");
}
public void query() {
System.out.println("增强查询前置处理");
System.out.println("查询");
System.out.println("增强查询后置处理");
}
//...假如下面还有一堆方法的话, 每个方法都需要去手动改源代码
}
解决思路
生成一个代理类, 代理类要和被代理类拥有一样方法(实现同一个接口), 然后调用方直接调用代理类的方法, 而非被代理类的方法. 在代理类的实现方法中, 增强被代理类的方法, 然后客户端请求代理类.
代码实现
这里用房东/中介/租客来举例子
-
首先房东和中介都需要有"租房"这个方法, 中介来增强房东的"租房"方法,因此需要定义一个租房接口.
/** * 租房接口 * @author : Shen Hanbo * @date : 2020/11/9 15:52 */ public interface ZuFang { /** * 房东和中介都需要实现租房接口,因为他们都有租房的功能 */ void zuFang(); } -
房东的"租房"方法, 即被代理的方法
/** * 房东类 * @author : Shen Hanbo * @date : 2020/11/9 15:54 */ public class FangDong implements ZuFang { /** * 房东出租房子(被代理方法) */ public void zuFang() { System.out.println("房东出租房子"); } } -
中介的"租房"方法, 即代理方法, 在代理方法中对原方法进行增强
/** * 中介(代理类) * @author : Shen Hanbo * @date : 2020/11/9 16:10 */ public class ZhongJie implements ZuFang { /** * 需要一个房东 */ private FangDong fangDong; public ZhongJie(FangDong fangDong) { this.fangDong = fangDong; } /** * 中介代理房东出租房子(代理方法) */ public void zuFang() { //前置增强 System.out.println("前置增强"); //原方法 fangDong.zuFang(); //后置增强 System.out.println("后置增强"); } } -
调用
public static void main(String[] args) { //房东 FangDong fangDong = new FangDong(); //中介代理房东 ZhongJie zhongJie = new ZhongJie(fangDong); //调用中介的租房方法 zhongJie.zuFang(); } /* 打印结果: 前置增强 房东出租房子 后置增强 */
二.动态代理
痛点
-
静态代理需要手动编写代理类, 比如上个例子中的ZhongJie类.
-
静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类, 每个接口都需要自己的代理类.
解决思路
在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
代码实现
InvocationHandler接口
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
//实际调用的方法会走这个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
Proxy类
//用于获取代理类
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
实现
public class LogHandler implements InvocationHandler {
// 目标对象(被代理类)
private Object targetObject;
//生成代理类:绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object targetObject){
this.targetObject=targetObject;
//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
//根据传入的目标返回一个代理对象
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("动态代理前置增强");
Object ret=method.invoke(targetObject, args);
System.out.println("动态代理后置增强");
return ret;
}
}
public static void main(String[] args) {
LogHandler logHandler = new LogHandler(new FangDong());
ZuFang o = (ZuFang)logHandler.newProxyInstance();
o.zuFang();
}