<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MyBatis自定義實(shí)現(xiàn)攔截器插件

          共 5433字,需瀏覽 11分鐘

           ·

          2020-11-24 23:12

          首先熟悉一下Mybatis的執(zhí)行過程,如下圖:

          類型

          先說明Mybatis中可以被攔截的類型具體有以下四種:

          1.Executor:攔截執(zhí)行器的方法。
          2.ParameterHandler:攔截參數(shù)的處理。
          3.ResultHandler:攔截結(jié)果集的處理。
          4.StatementHandler:攔截Sql語法構(gòu)建的處理。
          1234

          規(guī)則

          Intercepts注解需要一個(gè)Signature(攔截點(diǎn))參數(shù)數(shù)組。通過Signature來指定攔截哪個(gè)對(duì)象里面的哪個(gè)方法。@Intercepts注解定義如下:

          @Documented
          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.TYPE)
          public?@interface?Intercepts?{
          ????/**
          ?????*?定義攔截點(diǎn)
          ?????*?只有符合攔截點(diǎn)的條件才會(huì)進(jìn)入到攔截器
          ?????*/

          ????Signature[]?value();
          }

          Signature來指定咱們需要攔截那個(gè)類對(duì)象的哪個(gè)方法。定義如下:

          @Documented
          @Retention(RetentionPolicy.RUNTIME)
          @Target({})
          public?@interface?Signature?{
          ??/**
          ???*?定義攔截的類?Executor、ParameterHandler、StatementHandler、ResultSetHandler當(dāng)中的一個(gè)
          ???*/

          ??Class?type();

          ??/**
          ???*?在定義攔截類的基礎(chǔ)之上,在定義攔截的方法
          ???*/

          ??String?method();

          ??/**
          ???*?在定義攔截方法的基礎(chǔ)之上在定義攔截的方法對(duì)應(yīng)的參數(shù),
          ???*?JAVA里面方法可能重載,故注意參數(shù)的類型和順序
          ???*/

          ??Class[]?args();
          }

          標(biāo)識(shí)攔截注解@Intercepts規(guī)則使用,簡(jiǎn)單實(shí)例如下:

          @Intercepts({//注意看這個(gè)大花括號(hào),也就這說這里可以定義多個(gè)@Signature對(duì)多個(gè)地方攔截,都用這個(gè)攔截器
          ????????@Signature(
          ????????????????type?=?ResultSetHandler.class,
          ????????????????method?
          =?"handleResultSets",?
          ????????????????args?=?{Statement.class}),
          ????????@Signature(type?
          =?Executor.class,
          ????????????????method?
          =?"query",
          ????????????????args?=?{MappedStatement.class,?Object.class,?RowBounds.class,?ResultHandler.class})
          })

          說明:@Intercepts:標(biāo)識(shí)該類是一個(gè)攔截器;

          @Signature:指明自定義攔截器需要攔截哪一個(gè)類型,哪一個(gè)方法;

          • type:上述四種類型中的一種;
          • method:對(duì)應(yīng)接口中的哪類方法(因?yàn)榭赡艽嬖谥剌d方法);
          • args:對(duì)應(yīng)哪一個(gè)方法的入?yún)ⅲ?/section>

          method中對(duì)應(yīng)四種的類型的方法:

          攔截類型攔截方法
          Executorupdate, query, flushStatements, commit, rollback,getTransaction, close, isClosed
          ParameterHandlergetParameterObject, setParameters
          StatementHandlerprepare, parameterize, batch, update, query
          ResultSetHandlerhandleResultSets, handleOutputParameters

          介紹

          談到自定義攔截器實(shí)踐部分,主要按照以下三步:

          1. 實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口,重寫以下方法:
          public?interface?Interceptor?{
          ????Object?intercept(Invocation?var1)?throws?Throwable;
          ????Object?plugin(Object?var1);
          ????void?setProperties(Properties?var1);
          }
          1. 添加攔截器注解@Intercepts{...}。具體值遵循上述規(guī)則設(shè)置。
          2. 配置文件中添加攔截器。

          intercept(Invocation invocation)

          從上面我們了解到interceptor能夠攔截的四種類型對(duì)象,此處入?yún)?code style="margin-right: 2px;margin-left: 2px;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;background: rgb(248, 245, 236);color: rgb(255, 53, 2);line-height: 1.5;font-size: 90%;padding: 3px 5px;border-radius: 2px;">invocation便是指攔截到的對(duì)象。舉例說明:攔截StatementHandler#query(Statement st,ResultHandler rh)方法,那么Invocation就是該對(duì)象。

          plugin(Object target)

          這個(gè)方法的作用是就是讓mybatis判斷,是否要進(jìn)行攔截,然后做出決定是否生成一個(gè)代理。

          @Override
          public?Object?plugin(Object?target)?{
          ??//判斷是否攔截這個(gè)類型對(duì)象(根據(jù)@Intercepts注解決定),然后決定是返回一個(gè)代理對(duì)象還是返回原對(duì)象。
          ??//故我們?cè)趯?shí)現(xiàn)plugin方法時(shí),要判斷一下目標(biāo)類型,如果是插件要攔截的對(duì)象時(shí)才執(zhí)行Plugin.wrap方法,否則的話,直接返回目標(biāo)本身。
          ??if?(target?instanceof?StatementHandler)?{
          ????return?Plugin.wrap(target,?this);
          ??}
          ??return?target;
          }

          注意:每經(jīng)過一個(gè)攔截器對(duì)象都會(huì)調(diào)用插件的plugin方法,也就是說,該方法會(huì)調(diào)用4次。根據(jù)@Intercepts注解來決定是否進(jìn)行攔截處理。

          setProperties(Properties properties)

          攔截器需要一些變量對(duì)象,而且這個(gè)對(duì)象是支持可配置的。

          實(shí)戰(zhàn)

          • 自定義攔截器
          @Intercepts(value?=?{@Signature(type?=?StatementHandler.class,?method?=?"prepare",?args?=?{Connection.class,?Integer.class})})
          public?class?MyInterceptor?implements?Interceptor?
          {

          ????@Override
          ????public?Object?intercept(Invocation?invocation)?throws?Throwable?{
          ????????StatementHandler?statementHandler?=?(StatementHandler)?invocation.getTarget();
          ????????BoundSql?boundSql?=?statementHandler.getBoundSql();
          ????????Object?obj?=?boundSql.getParameterObject();
          ????????String?sql?=?boundSql.getSql();
          ????????if?(sql.trim().toUpperCase().startsWith("INSERT"))?{
          ????????????ReflectUtil.setFieldValue(obj,?"rev",?0);
          ????????????ReflectUtil.setFieldValue(obj,?"createTime",?new?Date());
          ????????????ReflectUtil.setFieldValue(obj,?"operateTime",?new?Date());
          ????????????ReflectUtil.setFieldValue(boundSql,"parameterObject",?obj);

          ????????}?else?if?(sql.trim().toUpperCase().startsWith("UPDATE"))?{
          ????????????sql?=?sql.replaceAll("?set?",?"?SET?")
          ????????????????????.replaceAll("?Set?",?"?SET?")
          ????????????????????.replaceAll("?SET?",?"?SET?rev?=?rev+1,?operate_time?=?NOW(),?");
          ????????????ReflectUtil.setFieldValue(boundSql,"sql",?sql);
          ????????}
          ????????return?invocation.proceed();
          ????}

          ????@Override
          ????public?Object?plugin(Object?o)?{
          ????????return?Plugin.wrap(o,?this);
          ????}

          ????@Override
          ????public?void?setProperties(Properties?properties)?{

          ????}
          }

          主要看下核心代碼方法intercept(): 這段代碼主要目的:攔截insert和update語句,利用反射機(jī)制,設(shè)置insert語句的參數(shù)rev(版本號(hào),利用樂觀鎖),第一次查詢,故創(chuàng)建時(shí)間和操作時(shí)間相同;update是將版本號(hào)+1,統(tǒng)一修改其操作時(shí)間。

          • mybatis-config

          configuration?PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-config.dtd">
          <configuration>????
          ?<plugins>
          ????????<plugin?interceptor="com.qxy.mybatis.interceptor.MyInterceptor"/>
          ????plugins>

          configuration>
          • application.yml 特別重要的一點(diǎn),一定將mybatis-config中的對(duì)象注入到Sprint容器中,否則不會(huì)生效。
          ...//省略其他配置
          mybatis:
          ??config-location:?classpath:/mybatis-config.xml
          • ReflectUtil
          public?class?ReflectUtil?{

          ????private?ReflectUtil()?{}

          ????/**
          ?????*?利用反射獲取指定對(duì)象的指定屬性
          ?????*?@param?obj?目標(biāo)對(duì)象
          ?????*?@param?fieldName?目標(biāo)屬性
          ?????*?@return?目標(biāo)字段
          ?????*/

          ????private?static?Field?getField(Object?obj,?String?fieldName)?{
          ????????Field?field?=?null;
          ????????for?(Class?clazz?=?obj.getClass();?clazz?!=?Object.class;?clazz?=?clazz.getSuperclass())?{
          ????????????try?{
          ????????????????field?=?clazz.getDeclaredField(fieldName);
          ????????????????break;
          ????????????}?catch?(NoSuchFieldException?e)?{
          ????????????????//這里不用做處理,子類沒有該字段,可能父類有,都沒有就返回null
          ????????????}
          ????????}
          ????????return?field;
          ????}

          ????/**
          ?????*?利用反射設(shè)置指定對(duì)象的指定屬性為指定的值
          ?????*?@param?obj?目標(biāo)對(duì)象
          ?????*?@param?fieldName?目標(biāo)屬性
          ?????*?@param?fieldValue?目標(biāo)值
          ?????*/

          ????public?static?void?setFieldValue(Object?obj,?String?fieldName,?Object?fieldValue)?throws?IllegalAccessException?{
          ????????Field?field?=?getField(obj,?fieldName);
          ????????if?(field?!=?null)?{
          ????????????field.setAccessible(true);
          ????????????field.set(obj,?fieldValue);
          ????????}
          ????}
          }
          • debug

          上圖中能夠看到BoundSql對(duì)象中主要存儲(chǔ)的屬性值,所以我們自定義攔截器時(shí),主要針對(duì)BoundSql的屬性值進(jìn)行修改。程序代碼沒有走到我們反射機(jī)制設(shè)置值的位置,測(cè)試createTime=null;

          返回之前,看下BoundSql對(duì)象的值,創(chuàng)建時(shí)間已被賦值。

          點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了
          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人AV影院久久 | 欧美黄频| 日本视频国产 | 特黄AAAAAAAA片免费直播 | 天天操综合网 |