运行流程:
/**
* 1、获取sqlSessionFactory对象:
* 解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
* 注意:【MappedStatement】:代表一个增删改查的详细信息
*
* 2、获取sqlSession对象
* 返回一个DefaultSQlSession对象,包含Executor和Configuration;
* 这一步会创建Executor对象;
*
* 3、获取接口的代理对象(MapperProxy)
* getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
* 代理对象里面包含了,DefaultSqlSession(Executor)
* 4、执行增删改查方法
*
* 总结:
* 1、根据配置文件(全局,sql映射)初始化出Configuration对象
* 2、创建一个DefaultSqlSession对象,
* 他里面包含Configuration以及
* Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
* 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
* 4、MapperProxy里面有(DefaultSqlSession);
* 5、执行增删改查方法:
* 1)、调用DefaultSqlSession的增删改查(Executor);
* 2)、会创建一个StatementHandler对象。
* (同时也会创建出ParameterHandler和ResultSetHandler)
* 3)、调用StatementHandler预编译参数以及设置参数值;
* 使用ParameterHandler来给sql设置参数
* 4)、调用StatementHandler的增删改查方法;
* 5)、ResultSetHandler封装结果
* 注意:
* 四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
*/
首先要在mybatis全局配置文件中配置:
<!--plugins:注册插件 -->
<plugins>
<plugin interceptor="com.gong.mybatis.dao.MyFirstPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
</plugins>
property为自定义的属性名和值。
MyFirstPlugin.java
package com.gong.mybatis.dao;
import java.util.Properties;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
//完成插件签名,用于拦截哪个对象的哪个方法
@Intercepts({
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {
/**
* intercept:拦截
* */
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("myfirstplugin...intercept:"+invocation.getMethod());
//执行目标方法
Object proceed = invocation.proceed();
//返回执行后的返回值
return proceed;
}
//包装目标对象,为目标对象创建一个代理对象
@Override
public Object plugin(Object target) {
System.out.println("-->myfirstplugin...plugin,将要包装的对象:"+target);
// TODO Auto-generated method stub
Object wrap = Plugin.wrap(target, this);
//返回为当前target创建的动态代理
return wrap;
}
//将插件注册时的property属性设置进来
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
System.out.println("插件配置的信息:"+properties);
}
}
我们进行测试:随便运行一个要测试的方法
package com.gong.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.gong.mybatis.bean.Department;
import com.gong.mybatis.bean.Employee;
import com.gong.mybatis.dao.EmployeeMapper;
import com.gong.mybatis.mapper.EmployeeMapperDynamicSql;
public class TestMybatis5 {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(is);
}
@Test
public void test() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee em = mapper.getEmpById(1);
System.out.println(em);
} finally {
// TODO: handle finally clause
openSession.close();
}
}
}
输出:
插件配置的信息:{password=123456, username=root}
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.CachingExecutor@23faf8f2
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@1563da5
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@34c4973
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@7a765367
DEBUG 01-23 12:59:28,739 ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? (BaseJdbcLogger.java:145)
myfirstplugin...intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
DEBUG 01-23 12:59:28,823 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145)
DEBUG 01-23 12:59:28,850 <== Total: 1 (BaseJdbcLogger.java:145)
Employee [id=1, lastName=dema, gender=1, email=dema@qq.com, dept=null]
说明:执行sql方法时会调用四大对象,如果不是自己配置拦截的类型,就放过,否则就进行拦截。我们定义的插件是拦截StatementHandler类中的parameterize方法,其参数为java.sql.Statement。
当有多个插件时是怎么运行的?
<!--plugins:注册插件 -->
<plugins>
<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</plugin>
<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
</plugins>
MySecondPlugin.java
package com.gong.mybatis.dao;
import java.util.Properties;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
@Intercepts(
{
@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// TODO Auto-generated method stub
System.out.println("MySecondPlugin...plugin,要包装的对象:"+target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
结果:
插件配置的信息:{password=123456, username=root}
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.CachingExecutor@23faf8f2
MySecondPlugin...plugin,要包装的对象:org.apache.ibatis.executor.CachingExecutor@23faf8f2
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@1563da5
MySecondPlugin...plugin,要包装的对象:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@1563da5
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@34c4973
MySecondPlugin...plugin,要包装的对象:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@34c4973
-->myfirstplugin...plugin,将要包装的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@7a765367
MySecondPlugin...plugin,要包装的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@7a765367
DEBUG 01-23 13:21:17,640 ==> Preparing: select id,last_name lastName,email,gender from tbl_employee where id = ? (BaseJdbcLogger.java:145)
MySecondPlugin...intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
myfirstplugin...intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
DEBUG 01-23 13:21:17,698 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145)
DEBUG 01-23 13:21:17,731 <== Total: 1 (BaseJdbcLogger.java:145)
Employee [id=1, lastName=dema, gender=1, email=dema@qq.com, dept=null]
需要注意一点:插件会按配置的顺序依次拦截,但在执行时会先执行后配置的,因为相当于为第一个代理对象再进行代理。