设计模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(结构型模式)
代码示例:
业务需求:银行提供 存钱 和 取钱 的功能
1、提供一个银行类的接口,包含 存钱 和 取钱 的功能
package pattern.proxy;
/**
* 银行接口
* @author L
*/
public interface IBank {
/**
* 存钱
*/
void topUp();
/**
* 取钱
*/
void withdraw();
}
2、为方便输出,我们提供一个输出工具,相当于打出日志
package pattern.proxy;
/**
* 日志
* @author L
*/
public class Logger {
private Class<?> cls;
private Logger(Class<?> cls) {
this.cls = cls;
}
public static Logger getLogger(Class<?> cls) {
return new Logger(cls);
}
public void info(String msg) {
System.out.println(cls.getName() + "[info] - " + msg);
}
}
3、紧接着,我们就可以实现 IBank 中的业务逻辑
package pattern.proxy;
/**
* @author L
*/
public class SimpleBankImpl implements IBank {
private Logger logger = Logger.getLogger(SimpleBankImpl.class);
@Override
public void topUp() {
logger.info("topUp ...");
}
@Override
public void withdraw() {
logger.info("withdraw ...");
}
}
以上就单纯的实现了业务需求,测试下结果
public static void test1() {
IBank simpleBank = new SimpleBankImpl();
simpleBank.topUp();
simpleBank.withdraw();
}
// pattern.proxy.SimpleBankImpl[info] - topUp ...
// pattern.proxy.SimpleBankImpl[info] - withdraw ...
4、现在,我们想要在执行 存钱 和 取钱 的方法前后都分别加一些别的日志,可以考虑在不改变现有代码的前提下,使用代理模式实现新的逻辑
package pattern.proxy;
/**
* @author L
*/
public class ProxyBankImpl implements IBank {
private Logger logger = Logger.getLogger(ProxyBankImpl.class);
private IBank bank;
public ProxyBankImpl(IBank bank) {
this.bank = bank;
}
@Override
public void topUp() {
logger.info("start topUp");
bank.topUp();
logger.info("end topUp");
}
@Override
public void withdraw() {
logger.info("start withdraw");
bank.withdraw();
logger.info("end withdraw");
}
}
可以看到,程序会先执行 ProxyBankImpl 的 start 日志,之后再执行 SimpleBankImpl 的内容,最后执行 end 日志。测试结果:
public static void test2() {
IBank simpleBank = new SimpleBankImpl();
IBank proxyBank = new ProxyBankImpl(simpleBank);
proxyBank.topUp();
proxyBank.withdraw();
}
// pattern.proxy.ProxyBankImpl[info] - start topUp
// pattern.proxy.SimpleBankImpl[info] - topUp ...
// pattern.proxy.ProxyBankImpl[info] - end topUp
// pattern.proxy.ProxyBankImpl[info] - start withdraw
// pattern.proxy.SimpleBankImpl[info] - withdraw ...
// pattern.proxy.ProxyBankImpl[info] - end withdraw
补充:
1、JDK 自带的动态代理
在写到上述的第3步后,我们可以不需要实现 ProxyBankImpl,而使用 JDK 自带的 Proxy 动态生成代理对象。
1.1 需要实现 JDK 的 InvocationHandler 接口,自定义实现类通过反射生成
package pattern.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author L
*/
public class BankHandler implements InvocationHandler {
private Logger logger = Logger.getLogger(BankHandler.class);
private IBank bank;
public BankHandler(IBank bank) {
this.bank = bank;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
logger.info("start " + method.getName());
Object obj = method.invoke(bank, args);
logger.info("end " + method.getName());
return obj;
}
}
1.2 原理与上述第4步相同,只不过是通过反射实现,代码会更简洁。
public static void test3() {
IBank simpleBank = new SimpleBankImpl();
ClassLoader loader = simpleBank.getClass().getClassLoader();
Class<?>[] interfaces = simpleBank.getClass().getInterfaces();
BankHandler handler = new BankHandler(simpleBank);
IBank proxyBank = (IBank) Proxy.newProxyInstance(loader, interfaces, handler);
proxyBank.topUp();
proxyBank.withdraw();
}
// pattern.proxy.BankHandler[info] - start topUp
// pattern.proxy.SimpleBankImpl[info] - topUp ...
// pattern.proxy.BankHandler[info] - end topUp
// pattern.proxy.BankHandler[info] - start withdraw
// pattern.proxy.SimpleBankImpl[info] - withdraw ...
// pattern.proxy.BankHandler[info] - end withdraw
2、Cglib 动态代理实现
之前所提到的代理对象,都是实现某一接口来完成代理,而 Cglib 它可以为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
Cglib 为开源项目,需要导入相应的 jar 包来实现动态代理。