app开发者平台在数字化时代的重要性与发展趋势解析
592
2022-10-22
聊聊MyBatis的日志模块之动态代理模式
@[TOC]
聊聊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
这个方法中会从proxyClassCache缓存中获取数据,这个缓存是通过WeakCache的构造方法生成的,构造方法中调用了ProxyClassFactory.apply()创建和加载代理类:
创建和加载代理类
ProxyClassFactory.apply()方法:
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map
对interfaces进行检测 定义代理类的包名 定义代理类的名称:包名+代理类名称前缀+数字 调用ProxyGenerator.generateProxyClass()方法生成代理类的字节码 调用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; // 返回上面生成的代理类的字节码 }
动态生成代理类的字节码 如果saveGeneratedFiles为true,保存字节码到文件中 返回生成的字节码
这是对ConnectionLogger类的分析,其他类也一样
总结
本文分析了一下ConnectionLogger类动态代理的实现,动态代理和静态代理的区别就是静态代理针对每个接口的具体实现类都要创建相应的代理类,如果实现类太多,代理类就会增多,而动态代理是通过实现类实现InvocationHandler接口,代理类的创建通过调用Proxy.newProxyInstance()创建,调用代理对象的时候就执行invoke()方法,因此代理逻辑重写invoke()就可以。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~