进击的小羊

  1. 首页
  2. 后端
  3. Java
  4. 正文

设计模式-代理模式

2021年8月25日 461点热度 0人点赞 0条评论

---

title: 设计模式-代理模式
top: false
cover: false
toc: true
mathjax: true
date: 2018-10-27 02:01:03
password:
summary:
tags: "设计模式"
categories: "java"

Java代理

什么是代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问,属于结构型模式。
代理模式

目的

  1. 保护目标对象
  2. 增强目标对象

使用场景

  1. 要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  2. 针对历史代码中的内容需要进行改造,在不侵入历史代码时,对方法进行拓展,完成新功能的拓展。

Java的代理实现

场景描述

在原始的业务中,通过service调用dao层的方法写入到数据库中,目前为了进行业务拓展需要针对订单ID进行数据源的动态切换,在实现dao层时,需要根据主键进行分库的操作,不同的订单ID要分配到不同的数据库中。

代码实现

  1. OrderServiceImpl代码调用dao层代码
  2. OrderDao定义dao层接口,OrderDaoImpl实现接口进行数据写入
//service 代码
public class OrderServiceImpl {
   private OrderDao orderDao;

   public OrderServiceImpl(OrderDao orderDao) {
       this.orderDao = orderDao;
   }

   public int createOrder(String orderId) {
       return orderDao.insertOrder(orderId);
   }

}

//dao接口
public interface OrderDao {
   /**
    * 创建的订单
    *
    * @param orderId 订单ID
    * @return 插入的数据条数
    */
   int insertOrder(String orderId);
}

//dao实现
public class OrderDaoImpl implements OrderDao {

   public int insertOrder(String orderId) {
       System.out.println("插入订单" + orderId);
       return 1;
   }
}

静态代理

仅能指定需要代理的对象,且无法进行其他功能的拓展,当被代理对象的定义进行修改后,代理类需要同步修改,不符合开闭原则。

public class OrderDaoStaticProxy implements OrderDao {
    private OrderDaoImpl orderDaoImpl;

    public OrderDaoStaticProxy(OrderDaoImpl orderDaoImpl) {
        this.orderDaoImpl = orderDaoImpl;
    }

    @Override
    public int insertOrder(String orderId) {
        changeDataSource(orderId);
        //原始逻辑
        Object obj = orderDaoImpl.insertOrder(orderId);
        //数据源恢复默认
        restoreDataSource();
        return (int) obj;
    }

    /**
     * 根据订单ID修改数据源信息
     *
     * @param orderId
     */
    private void changeDataSource(String orderId) {
        System.out.println("当前数据源切换到 db_" + (Integer.parseInt(orderId) % 4 + 1));
    }

    /**
     * 恢复默认数据源
     */
    private void restoreDataSource() {
        System.out.println("恢复数据源db_0");
    }
}
//测试代码
public class TestStaticProxy {
    public static void main(String[] args) {
        OrderDao orderDao = new OrderDaoStaticProxy(new OrderDaoImpl());
        orderDao.insertOrder("7");
    }
}

截屏2021-09-07 01.06.05

静态代理定义后只能代理指定的对象OrderDao,当OrderDao接口中定义的内容更新后,需要同步更新代理类中的调用。

动态代理

要求:被代理类必须实现接口

  1. 定义代理类,并且实现InvocationHandler接口
  2. 实现invoke方法
//代理类
public class OrderDaoDynamicProxy implements InvocationHandler {
    private OrderDaoImpl orderDaoImpl;

    public Object getInstance(OrderDaoImpl orderDaoImpl) {
        //注入
        this.orderDaoImpl = orderDaoImpl;
        Class<? extends OrderDao> clazz = orderDaoImpl.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);

    }

    /**
     * 实现invoke方法
     *
     * @param proxy  代理对象
     * @param method 方法
     * @param args   参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //数据源切换
        String orderId = (String) args[0];

        changeDataSource(orderId);
        //原始逻辑
        Object obj = method.invoke(this.orderDaoImpl, args);
        //数据源恢复默认
        restoreDataSource();
        return obj;
    }

    /**
     * 根据订单ID修改数据源信息
     *
     * @param orderId
     */
    private void changeDataSource(String orderId) {
        System.out.println("当前数据源切换到 db_" + (Integer.parseInt(orderId) % 4 + 1));
    }

    /**
     * 恢复默认数据源
     */
    private void restoreDataSource() {
        System.out.println("恢复数据源db_0");
    }
}
//测试代码
public class TestDynamicProxy {
    public static void main(String[] args) {
        OrderDao orderDao = (OrderDao) new OrderDaoDynamicProxy().getInstance(new OrderDaoImpl());
        orderDao.insertOrder("7");
    }
}

截屏2021-09-07 01.06.38

源码分析

  1. 调用newProxyInstance方法
  2. 调用getProxyClass0做接口个数的检查,如果定义过直接返回,不存在则继续
  3. 调用ProxyClassFactory类的apply方法
  4. 首先做接口列表的一系列检查,比如是否为接口、接口可见性
  5. ProxyGenerator.generateProxyClass方法开始生成代理类
  6. generateClassFile方法创建代理类的二进制文件
  7. 根据接口列表分别重写代理的方法,并将类内容写入到之前定义的以($Proxy+数字)的类中

源码如下:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
    ...
     /*
     * Look up or generate the designated proxy class.
     */
     Class<?> cl = getProxyClass0(loader, intfs);
     ...
}
/**
 * Generate a proxy class.  Must call the checkProxyAccess method
 * to perform permission checks before calling this.
 */
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
...
if (interfaces.length > 65535) { //JDK 限制接口数量,进行检查
    throw new IllegalArgumentException("interface limit exceeded");
}
//如果继承于相同接口的对应的loader已经在cache中存在了,那么直接返回,否则通过ProxyClassFactory进行代理类的创建
return proxyClassCache.get(loader, interfaces);
...
}
//根据ProxyClassFactory工厂构建class
 private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

//根据给到的classload和接口列表产出class类
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names,类的前缀都是'$Proxy'
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names,通过生成的唯一自增数字为类编号,'$Proxy0'、'$Proxy1'、'$Proxy2'
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            //将接口列表装入Set去重
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //遍历所有接口进行处理
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    //反射调用接口,获取接口对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //接口校验可见性权限
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                //接口校验
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                //校验接口是否唯一
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            //处理要写入的包路径
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            //生成代理类
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
//生成class文件的方法
private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        //遍历接口,获取接口对应的方法
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                //添加需要代理的方法
                this.addProxyMethod(var8, var4);
            }
        }

        //处理方法的返回值
        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        //处理方法定义的属性
        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            //类内容写出
            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

cglib代理

代码实现

public class CglibOrderDaoDynamicProxy implements MethodInterceptor {
    private OrderDaoImpl orderDaoImpl;

    public Object getInstance(OrderDaoImpl orderDaoImpl) {
        //注入
        this.orderDaoImpl = orderDaoImpl;
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);
        enhancer.setSuperclass(orderDaoImpl.getClass());

        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        changeDataSource((String) objects[0]);
        Object o1 = methodProxy.invoke(orderDaoImpl, objects);
        restoreDataSource();
        return o1;
    }

    /**
     * 根据订单ID修改数据源信息
     *
     * @param orderId
     */
    private void changeDataSource(String orderId) {
        System.out.println("当前数据源切换到 db_" + (Integer.parseInt(orderId) % 4 + 1));
    }

    /**
     * 恢复默认数据源
     */
    private void restoreDataSource() {
        System.out.println("恢复数据源db_0");
    }
}
//测试代码
public class TestCGLIBDynamicProxy {
    public static void main(String[] args) {
        OrderDao orderDao = (OrderDao) new CglibOrderDaoDynamicProxy().getInstance(new OrderDaoImpl());
        orderDao.insertOrder("7");
    }
}

截屏2021-09-08 01.04.01

实现原理

为了完整的理解cglib 如何工作,可以将产出的对象写到本地中

public class TestCGLIBDynamicProxy {
    public static void main(String[] args) {
        //写入到本地文件中
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/qizi/work/gupao/pattern");

        OrderDao orderDao = (OrderDao) new CglibOrderDaoDynamicProxy().getInstance(new OrderDaoImpl());
        orderDao.insertOrder("7");
    }
}

截屏2021-09-08 01.15.04

截屏2021-09-08 01.22.11

其中OrderDaoImpl&#124;EnhancerByCGLIB&#124;8a2e97f4为主要生成的代理的类,其他的两个为辅助类,net包下为缓存相关的类。

OrderDaoImpl&#124;EnhancerByCGLIB&#124;8a2e97f4类代码如下:

截屏2021-09-08 01.23.08

可以看到OrderDaoImpl&#124;EnhancerByCGLIB&#124;8a2e97f4是继承了需要代理的类对象的

    public final int insertOrder(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            //会调用定义的intercept方法
            Object var2 = var10000.intercept(this, CGLIB&#124;insertOrder&#124;0&#124;Method, new Object[]{var1}, CGLIB$insertOrder&#124;0&#124;Proxy);
            return var2 == null ? 0 : ((Number)var2).intValue();
        } else {
            return super.insertOrder(var1);
        }
    }

同时针对需要代理的方式,会进行重写,并且定义为final,表示已经代理过的方法无法再次被代理。

标签: 设计模式
最后更新:2022年3月22日

qizi

一生懸命,步履不停

点赞
< 上一篇
下一篇 >
分类
  • Java
  • 大数据
  • 数据库
  • 杂项
文章目录
  • ---
  • Java代理
    • 什么是代理模式
      • 目的
      • 使用场景
    • Java的代理实现
      • 场景描述
      • 静态代理
      • 动态代理
      • cglib代理

COPYRIGHT © 2022 进击的小羊. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang