以下内容的公开课视频已经录制,需要的同学可留言
MyBatis
这个框架具有强大的灵活性,MyBatis
对持久层的操作就是借助于四大组件(Executor
、StatementHandler
、ParameterHandler
、ResultSetHandler
),在四大组件处提供了简单易用的插件扩展机制。
MyBatis
支持用插件对四大核心组件进行拦截,对MyBatis
来说插件就是拦截器,用来增强核心组件的功能,增强功能本质上是借助于底层的动态代理实现的,换句话说,MyBatis
中的四大对象都是代理对象。
四大核心组件简介
MyBatis
四大核心组件:
-ParameterHandler
:处理SQL的参数的类
-ResultSetHandler
:处理SQL的返回结果集的类
-StatementHandler
:处理数据库SQL语句的类
-Executor
:用于执行增删改查操作的执行器类
MyBatis
插件原理
1.MyBatis
的插件借助于责任链的模式进行对拦截的处理
2.使用动态代理
对目标对象进行包装,达到拦截的目的
拦截
插件具体是如何拦截并附加额外的功能的呢?以ParameterHandler
来说
publicParameterHandlernewParameterHandler(MappedStatementmappedStatement,Objectobject,BoundSqlsql,InterceptorChaininterceptorChain){
ParameterHandlerparameterHandler=mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
parameterHandler=(ParameterHandler)interceptorChain.pluginAll(parameterHandler);
returnparameterHandler;
}
publicObjectpluginAll(Objecttarget){
for(Interceptorinterceptor:interceptors){
target=interceptor.plugin(target);
}
returntarget;
}
interceptorChain
保存了所有的拦截器(interceptors),是MyBatis
初始化的时候创建的。
调用拦截器链中的拦截器依次的对目标进行拦截或增强。
interceptor.plugin(target)中的target就可以理解为MyBatis
中的四大组件,返回的target是被重重代理后的对象。
插件接口
MyBatis
插件接口-Interceptor
1.intercept()
方法,插件的核心方法
2.plugin()
方法,生成target的代理对象
3.setProperties()
方法,传递插件所需参数
插件实例
插件开发需要以下步骤
1.自定义插件需要实现上述接口
2.增加@Intercepts注解(声明是哪个核心组件的插件,以及对哪些方法进行扩展)
3.在xml文件中配置插件
**
*插件签名,告诉MyBatis插件用来拦截那个对象的哪个方法
**/
@Intercepts(
{
@Signature(
type=StatementHandler.class,
method="parameterize",
args=Statement.class
)
}
)
publicclassMyInterceptsimplementsInterceptor{
/**
*拦截目标对象的目标方法
*
*@paraminvocation
*@return
*@throwsThrowable
*/
@Override
publicObjectintercept(Invocationinvocation)throwsThrowable{
System.out.println("进入自定义的拦截器,拦截目标对象"+invocation+invocation.getMethod()+invocation.getTarget());
returninvocation.proceed();
}
/**
*包装目标对象为目标对象创建代理对象
*
*@Paramtarget为要拦截的对象
*@Return代理对象
*/
@Override
publicObjectplugin(Objecttarget){
System.out.println("自定义plugin方法,将要包装的目标对象"+target.toString()+target.getClass());
returnPlugin.wrap(target,this);
}
/**
*获取配置文件的属性
*
*@paramproperties
*/
@Override
publicvoidsetProperties(Propertiesproperties){
System.out.println("自定义插件的初始化参数"+properties);
}
}
在mybatis-config.xml中配置插件
<!--自定义插件-->
<plugins>
<plugininterceptor="com.boxuegu.javaee.mybatissourcelearn.MyIntercepts">
<propertyname="test"value="testvalue"/>
</plugin>
</plugins>
调用查询方法,查询方法会返回ResultSet
publicclassTest{
publicstaticvoidmain(String[]args){
//1.加载配置文件
Stringresource="mybatis-config.xml";
InputStreaminputStream=null;
try{
inputStream=Resources.getResourceAsStream(resource);
}catch(IOExceptione){
e.printStackTrace();
}
//2.获取sqlSessionFactory
SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(inputStream);
//3.获取sqlSession
SqlSessionsqlSession=sqlSessionFactory.openSession();
try{
//通过xml文件直接执行sql语句
//Employeeemployee=sqlSession.selectOne("com.boxuegu.javaee.mybatissourcelearn.dao.EmployeeMapper.getEmployeeById",1);
//alt+shift+Lintroducelocalvariables;
Threadthread=newThread(()->System.out.println("test"));
//4.获取mapper接口实现
EmployeeMappermapper=sqlSession.getMapper(EmployeeMapper.class);
System.out.println("mapper::::"+mapper.getClass());
//5.执行sql语句
Employeeemployee=mapper.getEmployeeById(1);
System.out.println(employee);
}finally{
sqlSession.close();
}
}
}
输出结果
自定义插件的初始化参数{test=testvalue}
自定义plugin方法,将要包装的目标对象org.apache.ibatis.executor.CachingExecutor@46f5f779classorg.apache.ibatis.executor.CachingExecutor
mapper::::classcom.sun.proxy.$Proxy3
自定义plugin方法,将要包装的目标对象org.apache.ibatis.scripting.defaults.DefaultParameterHandler@1bc6a36eclassorg.apache.ibatis.scripting.defaults.DefaultParameterHandler
自定义plugin方法,将要包装的目标对象org.apache.ibatis.executor.resultset.DefaultResultSetHandler@387c703bclassorg.apache.ibatis.executor.resultset.DefaultResultSetHandler
自定义plugin方法,将要包装的目标对象org.apache.ibatis.executor.statement.RoutingStatementHandler@c39f790classorg.apache.ibatis.executor.statement.RoutingStatementHandler
WedJun1918:14:24CST2019WARN:EstablishingSSLconnectionwithoutserver'sidentityverificationisnotrecommended.AccordingtoMySQL5.5.45+,5.6.26+and5.7.6+requirementsSSLconnectionmustbeestablishedbydefaultifexplicitoptionisn'tset.ForcompliancewithexistingapplicationsnotusingSSLtheverifyServerCertificatepropertyissetto'false'.YouneedeithertoexplicitlydisableSSLbysettinguseSSL=false,orsetuseSSL=trueandprovidetruststoreforservercertificateverification.
进入自定义的拦截器,拦截目标对象org.apache.ibatis.plugin.Invocation@50f8360dpublicabstractvoidorg.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement)throwsjava.sql.SQLExceptionorg.apache.ibatis.executor.statement.RoutingStatementHandler@c39f790
Employee{id=1,lastName='zhangsan',email='zhangsan@itcast.cn',gender='1'}
多插件开发
1.创建代理对象时,按照插件配置的顺序进行包装
2.执行目标方法后,是按照代理的逆向进行执行
小结
1.遵循插件尽量不使用的原则,因为会修改底层设计
2.插件是生成的层层代理对象的责任链模式,使用反射机制实现
3.插件的编写要考虑全面,特别是多个插件层层代理的时候
网友评论