美文网首页
代理模式

代理模式

作者: 三不猴子 | 来源:发表于2019-01-14 22:24 被阅读6次

    代理模式

    定义

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

    优点
    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<String> CONTEXT_HOLDER = new ThreadLocal<String>();
    
        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 code
    Service层调用Dao层添加Order
    Dao层添加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");
        }
    }
    
    

    代理模式就这些内容啦。

    相关文章

      网友评论

          本文标题:代理模式

          本文链接:https://www.haomeiwen.com/subject/ybesdqtx.html