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}