美文网首页
MyBatis插件机制分析

MyBatis插件机制分析

作者: 山东大葱哥 | 来源:发表于2020-01-28 11:15 被阅读0次

    以下内容的公开课视频已经录制,需要的同学可留言

    MyBatis这个框架具有强大的灵活性,MyBatis对持久层的操作就是借助于四大组件(ExecutorStatementHandlerParameterHandlerResultSetHandler),在四大组件处提供了简单易用的插件扩展机制。

    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.插件的编写要考虑全面,特别是多个插件层层代理的时候

    相关文章

      网友评论

          本文标题:MyBatis插件机制分析

          本文链接:https://www.haomeiwen.com/subject/ffipthtx.html