上一篇主要讲了properties、settings、typeAliases三个节点的解析,其实还是比较容易的
这一篇主要讲下这个几个 plugins、factory相关的、environments
话不多说,还是接着看源码
plugins首先还是先看下使用,具体可以参考官网地址
https://mybatis.org/mybatis-3/zh/configuration.html#plugins
1. 先定义实现了Interceptor接口的实现类
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
@Override
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
2. 在mybatis-config.xml文件中进行配置
其中properties 到时mybatis会拿到你的配置设置进去
这个主要是mybatis提供给我们进行方法拦截的功能,分页自动拦截就发生在这些插件中
接着看下源码的解析
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//循环插件列表
for (XNode child : parent.getChildren()) {
//获取拦截器全类名
String interceptor = child.getStringAttribute("interceptor");
//获取你配置的属性值,到时会调用Interceptor接口的setProperties传给你定义的拦截器
Properties properties = child.getChildrenAsProperties();
//解析拦截器,这里别名就发挥作用了,你可以定义一些简短的名字,在引用的时候就比较方便了
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
//添加到配置类中进行统一管理
configuration.addInterceptor(interceptorInstance);
}
}
}
仍然是一目了然,其实还是挺简单的,别名注册测就发挥了作用,可以看看resolveClass方法做了什么
protected Class> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
//从别名注册里面解析一个类出来
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
再到具体的解析方法
protected Class> resolveAlias(String alias) {
//其实就是调用前面的注册map进行获取了
return typeAliasRegistry.resolveAlias(alias);
}
再稍微看下configuration里面的Interceptor是如何管理的,后面会再说到这个东西,因为这个是sql运行过程中一个比较核心的拦截点
configuration使用了InterceptorChain对象同时使用了组合模式来统一管理所有的拦截器,如下
public class InterceptorChain {
private final List interceptors = new ArrayList();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
一般进行代理都是使用 Plugin.wrap(target, this);即可,这是mybatis提供的插件代理生成,可以大概看看里面做了什么
public static Object wrap(Object target, Interceptor interceptor) {
//获取前面接口
Map, Set> signatureMap = getSignatureMap(interceptor);
//获取代理类,通过责任链传输过来的,一层层进行包装
Class> type = target.getClass();
//获取所有的接口
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//使用jdk进行动态代理,InvocationHandler为Plugin当前这个类
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
比如上面的 ExamplePlugin 就是一个例子了,这个代理逻辑等后面sql执行的时候再具体分析,当然有兴趣的可以自行分析
factory相关的
这个我们一般不会用,所以只会讲两个比较简单的,如下
官网参考地址:https://mybatis.org/mybatis-3/zh/configuration.html#objectFactory
objectFactory:这个接口是mybatis用来创建对象时候使用的,他提供了一个默认实现DefaultObjectFactory,我们一般不会定义,当然了你也可以进行定义
1,先提供一个实现类
public class ExampleObjectFactory extends DefaultObjectFactory {
@Override
public T create(Class type) {
return super.create(type);
}
@Override
public T create(Class type, List> constructorArgTypes, List
environments
这是环境相关的一个类,主要就是数据源和事务工厂了,不得不说mybatis的抽象还是比我们普通人要好的
官网配置地址:https://mybatis.org/mybatis-3/zh/configuration.html#environments这里主要是讲几个比较核心的接口 DataSourceFactory:数据源工厂,用来获取数据源的,想必大家对这个还是比较熟悉的,没错,mybatis自己提供了数据库连接池的实现,接口就这样 public interface DataSourceFactory { void setProperties(Properties props); DataSource getDataSource(); } TransactionFactory:事务工厂,主要是用来获取Transaction事务对象的,看看接口的样子 public interface TransactionFactory { void setProperties(Properties props); Transaction newTransaction(Connection conn); Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); } Transaction:事务管理对象,用来处理一次事务过程中所需要的属性和对象已经操作的,看到接口的定义你就明白了 public interface Transaction { Connection getConnection() throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; Integer getTimeout() throws SQLException; } 这些方法是不是都很熟悉,就是在一次事务过程中会进行的一些提交.... 最后一个对象是 Environment : 也就是环境对象,对于mybatis来说,环境对象就是数据源和事务处理等,如下 public final class Environment { private final String id; private final TransactionFactory transactionFactory; private final DataSource dataSource; public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) { if (id == null) { throw new IllegalArgumentException("Parameter 'id' must not be null"); } if (transactionFactory == null) { throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null"); } this.id = id; if (dataSource == null) { throw new IllegalArgumentException("Parameter 'dataSource' must not be null"); } this.transactionFactory = transactionFactory; this.dataSource = dataSource; } 上面mybatis就用了很多设计模式:比如抽象工厂、建造者模式等 最后再简单看下源码解析,其实源码解析一直都不难 private void environmentsElement(XNode context) throws Exception { if (context != null) { //当前环境没配置的时候拿默认的 if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { //循环环境,每个环境都有一个id,必须保证不一样即可 String id = child.getStringAttribute("id"); //如果是不指定就是上面默认的 if (isSpecifiedEnvironment(id)) { //解析事务工厂 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //解析数据源工厂 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //从数据源工厂获取数据源 DataSource dataSource = dsFactory.getDataSource(); //使用建造者模式构建环境对象 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); //放到configuration进行管理 configuration.setEnvironment(environmentBuilder.build()); } } } } 下面的子标签就不看了,都是比较简单的,这里主要是学习隔离的设计,mybatis抽象出了数据源工厂,事务工厂,事务对象等等
好了这一篇就到这里,内容看起来挺多,但是源码看懂并不复杂,复杂的是对mybatis的抽象进行理解,程序的美妙之处是在设计上面,而不是单纯的实现
好的设计可以提供更多的扩展性给其他开发者,而且维护起来也会更方便,基本都是在跟接口打交道进行维护的
这才是我们学习的重点



