玖富娱乐平台全网唯一指定1956注册开户网站

互联网轻量级框架SSM-查缺补漏第八天(MyBatis插件

日期:2019-01-04 浏览:
玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。

  简言:本日举行第八天的纪录(只是写了八天)。有的时刻看的多,有的时刻看的少,看的少的时刻就攒几天一同写了。而本日这个插件我昨天写了一下昼,放工没写完就回去了,本日把尾收了,再加上一个历程图轻易下面道理的明白。我这个迥殊不爱看书的人都看半个多月了,愿望我一个月真能养成个戒不掉的习气~

第八章 插件

  在前一篇引见了四个对象,是SqlSession实行历程当中经由过程他们来完成数据库操作和效果返回的。(ExecutorStatementHandlerParameterHandlerResultSetHandler)。我昨天的查缺补漏有纪录,若是想深切相识就能够去查资料了,或许看我这本书的第七章(书名《JavaEE 互联网轻量级框架整合开辟》)

  插件的道理就是在四大对象调理时插进去我们的代码去实行一些特别的请求以知足特别的场景需求

  斜体字是局部道理。我先在这插进去一张流程图,若是有疑问能够看到末了再返返来揣摩一下这个图。

 

  运用要领:(举例:要想在预编译之前在掌握台上打印一些器械,就需要阻拦实行SQL的StatementHandler对象的预编译要领,也就是prepare要领)

  在MyBatis中运用插件,就必须完成interceptor接口,完成它的三个要领(代码中有解释,应该能晓得啥意义):

package com.ssm.chapter8.plugin;
import java.sql.Connection;
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;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.log4j.Logger;

@Intercepts({
    @Signature(
            type=StatementHandler.class,
            method="prepare",
            args = {Connection.class,Integer.class}
            )
})
public class MyPlugin implements Interceptor {
    private Logger log = Logger.getLogger(MyPlugin.class);
    private Properties props = null;
    /**
     * 插件要领,它替代StatementHandler的prepare要领
     * 
     * @param invocation 入参
     * @return 返回预编译后的preparedStatement
     * @throws Throwable 非常
     * */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        //举行绑定
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        Object object = null;
        /*星散署理对象链(因为目的类能够被多个阻拦器[插件]阻拦,从而构成屡次署理,经由过程轮回能够星散出最原始的目的类)*/
        while(metaStatementHandler.hasGetter("h")){
            object = metaStatementHandler.getValue("h");
            metaStatementHandler = SystemMetaObject.forObject(object);
        }
        statementHandler = (StatementHandler)object;
        String sql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
        Long parameterObject = (Long)metaStatementHandler.getValue("delegate.boundSql.parameterObject");
        
        log.info("实行的SQL:【" sql "】");
        log.info("参数:【" parameterObject "】");
        log.info("before......");
        //若是以后署理的事一个非署理对象,那末它就会回挪用实在阻拦对象的要领
        //若是不是,那末它就会调理下一个插件署理对象的invoke要领
        Object obj = invocation.proceed();
        log.info("after......");
        return obj;
    }
    /**
     * 天生署理对象
     * 
     * @param target 被阻拦对象
     * @return 署理对象
     * */
    public Object plugin(Object target) {
        // 接纳体系默许的Plugin.wrap要领天生
        return Plugin.wrap(target, this);
    }
    /**
     * 设置参数,MyBatis初始化时,就会天生插件实例,并挪用这个要领
     * 
     * @param props 设置装备摆设参数
     * */
    public void setProperties(Properties props) {
        this.props = props;
        log.info("dbType = " this.props.getProperty("dbType"));
        
    }
}

  标黄的三个就是要去完成的要领:

  • intercept:英译:阻拦。简单说就是阻拦署名(指类名上面的注解@Signature)中对应中对应要领。参数Invocation就是被阻拦内容的整合。
  • plugin:英译:插件(计算机名词)。target是被阻拦的对象
  • setProperties:设置参数。这个参数是要在XML设置装备摆设中设置装备摆设的。

  注解@Intercepts申明它是一个阻拦器。@Singnature是注册阻拦器署名的处所,只要署名知足请求能力阻拦,type能够是四大对象中的一个,这里是StatementHandler。method代表要阻拦四大对象的某一接口要领,而args则透露表现该要领的参数(要根据阻拦对象的要领举行阻拦。)下面贴一段StatementHandler中prepare要领的界说代码。

public abstract Statement prepare(Connection connection, Integer integer) throws SQLException;

  以是args中是一个Connection.class和 Integer.class。阻拦后,经由过程 Invocation对象能够反射调理本来对象的要领。贴一段 Invocation的源代码。

public class Invocation {

    public Invocation(Object target, Method method, Object args[]) {
        this.target = target;
        this.method = method;
        this.args = args;
    }
    public Object getTarget() {
        return target;
    }
    public Method getMethod() {
        return method;
    }
    public Object[] getArgs() {
        return args;
    }
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, args);
    }
    private final Object target;
    private final Method method;
    private final Object args[];
}

  一看就晓得 Target是被阻拦的目的对象,Method是被阻拦的要领,Args就是注解中的参数,这里的proceed要领是经由过程反射挪用原对象中的要领。

  设置装备摆设XML:注重MyBatis设置装备摆设XML中标签的递次

<plugins><!-- 插件 -->
    <plugin interceptor="com.ssm.chapter8.plugin.MyPlugin">
        <property name="dbType" value="mysql"/>
    </plugin>
</plugins>

  我运用的查询设置装备摆设:

<select id="getRole" parameterType="Long" resultType="role">
    select id,role_name as roleName,note from t_role where id = #{id}
</select>

  实行效果:

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。-
DEBUG - Reader entry: <?xml version="1.0" encoding="UTF-8"?>
DEBUG - Checking to see if class com.learn.ssm.chapter3.mapper.RoleMapper matches criteria [is assignable to Object]
DEBUG - Cache Hit Ratio [com.learn.ssm.chapter3.mapper.RoleMapper]: 0.0
DEBUG - Opening JDBC Connection
DEBUG - Created connection 407858146.
DEBUG - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@184f6be2]
 INFO - 实行的SQL:【select id,role_name as roleName,note from t_role where id = ?】
 INFO - 参数:【1】
 INFO - before......
DEBUG - ==>  Preparing: select id,role_name as roleName,note from t_role where id = ? 
 INFO - after......
DEBUG - ==> Parameters: 1(Long)
DEBUG - <==      Total: 1
DEBUG - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@184f6be2]
DEBUG - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@184f6be2]
DEBUG - Returned connection 407858146 to pool.

  标红为Intercept要领中log.info打印的器械。在before和after中央能够清楚看出,举行了预编译,也就是挪用了原有的prepare。

  总结

  梳理一下思绪,在引见一下没提到的局部道理:

  依照顺序的举行,在构建Configuration的时刻,插件对象被建立。下面是XMLConfigBuilder局部代码。

private void parseConfiguration(XNode root) {
     try {
            /*省略*/
            pluginElement(root.evalNode("plugins"));
            /*省略*/
        } catch (Exception e) {
            throw new BuilderException((new StringBuilder()).append("Error parsing SQL Mapper Configuration. Cause: ")
                    .append(e).toString(), e);
        }
}
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        Interceptor interceptorInstance;
        for (Iterator iterator = parent.getChildren().iterator(); iterator.hasNext(); configuration .addInterceptor(interceptorInstance)) {
            XNode child = (XNode) iterator.next();
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            interceptorInstance.setProperties(properties);
        } } }

  在初始化插件的时刻,挪用pluginElement要领。在pluginElement要领中运用反射手艺天生插件对应的插件气力,然后挪用插件要领中的setProperties要领,设置我们设置装备摆设的参数,将插件实例保存到设置装备摆设对象中,以便读取和运用它。以是插件的气力对象是一开始就被初始化的,而不是用的时刻才初始化

  实在反射得来的插件实例被存储到interceptorChain中(Chain是链的意义),贴段流程。

 

 

  插件预备好了,接下来就是应用插件阻拦了。在configuration类中。四大对象的初始化要领代码,这里用到了interceptorChain的另一个要领:

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
            BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
}

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
}

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType != null ? executorType : defaultExecutorType;
    executorType = executorType != null ? executorType : ExecutorType.SIMPLE;
    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);
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

  注重视察标红的代码,interceptorChain.pluginAll要领就是就是用来方便一切的插件,校验是不是阻拦该对象。

public Object pluginAll(Object target) {
    for (Iterator iterator = interceptors.iterator(); iterator.hasNext();) {
        Interceptor interceptor = (Interceptor) iterator.next();
        target = interceptor.plugin(target);
    }
    return target;
}

  plugin要领,也就是最上面例子中我写的要领。

  如今就差阻拦prepare要领并用intercept要领覆盖了

  我上面写了intercept要领阻拦了StatementHandler的prepare要领,四大对象传递给plugin要领后就会返回一个署理对象,在运用署理对象的要领(例子中就是StatementHandler的prepare要领)时刻就会进入invoke要领举行逻辑处置惩罚,这是署理形式的症结,经由过程逻辑推断就能够不实用prepare要领而返回intercept要领这就是阻拦的处所。本身编写署理类的工作量很大,MyBatis供应了一个经常使用的对象类Plugin,用来天生署理对象。Plugin类完成了InvocationHandler接口,接纳的是JDK的动态署理手艺。代码以下:

public class Plugin implements InvocationHandler {
    public static Object wrap(Object target, Interceptor interceptor) {
    Map signatureMap = getSignatureMap(interceptor);
    Class type = target.getClass();
    Class interfaces[] = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0)
        return Proxy.newProxyInstance(type.getClassLoader(), interfaces,
                new Plugin(target, interceptor, signatureMap));
    else
        return target;
    }
    public Object invoke(Object proxy, Method method, Object args[]) throws Throwable {
    try {
        Set methods = (Set) signatureMap.get(method.getDeclaringClass());
        if (methods != null && methods.contains(method))
            return interceptor.intercept(new Invocation(target, method, args));
    } catch (Exception e) {
        throw ExceptionUtil.unwrapThrowable(e);
    }
    return method.invoke(target, args);
    }
}

  在例子中,MyPlugin的plugin要领里就是挪用Plugin.wrap要领来返回一个署理对象。当署理对象挪用prepare要领就会进入invoke要领。Signature就是上面提到的署名,若是存在署名的阻拦要领,就会挪用intercept要领并返回效果。如许我们想要做的事就做好了~

 

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。


平台知识

联系方式丨CONTACT

  • 全国热线:7711177
  • 传真热线:010-88888888
  • Q Q咨询:7711177
  • 企业邮箱:
首页
电话
短信