代理模式(Proxy)

设计模式

        代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(结构型模式)

代码示例:
业务需求:银行提供 存钱取钱 的功能

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 包来实现动态代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值