聊聊MyBatis的日志模块之动态代理模式

举报
周杰伦本人 发表于 2022/08/30 09:57:17 2022/08/30
【摘要】 聊聊MyBatis的日志模块之动态代理模式 jdbc日志基类BaseJdbcLogger 连接日志类ConnectionLogger 生成代理类 创建和加载代理类 生成代理类字节码 总结 聊聊MyBatis的日志模块之动态代理模式mybatis的日志模块下有个jdbc文件夹,这里面主要实现功能是日志输出到文件中,生产中不建议大量日志的输出,因为影响性能。这里用到了JDK的动态代理 jdbc...

聊聊MyBatis的日志模块之动态代理模式

mybatis的日志模块下有个jdbc文件夹,这里面主要实现功能是日志输出到文件中,生产中不建议大量日志的输出,因为影响性能。这里用到了JDK的动态代理

jdbc日志基类BaseJdbcLogger

BaseJdbcLogger顾名思义是一个基类,文件夹中的其他类都是继承这个抽象类,同时实现了InvocationHandler接口进行动态代理,重写invoke方法:

 public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
        ... // 在执行业务逻辑之前的预处理逻辑
        Object result = method.invoke(target, args);
        ... // 在执行业务逻辑之后的后置处理逻辑
        return result;
    }

一般使用代理模式,可以在执行方法前后添加自己的逻辑,而代理类的生成是调用Proxy.newProxyInstance()方法,

这个方法需要三个参数:加载代理类的类加载器,业务类实现的接口和自定义的InvocationHandler的实现类

连接日志类ConnectionLogger

BaseJdbcLogger的实现类ConnectionLogger,它的invoke()方法:

    @Override
    public Object invoke(Object proxy, Method method, Object[] params)
            throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                // 如果调用的是从Object继承的方法,则直接调用,不做任何拦截
                return method.invoke(this, params);
            }
            if ("prepareStatement".equals(method.getName())
                    || "prepareCall".equals(method.getName())) {
                if (isDebugEnabled()) {
                    debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
                }
                PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
                stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
                return stmt;
            } else if ("createStatement".equals(method.getName())) {
                Statement stmt = (Statement) method.invoke(connection, params);
                stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
                return stmt;
            } else {
                return method.invoke(connection, params);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
    }

在这个方法中主要是为prepareStatement、createStatement、prepareCall方法添加业务逻辑,生成代理类的方法:

public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
        InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
        ClassLoader cl = Connection.class.getClassLoader();
        return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
    }

深入Proxy.newProxyInstance可以看到方法中进行一些权限检查后调用getProxyClass0()方法生成代理类

生成代理类

getProxyClass0()方法:

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

这个方法中会从proxyClassCache缓存中获取数据,这个缓存是通过WeakCache的构造方法生成的,构造方法中调用了ProxyClassFactory.apply()创建和加载代理类:

创建和加载代理类

ProxyClassFactory.apply()方法:

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            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());
            }
        }
  1. 对interfaces进行检测
  2. 定义代理类的包名
  3. 定义代理类的名称:包名+代理类名称前缀+数字
  4. 调用ProxyGenerator.generateProxyClass()方法生成代理类的字节码
  5. 调用defineClass0()方法加载代理类,返回Class对象

生成代理类字节码

ProxyGenerator.generateProxyClass()方法:

public static byte[] generateProxyClass(final String name,
       Class[] interfaces) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    final byte[] classFile = gen.generateClassFile();
    if (saveGeneratedFiles) { 
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction() {
                public Void run() {
                    // 省略try/catch代码块
                    FileOutputStream file = new FileOutputStream(
                        dotToSlash(name) + ".class");
                    file.write(classFile);
                    file.close();
                    return null;
                }
            }
        );
    }
    return classFile; // 返回上面生成的代理类的字节码
}
  1. 动态生成代理类的字节码
  2. 如果saveGeneratedFiles为true,保存字节码到文件中
  3. 返回生成的字节码

这是对ConnectionLogger类的分析,其他类也一样

总结

本文分析了一下ConnectionLogger类动态代理的实现,动态代理和静态代理的区别就是静态代理针对每个接口的具体实现类都要创建相应的代理类,如果实现类太多,代理类就会增多,而动态代理是通过实现类实现InvocationHandler接口,代理类的创建通过调用Proxy.newProxyInstance()创建,调用代理对象的时候就执行invoke()方法,因此代理逻辑重写invoke()就可以。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。