【设计模式】动态代理示例
需求
目前有新旧两个业务:
- 旧业务:买家调用action购买衣服,衣服在数据库的标价为50元,所以直接返回50元。购买流程就是简单的调用。
- 新业务:衣服在原先的价格上可以使用优惠券,买家调用action购买衣服并使用优惠券10元,返回衣服的价格为40元。
分析
- 在旧业务中,我们用action调用service的方法实现业务即可。
- 但是在新业务中,由于之前在service中实现的业务不能够满足当前客户的要求,我们需要重新修改service中的方法,但是service的方法不只在我们这个模块使用,在其他模块也在调用,其他模块调用的时候,现有的service方法已经能够满足业务需求,所以我们不能只为了我们的业务而修改service,导致其他模块受影响。
- 那怎么办呢?我们可以通过动态代理的方式,扩展我们的service中的方法实现,使得在原有的方法中增加更多的业务,而不是实际修改service中的方法,这种实现技术就叫做动态代理。
改造过程
- 书写代理类和代理方法,在代理方法中实现代理
Proxy.newProxyInstance
。 - 代理中需要的参数分别为
- 被代理类的类加载器:
someObjectclass.getClassLoader()
- 被代理类的所有实现接口:
new Class[] { Interface.class }
- 被代理类的委托类:
new InvocationHandler()
- 在被代理类的委托类中复写
invoke
方法,之后想要调用被代理类的方法时,都会委托给这个类的invoke
方法 - 在这个方法中,我们可以定制化的开发新的业务。
invoke
方法的输入有3个参数
- 代理类对象:
Object proxy
- 被代理类的方法:
Method method
- 被代理类方法的传入参数:
Object[] args
- 获取代理类,强转成被代理的接口
- 最后,我们可以像没被代理一样,调用接口的任何方法,方法被调用后,方法名和参数列表将被传入代理类的
invoke
方法中,进行新业务的逻辑流程。
示例代码
业务接口
/**
* 这是一个业务的接口,这个接口中的业务就是返回衣服的价格
* @author dj4817
* @version $Id: IBoss.java, v 0.1 2017/12/3 9:32 dj4817 Exp $$
*/
public interface IBoss {
/**
* 衣服的价格
* @param size
* @return
*/
int yifu(String size);
}
业务接口实现类
/**
* 衣服销售业务实现类
* @author dj4817
* @version $Id: Boss.java, v 0.1 2017/12/3 9:34 dj4817 Exp $$
*/
public class Boss implements IBoss {
/**
* 衣服的价格
* @param size
* @return
*/
@Override
public int yifu(String size) {
System.out.println("天猫小强旗舰店,老板给客户发快递----衣服型号:" + size);
// 这件衣服的价钱,从数据库读取
return 50;
}
}
原业务逻辑
/**
* 销售业务
* @author dj4817
* @version $Id: SaleAction.java, v 0.1 2017/12/3 9:36 dj4817 Exp $$
*/
public class SaleAction {
public static void main(String[] args) {
IBoss boss = new Boss();
System.out.println("老板自营!");
// 老板自己卖衣服,不需要客服,结果就是没有聊天记录
int money = boss.yifu("xxl");
System.out.println("衣服成交价:" + money);
}
}
新业务逻辑
/**
* 代理销售业务
* @author dj4817
* @version $Id: ProxySaleAction.java, v 0.1 2017/12/3 9:38 dj4817 Exp $$
*/
public class ProxySaleAction {
public static void main(String[] args) {
// 将代理的方法实例化成接口
IBoss boss = ProxyUtil.getProxyBoss(10, Boss.class);
System.out.println("代理经营!");
// 调用接口的方法,实际上调用方式没有变
int money = boss.yifu("xxl");
System.out.println("衣服成交价:" + money);
}
}
新业务逻辑中获取代理类工具
/**
* 代理获取
*
* @author dj4817
* @version $Id: ProxyUtil.java, v 0.1 2017/12/3 9:39 dj4817 Exp $$
*/
public class ProxyUtil {
/**
* 获取Boss的代理对象
* @param discountCoupon 优惠金额
* @param implementClass 被代理类
* @param <T>
* @return
*/
public static <T> T getProxyBoss(int discountCoupon, Class<?> implementClass) {
// 被代理类的类加载器
ClassLoader classLoader = implementClass.getClassLoader();
// 被代理类的所有实现接口
Class<?>[] interfaces = implementClass.getInterfaces();
Object object = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy invoke begin");
System.out.println("proxy: " + proxy.getClass().getName());
System.out.println("method: " + method.getName());
for (Object o : args) {
System.out.println("arg: "+ o);
}
// 被代理类的实例
Object implement = implementClass.newInstance();
Integer result = (Integer) method.invoke(implement, args);
System.out.println("Proxy invoke end");
return result - discountCoupon;
}
});
return (T) object;
}
}
评论区