1、mybatis初始化
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory
的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML
配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。(摘自www.mybatis.org)
mybatis的核心配置类就是Configuration,初始化时肯定是根据xml得到Configuration进而得到SqlSessionFactory。
现在我们使用spring+mybatis一般会配置两个bean
org.mybatis.spring.SqlSessionFactoryBean
org.mybatis.spring.mapper.MapperScannerConfigurer
MapperScannerConfigurer是帮我们生成dao的代理对象,并注入到spring容器中。
SqlSessionFactoryBean就是用来初始化mybatis的
SqlSessionFactoryBean实现了几个spring的接口
实现FactoryBean在getObject()返回了sqlSessionFactory
实现InitializingBean在afterPropertiesSet()调用了buildSqlSessionFactory()
XMLConfigBuilder传入了配置文件,那么按照道理它应该会初始化configuration,对应的方法就是xmlConfigBuilder.parse()
可以看到配置文件中那些properties,plugins,settings等都设置Configuration对应属性里面了。
buildSqlSessionFactory()最后就是return this.sqlSessionFactoryBuilder.build(configuration);还是使用的mybatis的SqlSessionFactoryBuilder
3、插件链的生成及执行
插件作用在四个对象上面Executor,StatementHandler,ResultSetHandler,ParameterHandler
操作sql需要使用SqlSession对象,实现类DefaultSqlSession里面的方法最终都调用了Executor方法
Configuration的四个方法
这样循环调用的结果就是生成了一个链,假如有两个会形成下面的
最后得到的代理对象,执行对应方法时候,如果注解配置满足,就会执行插件的intercept方法,
当然如果我们需要让插件继续往下执行,则必须return invocation.proceed()
插件的原理执行过程就是这样。
继续看Executor后续执行会发现,它又调用用了StatementHandler方法,而StatementHandler在处理参数时候
调用了ParameterHandler,查询结束返回时候调用了ResultSetHandler
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory
的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML
配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。(摘自www.mybatis.org)
mybatis的核心配置类就是Configuration,初始化时肯定是根据xml得到Configuration进而得到SqlSessionFactory。
现在我们使用spring+mybatis一般会配置两个bean
org.mybatis.spring.SqlSessionFactoryBean
org.mybatis.spring.mapper.MapperScannerConfigurer
MapperScannerConfigurer是帮我们生成dao的代理对象,并注入到spring容器中。
SqlSessionFactoryBean就是用来初始化mybatis的
SqlSessionFactoryBean实现了几个spring的接口
实现FactoryBean在getObject()返回了sqlSessionFactory
实现InitializingBean在afterPropertiesSet()调用了buildSqlSessionFactory()
buildSqlSessionFactory()方法
if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } ...... xmlConfigBuilder.parse();
XMLConfigBuilder传入了配置文件,那么按照道理它应该会初始化configuration,对应的方法就是xmlConfigBuilder.parse()
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
可以看到配置文件中那些properties,plugins,settings等都设置Configuration对应属性里面了。
buildSqlSessionFactory()最后就是return this.sqlSessionFactoryBuilder.build(configuration);还是使用的mybatis的SqlSessionFactoryBuilder
2、插件
插件需要实现的接口
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; //插件具体的处理方法,参数invocation是上一个代理对象的封装 Object plugin(Object target);//生成代理对象的方法,需要将插件,与目标对象target整合一起,一般使用Plugin.wrap生成 void setProperties(Properties properties);//给插件设置参数方法 } //工具方法Plugin public class Plugin implements InvocationHandler { private Object target; private Interceptor interceptor; private Map<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass());//取得此interceptor注解配置的需要拦截的方法 if (methods != null && methods.contains(method)) {//如果方法里面包含当前执行方法,执行插件的intercept方法 return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } //配置插件例 @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) type要拦截什么对象,Executor,StatementHandler,ResultSetHandler,ParameterHandler method要拦截的方法 args方法的参数
3、插件链的生成及执行
插件作用在四个对象上面Executor,StatementHandler,ResultSetHandler,ParameterHandler
操作sql需要使用SqlSession对象,实现类DefaultSqlSession里面的方法最终都调用了Executor方法
Configuration的四个方法
Executor newExecutor(...) StatementHandler newStatementHandler(...) ResultSetHandler newResultSetHandler(...) ParameterHandler newParameterHandler(...) //每个方法最后都调用了interceptorChain.pluginAll(..) public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }由于上面的初始化,如果我们配置了plugins,那么此时interceptors里面会有我们配置的插件对象
这样循环调用的结果就是生成了一个链,假如有两个会形成下面的
第2层 intercept(invocation) invocation指向第1层 第1层 intercept(invocation) invocation指向target 原始的target
最后得到的代理对象,执行对应方法时候,如果注解配置满足,就会执行插件的intercept方法,
当然如果我们需要让插件继续往下执行,则必须return invocation.proceed()
插件的原理执行过程就是这样。
继续看Executor后续执行会发现,它又调用用了StatementHandler方法,而StatementHandler在处理参数时候
调用了ParameterHandler,查询结束返回时候调用了ResultSetHandler
SqlSession->Executor->StatementHandler->{ParameterHandler,ResultSetHandler}