Java代理模式之静态代理和动态代理
代理模式定义
给目标对象提供一个代理对象,并由代理对象控制对目标对象的访问。

看上面的类图,我们知道代理对象和目标对象需要实现一样的接口。
代理模式目的
1、通过代理对象间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
2、通过代理对象对原有业务做增强。
静态代理与动态代理
我们先从简单的静态代理入手,以便更好地理解代理模式,然后分析静态代理的不足,接着引出动态代理。
静态代理
我们都知道由于关税和汇率的原因,去香港买东西便宜不少,质量也有保障,于是很多人买化妆品、奶粉、电子产品都会去香港。
我们就模拟这样一个场景,先定义接口:
/**
* 女人购物
*
* @author wanglingqiang
* @date 2020/7/18 下午1:19
**/
public interface IWomanShopping {
/**
* 买xxx化妆品
* @param name
*/
void buyMakeup(String name);
}
具体实现:
public class WomanShopping implements IWomanShopping {
@Override
public void buyMakeup(String name) {
System.out.println("买" + name + "的化妆品");
}
}
}
测试:
public class Test {
public static void main(String[] args) {
IWomanShopping womanShopping = new WomanShopping();
// 买美白保湿的化妆品
womanShopping.buyMakeup("美白保湿");
}
}
结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java
买美白保湿的化妆品
Process finished with exit code 0
但如果买的东西不多,自己办签证、来回路费、坐车逛街时间等等,算下来就不划算了,所以我们会请代购帮忙购买。
/**
* 代理对象
* 跟真实对象实现同一个接口,
* 包含真实对象,
* 对真实对象增加
*
* @author wanglingqiang
* @date 2020/7/18 下午1:27
**/
public class Agent implements IWomanShopping {
//被代理的真实对象
private IWomanShopping womanShopping;
public Agent(IWomanShopping womanShopping) {
this.womanShopping = womanShopping;
}
@Override
public void buyMakeup(String name) {
doBefore(); //前置增强
womanShopping.buyMakeup(name);
doAfter(); //后置增强
}
private void doBefore() {
System.out.println("----买前挑选----");
}
private void doAfter() {
System.out.println("----买后包装邮寄----");
}
}
测试:
public class Test {
public static void main(String[] args) {
IWomanShopping womanShopping = new WomanShopping();
// 买美白保湿的化妆品
//womanShopping.buyMakeup("美白保湿");
Agent agent = new Agent(womanShopping);
agent.buyMakeup("美白保湿");
}
}
运行结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java
----买前挑选----
买美白保湿的化妆品
----买后包装邮寄----
Process finished with exit code 0
上面代码示例很好说明了代理模式的思想,在不修改目标对象的前提下扩展目标对象的功能。但也存在缺点:
1、不易维护。比如除了买化妆品,还买个包包,那就得在IWomanShopping接口类增加一个买包包的接口方法,同时WomanShopping、Agent两个类都要做对应实现。违反了开闭原则。
2、冗余。由于代理对象要实现与目标对象一样的接口,会产生很多代理类。想一想Spring的事务、Mybatis的Mapper,就明白了。
我们来看看不易维护的示例。
情况一:IWomanShopping接口类增加一个买包包的接口方法。

情况二:男士想找代购购买Mac笔记本。
男士购买接口:
/**
* 男人购物
*
* @author wanglingqiang
* @date 2020/7/18 下午1:01
**/
public interface IManShopping {
/**
* 买某个尺寸的Mac笔记本
* @param size
*/
void buyMac(String size);
}
男士购物的实现类:
public class ManShopping implements IManShopping {
@Override
public void buyMac(String size) {
System.out.println("买" + size + "寸的Mac笔记本");
}
}
这个时候,实现类和静态代理类都要修改。


所以,这时就需要动态代理了。
动态代理
动态代理又分为JDK和CGLIB动态代理。
JDK动态代理:
使用Proxy和InvocationHandler来实现,InvocationHandler用于实现接口的方法并做增强,Proxy通过反射机制和调用native方法在内存中生成实现指定接口的实现类的字节码,从而实现对目标对象的代理功能。
CGLIB动态代理:
利用ASM开源包,把代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
这里我们以JDK的动态代理来做讲解。
静态代理与动态代理的区别:
1、 静态代理在编译时就已经实现代理过程,编译完成后代理类是一个实际存在的 class 文件;
2、动态代理是在运行时动态生成的,即编译完成后没有实际存在的 class 文件,而是在运行时动态生成类字节码,并加载到 JVM 中。
用动态代理的好处:
1、对原有代码无侵入增强;
2、不会影响原接口的使用。
动态代理的代码示例:
/**
* 动态代理
*
* @author wanglingqiang
* @date 2020/7/18 下午2:03
**/
public class AgentCompany implements InvocationHandler {
//被代理对象
private Object proxyObject;
//通过动态代理对象对方法进行增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object result = method.invoke(proxyObject, args);
doAfter();
return result;
}
//通过Proxy获取动态代理的对象
public Object getProxyInstance(){
/*
* ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h
*/
return Proxy.newProxyInstance(
proxyObject.getClass().getClassLoader(),
proxyObject.getClass().getInterfaces(),
this
);
}
public AgentCompany() {
}
public AgentCompany(Object proxyObject) {
this.proxyObject = proxyObject;
}
public Object getProxyObject() {
return this.proxyObject;
}
public void setProxyObject(Object proxyObject) {
this.proxyObject = proxyObject;
}
private void doBefore() {
System.out.println("----买前挑选----");
}
private void doAfter() {
System.out.println("----买后包装邮寄----\n");
}
}
只需要这一个代理类,就能满足上面前面静态代理的不足。
测试一下:
public class Test {
public static void main(String[] args) {
IWomanShopping womanShopping = new WomanShopping();
// 买美白保湿的化妆品
// womanShopping.buyMakeup("美白保湿");
// Agent agent = new Agent(womanShopping);
// agent.buyMakeup("美白保湿");
AgentCompany agentCompany = new AgentCompany(womanShopping);
womanShopping = (IWomanShopping) agentCompany.getProxyInstance();
womanShopping.buyMakeup("美白保湿");
womanShopping.buyBag("LV");
IManShopping manShopping = new ManShopping();
agentCompany.setProxyObject(manShopping);
manShopping = (IManShopping) agentCompany.getProxyInstance();
manShopping.buyMac("15.4");
}
}
运行结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java
----买前挑选----
买美白保湿的化妆品
----买后包装邮寄----
----买前挑选----
买LV的包包
----买后包装邮寄----
----买前挑选----
买15.4寸的Mac笔记本
----买后包装邮寄----
至此,代理模式已经分享完。后面有时间我会继续分享其他设计模式。
本文深入探讨Java代理模式,包括静态代理与动态代理的概念、实现及优劣对比。通过生动的购物场景,解析如何在不修改目标对象的前提下,通过代理对象实现功能增强。
1066

被折叠的 条评论
为什么被折叠?



