当前位置 : 主页 > 编程语言 > java >

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

来源:互联网 收集:自由互联 发布时间:2022-08-10
@[TOC] 聊聊MyBatis的日志模块之动态代理模式 mybatis的日志模块下有个jdbc文件夹,这里面主要实现功能是日志输出到文件中,生产中不建议大量日志的输出,因为影响性能。这里用到了J

@[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<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()); } }
  • 对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()就可以。

    上一篇:一文带你了解 Java 中的构造器
    下一篇:没有了
    网友评论