一.加载配置文件;
说明:
1.将xml配置文件转成输入流对象;
2.将输入流对象转成Doucment(其实本质也是Node)文档对象;
3.从文档对象中拿到configuration(mybatis所有的配置对象) 对象Node节点对象;
4.从configuration节点对象中解析plugins子Node节点配置;
1.将XML文件转成输入流对象
启动类
String resource = "org/apache/ibatis/Amy/mybatis-config.xml";//"mybatis-config.xml"
InputStream inputStream = Resources.getResourceAsStream(resource);
最终调用 jdk中的Url类,读取路径转成输入流对象;
Url类
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
2.将输入流对象转成Doucment文档(节点)对象
SqlSessionFactoryBuilder 根据输入流对象转成Doucment对象;
SqlSessionFactoryBuilder类
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//根据输入流对象去创建
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//通过 Configuration 对象 解析获取 SqlSessionFactory sql回话工厂类;
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
在new XMLConfigBuilder()构造方法中创建Doucment对象;
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
//初始化本类的 ,因为这里的参数在构造方法的时候可以手动设置
//private boolean validation;
// private EntityResolver entityResolver;
// private Properties variables;
//private XPath xpath; 这个几个字段
commonConstructor(validation, variables, entityResolver);
//new InputSource(inputStream) 代码将输入流类型变成字节流类型
//重点是createDocument 该方法拿到 Document 给本类赋值Document字段赋值
this.document = createDocument(new InputSource(inputStream));
}
3.从文档对象中拿到configuration(mybatis所有的配置对象) 对象Node节点对象;
SqlSessionFactory 类
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//根据输入流对象去创建
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//通过 Configuration 对象 解析获取 SqlSessionFactory sql回话工厂类;
//parser.parse() 就是去解析Doucment对象;
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
XMLConfigBuilder 解析
public Configuration parse() {
//针对 mybatis整体 的配置文件mybatis-config 只能被加载解析一次,加载多次就会抛出异常;
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 特别说明:1. XNode 节点对象是mybatis 自定义的对象, XNode对象中包含了 Node 节点对象 ,也包含了 XPathParser 对象
//1. Document 也是Node节点对象的一个子类,
//从之前存储xml对应的字节流转换成了 XPathParser 对象中 Document 字段中
// 调用XPathParser类(包好了Document对象,在之前对xml的解析中转成了Document对象)evalNode根据 configuration 字符串获取到
// Document 对象中存储的 configuration 节点锁包含的所有数据, 说明configuration 节点是所有配置文件总的父节点,所以解析
// configuration 就是解析整个配置文件/解析全局的配置文件;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
通过核心配置文件对应的Doucment对象,以及根据Document下面一级节点的名称,获取一级节点对象;这个一级节点对象被封装在XNode对象中;
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
parseConfiguration方法解析 configuration 节点下面所有的配置信息(包括插件配置);
private void parseConfiguration(XNode root) {
try {
//解析config.xml配置文件中configuration最大父节点中的configuration节点数据
propertiesElement(root.evalNode("properties"));
//解析和configuration节点平级的节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载VFS对象的数据
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
//这里就在解析插件节点对象plugins;
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
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);
}
}
4.从configuration节点对象中解析plugins子Node节点配置;
pluginElement(root.evalNode(“plugins”)); 拿到插件节点对象,并且解析插件节点的信息;
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//因为可以配置多个plugin ,所以这里是可以循环,来解析每一个plugin 对象;
for (XNode child : parent.getChildren()) {
//获取 <plugin interceptor="org.apache.ibatis.Amy.plugin.ConfigPlugin"> 标签中的 interceptor 属性的值,也就是获取
//插件的 相对路径
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
// 通过拿到类的的路径,通过反射获取到 Interceptor 接口的实例,也就是 这里说的 plugin 对象;
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
//这里给Interceptor 对象中的 Properties 字段做赋值,也就是拿到 xml配置文件中的
//eg <property name="someProperty" value="100"/> 配置信息,给property 字段赋值;
interceptorInstance.setProperties(properties);
//将所有的插件对象都存入 InterceptorChain对象中的list集合中;
// TODO 为啥不放在 Map集合中;
configuration.addInterceptor(interceptorInstance);
}
}
}
此方法执行完,插件相关的信息(其实主要是插件类对象信息,已经插件类对象中封装的参数信息;这里并没有加载插件类对象上的注解相关的信息)
二.获取到相应的代理对象(Executor,ParameterHandler,ResultSetHandler,StatementHandler)
以Executor对象的"query"方法做为例子,获取代理对象;
1.通过sqlSessionFactory对象获取 Sqlsession对象;
SqlSession session = sqlSessionFactory.openSession();
2.通过DefaultSqlSessionFactory初始化DefaultSqlSession对象的时候
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
//TransactionFactory 对象是 Environment对象
//通过 Environment 环境对象获取 TransactionFactory 获取事物工厂类
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//建造者模式,通过 TransactionFactory 事物工厂来创建事物对象
//通过 事物工厂类 以及 环境对象 再去创建 事物对象,
// 这里的是否开启事物是由上层调用的方法来决定的;
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//在这一步获取 执行器对象,获取获取执行器对象的代理对象;
final Executor executor =
configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) { // 132 4782 3106
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
3.初始化执行器对象(获取该对象的代理对象)(因为化执行器是SqlSession对象的一个构造参数)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//1.关于这段代码为啥要执行,只能说是上一步的代码需要构建 SQL执行之前的DefaultSqlSession 对象的,
// 这段的执行就是为了构建 DefaultSqlSession 对象中的 Executor对象,构建DefaultSqlSession 的时候不用太关心Executor 是代理对象,
//还是原生对象;
//2.当还没有走过下面这段代码的时候,executor 一定是一个原生对象,当走过下段代码的时候executor对象就有可能是代理对象;
//是否是代理对象,取决于是否自定义了插件对象;
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
4.判断执行器对象是否需要做代理;
//遍历所有的插件对象,可能多个插件会针对同一个对象的同一个方法都做了操作
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
//到了具体的plugin插件对象中,做代理的植入操作
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
5.通过JDK代理拿到执行对象的代理对象;
public static Object wrap(Object target, Interceptor interceptor) {
//获取一个自定义插件对象下,@Signature数组中的数据
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
//根据目标对象,拿signatureMap对象,判断signatureMap的key是否包含了目标对象实现的接口对象;
//并返回,signatureMap中包含的目标对象的接口对象;
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//大于0就说明需要被代理;
if (interfaces.length > 0) {
//下面就是走的JDK代理;
return Proxy.newProxyInstance(
//获取被代理对象的类加载器
type.getClassLoader(),
//传入被代理对象的接口
interfaces,
//Plugin 是InvocationHandler 的一个实例,代理执行任何操作的时候都会去执行InvocationHandler 类的 handler
new Plugin(target, interceptor, signatureMap));
}
//将 @Intercepts({@Signature(type= Executor.class, 注解中其实也就是被切入对象 Executor的代理对象返回
return target;
}
6.执行器的代理对象执行Query方法;
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
logger.error(ms.toString());
// 如果有自定义插件 刚好是对executor对象做了处理,那么这边的query 方法的调用,可能就是到代理对象Plugin对象中的方法了。
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
//有插件针对executor 的时候executor.对象是代理对象,和没有插件针对executor 的时候,这里就是executor原对象
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
7.InvocationHandler类的实例会对Executor执行器对象任何方法的执行都用 invoke 方法 做拦截;
拦截到了信息都有:代理类对象,拦截时候代理类对象执行的方法;以及被拦截的方法的参数列表
/**
*
只要 代理对象执行了任意一个方法,那么就都会走到这里来;然后再根据 InvocationHandler 对象中保存的数据 signatureMap中的需要具体做处理的方法名称;看传递过 来的方法是否在这个集合中,如果在那么就做去调用
插件的intercept方法;如果不在那么就走方法的正常流程;
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//拿到被拦截方法所在类(也就是被代理的类)的字节码对象,
//被代理的类的字节码对象从signatureMap获取到具体由哪些方法是需要做处理;
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//判断被拦截的方法是否在这个需要被处理的方法集合中。
//如果是的:1.就去执行,这个插件对应中的 intercept方法;2.把拦截到的方法执行的参数列表也传递过去(可以有更多的
// 功能操作);
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
//如果不在,那么这里就直接去执行被拦截的类的具体的方法;
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
8.执行插件的中的方法;
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.error("在具体方法之前执行");
Object returnObject = invocation.proceed();
log.error("在具体方法之后执行");
return returnObject;
}
9.插件中的方法调用查询方法(在方法之前需要被处理的方法);
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
10.代理对象执行query方法做查询操作(query方法就是原对象中的方法);
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//以 select * from user where id = ? 为例子
//参数是{"parameterMappings":[{"javaType":"java.lang.Long","mode":"IN","property":"id","typeHandler":{"rawType":"java.lang.Long"}}],
//"parameterObject":1,"sql":"select * from user where id = ?"}
//BoundSql存储的是每个参数的隐射关系,已经这个SQL 解析之后的长字符串
logger.error("BoundSql对象中的数据是:"+MethodTest.getSting(boundSql));
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
11.方法后置逻辑处理
执行query操作是在插件的intercept方法中,所以query方法返回的时候肯定还要再回到插件的 intercept 方法中,然后再去执行后置方法;