赞
踩
代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制
对这个对象的访问。代理对象在客户端和目标之前起到了中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一是保护目标对象,而是增强目标对象。
要弄清楚代理模式,首先我们需要了解什么是结构型设计模式。结构型设计模式存在的目的主要是:在解决了对象的创建问题之后,对象的组成和对象之间的依赖关系就成了开发人员关注的焦点。因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等等。因此,结构型设计模式最主要涉及和关心的是如何组合类和对象来获得更大的结构。结构型设计模式采用继承机制来组合接口火实现(也称为类结构型模式),或者通过组合一些对象从而实现一些新的功能(也称为对象结构型模式)。
以买票的行为为例,现在我们买票可以直接在12306上进行购票,也可以通过第三方平台如智行等进行购票
购票接口:
public interface BuyTicket {
/**
* 购票
*/
void buyTicket();
}
被代理对象:
public class User implements BuyTicket{
/**
* 购票
*/
@Override
public void buyTicket() {
System.out.println("用户:请求购票");
}
public void offerInfo(){
System.out.println("用户:提供购票需要的个人信息");
}
}
代理对象:
public class BuyTicketProxy implements BuyTicket{ private User user; public BuyTicketProxy(User u){ user=u; } /** * 购票 */ @Override public void buyTicket() { user.buyTicket(); System.out.println("代理平台:请求获取用户信息"); user.offerInfo(); System.out.println("代理平台:购票完成!!!"); } }
客户端:
public class Client {
public static void main(String[] args) {
BuyTicketProxy buyTicketProxy=new BuyTicketProxy(new User());
buyTicketProxy.buyTicket();
}
}
从以上代码可以看出,代理平台在进行购票时,实际上仍然是调用用户的功能逻辑,用户自身并不需要关心自己的方法何时使用,交由代理平台控制。
在分布式业务中 ,通常会对数据库进行分库分表操作,分库分表之后使用Java操作时就可能需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。
创建订单类:
@Getter
@Setter
@ToString
public class Order {
private String id;
private Object orderInfo;
private Long createTime;
}
创建OrderDao持久层操作
public class OrderDao {
public int insert(Order order) {
System.out.println("OrderDao 创建Order成功");
return 1;
}
}
创建IOrderService接口
public interface IOrderSerivce {
/**
* 创建Order
* @param order order
* @return int
*/
public int createOrder(Order order);
}
创建OrderService实现类
public class OrderService implements IOrderSerivce{
private OrderDao orderDao;
public OrderService(){
orderDao=new OrderDao();
}
@Override
public int createOrder(Order order){
System.out.println("OrderService调用OrderDao创建订单");
return orderDao.insert(order);
}
}
创建数据源路由对象,是与哦给你ThreadLocal的单例对象实现DynamicDataSourceEntry类
public class DynamicDataSourceEntry { /** * 默认数据源 */ public final static String DEFAULT_SOURCE=null; private final static ThreadLocal<String> LOCAL=new ThreadLocal<>(); private DynamicDataSourceEntry(){} /** * 清空数据源 */ public static void clear(){ LOCAL.remove(); } /** * 获取数据源 * @return 数据源 */ public static String get(){ return LOCAL.get(); } /** * 还原当前切换的数据源 */ public static void restore(){ LOCAL.set(DEFAULT_SOURCE); } /** * 设置数据源 * @param dataSource 数据源 */ public static void set(String dataSource){ LOCAL.set(dataSource); } /** * 根据年份动态设置数据源 * @param year */ public static void set(int year){ LOCAL.set("DB_"+year); } }
创建OrderService的静态代理类,主要完成的功能时根据订单创建时间自动按年份选择数据库。
public class OrderServiceStaticProxy implements IOrderSerivce{ private IOrderSerivce orderService; private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy"); public OrderServiceStaticProxy(IOrderSerivce orderService){ this.orderService=orderService; } /** * 创建Order * * @param order order * @return int */ @Override public int createOrder(Order order) { System.out.println("Proxy before method"); Long time=order.getCreateTime(); int dbRouter= Integer.parseInt(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据"); DynamicDataSourceEntry.set(dbRouter); int res=orderService.createOrder(order); System.out.println("Proxy after method"); return res; } }
测试类
public class Client {
public static void main(String[] args) throws ParseException {
Order order=new Order();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
Date date=sdf.parse("2020/05/03");
order.setCreateTime(date.getTime());
IOrderSerivce orderService=new OrderServiceStaticProxy(new OrderService());
orderService.createOrder(order);
}
}
上述的静态代理需要代理对象和目标对象实现相同的接口,这意味着随着代理对象和目标对象的不断添加,一旦接口增加了方法,目标对象和代理对象都需维护,代码会变得相当繁琐和冗余。
为此我们需要一种适应性更强的代理模式,这里JDK为我们提供了一种基于接口的动态代理。使用JDK的动态代理需要我门使用Proxy类以及InvocationHandler这个接口并实现invoke方法,调用Proxy类的静态方法newProxyInstance()。我们先查看下该方法的源码需要的参数
ClassLoader loader
指定目标对象使用的类加载器,可以使用Object.getClass().getClassLoader()
获取
Class<?>[ ] interfaces
目标对象实现的接口的类型,可用通过Object.getClass().getInterfaces()
获取
InvocationHandler h
事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行的目标对象的方法作为参数传入
现在我们再回顾下刚才的业务,将静态的数据源路由改成动态路由的业务。
创建动态代理类
public class OrderServiceDynamicProxy implements InvocationHandler { private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy"); private Object target; public OrderServiceDynamicProxy(Object target){ this.target=target; } public Object getInstance(){ Class<?> clazz=target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(args[0]); Object object=method.invoke(target,args); System.out.println("Proxy after method"); return object; } private void before(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { System.out.println("Proxy before method"); Long time= (Long) obj.getClass().getDeclaredMethod("getCreateTime").invoke(obj); int dbRouter= Integer.parseInt(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据"); DynamicDataSourceEntry.set(dbRouter); } }
测试类
public class Client {
public static void main(String[] args) throws ParseException {
Order order=new Order();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
Date date=sdf.parse("2020/05/03");
order.setCreateTime(date.getTime());
IOrderSerivce orderService= (IOrderSerivce) new OrderServiceDynamicProxy(new OrderService()).getInstance();
orderService.createOrder(order);
}
}
基于子类的动态代理技术,要求被代理类不可被final修饰,使用的是CGLib库提供的Enhance类,调用其create方法创建代理对象。create方法的传入参数为:
Class type
指定被代理对象的字节码文件
callback
回调接口,这里一般使用MethodInterceptor的实现类
继续对之前提到的业务类进行改造
public class OrderServiceCGLibProxy implements MethodInterceptor { private SimpleDateFormat yearFormat=new SimpleDateFormat("yyyy"); private Object target; public OrderServiceCGLibProxy(Object target){ this.target=target; } public Object getInstance(){ return Enhancer.create(target.getClass(),this); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(objects[0]); Object object=methodProxy.invokeSuper(o,objects); System.out.println("Proxy after method"); return object; } private void before(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { System.out.println("Proxy before method"); Long time= (Long) obj.getClass().getDeclaredMethod("getCreateTime").invoke(obj); int dbRouter= Integer.parseInt(yearFormat.format(new Date(time))); System.out.println("静态代理类自动分配到DB_"+dbRouter+"数据源处理数据"); DynamicDataSourceEntry.set(dbRouter); } }
createOrder()
方法为例。代理对象调用this.createOrder()方法->调用拦截器->methodProxy.invokeSuper->CGLIB$createOrder()$0->被代理对象createOrder()
方法init()
方法,init()
方法中获取index。1.JDK动态代理实现类被代理类的接口,CGLib代理继承了被代理对象。
2.JDK动态代理和CGLib代理都在运行期生成了字节码文件,JDK动态代理直接生成Class字节码文件,而CGLib代理使用ASM框架生成Class字节码文件,CGLib代理实现更加复杂,生成代理类的效率比通过JDK动态代理生成的效率低,即JDK动态代理字节码文件的生成效率高。
3.JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,所以CGLib代理的执行效率高。
1.静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,那么代理类也需要同步增加方法,这一点上违背了开闭原则
2.动态代理采用在运行时动态生成代码的方式,取消了对被代理类扩展的限制,遵循开闭原则
3.动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
优点:
缺点:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。