1、java常用的日志框架log4j、log4j2、apache commons log、java.util.logging、slf4j,这些接口都不是很统一,所以mybatis定义了一套统一的日志接口供上层使用,为常用的日志框架提供适配器
2、设计模式的六大原则
1)单一职责原则
2)里式替换原则
3)依赖倒置原则
4)接口隔离原则
5)迪米特法则
6)开放封闭原则
3、mybatis日志模块,使用了适配器模式,提供了多个adapter,将这些第三方组件对外的接口适配成了org.apache.ibatis.logging.Log
4、代理模式的作用
可以控制真实类的访问,可以在调用前后进行预处理和后置处理
实现调用类和真实类的解耦
5、mybatis延迟加载的机制
系统访问数据库时,首先可以得到一个代理对象,此时没有任何真实的数据库操作,当真的需要数据时,通过代理对象完成数据的查询
6、动态代理
package org.apache.ibatis; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class TestInvocationHandler implements InvocationHandler { private Object target; public TestInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { System.out.println("预处理"); Object result=method.invoke(target,args); System.out.println("后置处理"); return result; } public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this); } public static void main(String[] args) { Demo b=new DemoImpl(); TestInvocationHandler invocationHandler=new TestInvocationHandler(b); Demo proxy= (Demo) invocationHandler.getProxy(); proxy.hello(); }; }
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ //获取代理类 Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取代理类的构造方法 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //创建代理类 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
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 // 如果指定类加载器中已经创建了实现指定接口的代理类,则查找缓存,否则通过ProxyClassFactory创建接口的代理类 return proxyClassCache.get(loader, interfaces); }
JDK动态代理的实现原理是动态创建代理类并通过指定类加载器加载,然后在创建代理对象时将InvokerHandler对象作为构造参数传入。当调用代理对象时,会调用InvokerHandler.invoke()方法,并最终调用真正业务对象的相应方法。
7、在MyBatis的日志模块中有一个Jdbc包,它并不是将日志信息通过JDBC保存到数据库中,而是通过JDK动态代理的方式,将JDBC操作通过指定的日志框架打印出来。这个功能通常在开发阶段使用,它可以输出SQL语句、用户传入的绑定参数、SQL语句影响行数等等信息,对调试程序来说是非常重要的。