博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
代理模式
阅读量:6576 次
发布时间:2019-06-24

本文共 5109 字,大约阅读时间需要 17 分钟。

代理模式

定义

为其他对象提供一种代理,以控制对这个对象的访问。(代理对象在客户端和目标对象之间起到中介的作用)

优点
  1. 代码模式能将代理对象与真实被调用的目标对象分离。
  2. 一定程度上降低了系统的耦合度,扩展性好。
  3. 保护目标对象。(客户端直接交互的是代理类而不是目标对象,这样就保护了目标对象)
  4. 增强目标对象
缺点
  1. 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
  2. 增加了系统的复杂度。
分类
静态代理

在代码中显式的代理目标。

动态代理

jdk中的动态代理只能对实现了接口的类进行动态代理。并不能针对一个具体的类进行动态代理。无法代理可以代理接口,在代理类的时候只有在调用到代理类是才由jvm动态的创建,jvm根据传进来的对象以及代理方法名动态的创建了代理类的class文件,这个class文件被字节码引擎执行,然后通过该代理类的对象进行方法的调用。(比CGLib快大约百分之10)

CGLib代理

主要针对jdk无法代理具体的类的问题。实现原理是生成一个被代理类的子类,通过覆盖父类的方法来实现动态代理。也就是用过继承然后重写,所以被代理的不能是一个final的类或者是代理方法不能是一个final方法。

spring中代理选择

  1. 当bean有实现接口时,spring 就会使用jdk的动态代理。
  2. 当bean没有实现接口时,spring使用CGLib
  3. 可以强制使用CGLib (具体配置自行谷歌吧)

下面我们实现一下静态。我们假设一个应用场景就是,要保存一个订单,订单就包括订单实体类,service。dao等,我们要用代理的方式来实现数据库分库,不同的订单要放在不同的数据库中。下面开始看代码。

订单实体类。

public class Order {    private Object orderInfo;    private Integer userId;    public Object getOrderInfo() {        return orderInfo;    }    public void setOrderInfo(Object orderInfo) {        this.orderInfo = orderInfo;    }    public Integer getUserId() {        return userId;    }    public void setUserId(Integer userId) {        this.userId = userId;    }}复制代码

订单操作的dao接口

public interface IOrderDao {    int insert(Order order);}复制代码

dao的实现类,我们这里只是模拟一下数据库操作。

public class OrderDaoImpl implements IOrderDao {    @Override    public int insert(Order order) {        System.out.println("Dao层添加Order成功");        return 1;    }}复制代码

service接口

public interface IOrderService {    int saveOrder(Order order);}复制代码

service实现类,我们没有集成spring 模拟操作

public class OrderServiceImpl implements IOrderService {    private IOrderDao iOrderDao;    @Override    public int saveOrder(Order order) {        //Spring会自己注入,我们课程中就直接new了        iOrderDao = new OrderDaoImpl();        System.out.println("Service层调用Dao层添加Order");        return iOrderDao.insert(order);    }}复制代码

要分库我们来用ThreadLocal实现一个上下文对象,模拟暂放在哪个数据的操作。

public class DataSourceContextHolder {    private static final ThreadLocal
CONTEXT_HOLDER = new ThreadLocal
(); public static void setDBType(String dbType){ CONTEXT_HOLDER.set(dbType); } public static String getDBType(){ return (String)CONTEXT_HOLDER.get(); } public static void clearDBType(){ CONTEXT_HOLDER.remove(); }}复制代码

动态的使用数据库的实现。(模拟操作)

public class DynamicDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        return DataSourceContextHolder.getDBType();    }}复制代码

进入重点,代理类的实现。这里的核心就是在执行代理方式之前加入我们的增强操作。

public class OrderServiceStaticProxy {    private IOrderService iOrderService;    public int saveOrder(Order order){        beforeMethod(order);        iOrderService = new OrderServiceImpl();        int result = iOrderService.saveOrder(order);        afterMethod();        return result;    }    private void beforeMethod(Order order){        int userId = order.getUserId();        int dbRouter = userId % 2;        System.out.println("静态代理分配到【db"+dbRouter+"】处理数据");        //todo 设置dataSource;        DataSourceContextHolder.setDBType("db"+dbRouter);        System.out.println("静态代理 before code");    }    private void afterMethod(){        System.out.println("静态代理 after code");    }}复制代码

最后就是测试类

public class StaticProxyTest {    public static void main(String[] args) {        Order order = new Order();        order.setUserId(2);        OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();        orderServiceStaticProxy.saveOrder(order);    }}复制代码

输出结果

静态代理分配到【db0】处理数据静态代理 before codeService层调用Dao层添加OrderDao层添加Order成功静态代理 after code复制代码

静态代理每次代理都要写一个代理类,代理方法,容易产生类爆炸的情况。下面我们实现一下动态代理。 实现动态的时我们需要实现InvocationHandler接口,实现invoke方法这个方法需要Object proxy, Method method, Object[] objs args三个参数,一个参数是动态生成的类的对象,一般我们在invoke方法中很少用到。method顾名思义就是需要代理的方法,Object数据就是参数集合。在使用的时候先要 Proxy.newProxyInstance创建一个被代理的对象,这个方法有ClassLoader loader, Class[] interfaces, InvocationHandler h三个参数,ClassLoader对象我们通过getClass方法的getClassLoad方法很容易拿到,Class[] interfaces就是接口集合了,通过getInterfaces也可以很容易拿到,最后一个就是动态代理类的对象了。下面我们看一下代码。

public class OrderServiceDynamicProxy implements InvocationHandler {    private Object target;    public OrderServiceDynamicProxy(Object target) {        this.target = target;    }    public Object bind(){        Class cls = target.getClass();        return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        Object argObject = args[0];        beforeMethod(argObject);        Object object = method.invoke(target,args);        afterMethod();        return object;    }    private void beforeMethod(Object obj){        int userId = 0;        System.out.println("动态代理 before code");        if(obj instanceof Order){            Order order = (Order)obj;            userId = order.getUserId();        }        int dbRouter = userId % 2;        System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");        //todo 设置dataSource;        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));    }    private void afterMethod(){        System.out.println("动态代理 after code");    }}复制代码

代理模式就这些内容啦。

转载地址:http://vmwno.baihongyu.com/

你可能感兴趣的文章
mybatis高级(3)_延迟加载_深度延迟_一级缓存_二级缓存
查看>>
从今天起,开源中国
查看>>
带左右按钮、 渐隐渐现 轮播图
查看>>
body标签中的相关标签
查看>>
css3 animate 和关键帧 @-webkit-keyframes
查看>>
指向结构体变量的指针变量
查看>>
文字链接颜色设置
查看>>
ChannelHandler揭秘(Netty源码死磕5)
查看>>
图片转流
查看>>
Jessica's Reading Problem
查看>>
jQuery实现照片墙,附步骤详解
查看>>
vim编辑器常用命令总结
查看>>
大白话讲解如何给github上项目贡献代码
查看>>
常见幻灯片实现
查看>>
一个页面中,不同子页面见高度不受影响的布局
查看>>
Redis的数据类型及其常用命令
查看>>
ubunto应用软件
查看>>
wireshark----教你如何抓包
查看>>
从txt中读入数据到数组中(fscanf)
查看>>
jquery中的事件与动画
查看>>