Mybatis---学习过程---Mybatis缓存之插件机制

Mybatis—学习过程—Mybatis缓存之插件机制

1.什么是插件:可以理解为其他形式的拓展点在这里插入图片描述

2.Mybatis插件介绍:

在这里插入图片描述
也就是说使用插件完成组件中的动态增强,底层执行的就是动态代理,也就是说这四大组件(四大对象)返回的并不是对应的原生对象,而是代理对象
四大核心对象的关系图
在这里插入图片描述

  1. Executor:就是执行器,主要负责增删改查的行为
  2. StatementHandler:sql语法构建器,主要是完成sql语法的预编译
  3. ParamterHandler:参数处理器,用来设置参数
  4. ResultSetHandler:结果集处理器,主要用来处理返回结果集的
  5. Mybatis
  6. Mybatis允许使用插件对这四大核心对象进行拦截,允许拦截的方法如下
Mybatis被允许拦截的方法如下(也就是四大核心对象里面的方法)

在这里插入图片描述

3.Mybatis插件原理

在四大对象创建的时候

在这里插入图片描述
在这里插入图片描述

拦截:插件如何拦截并附加额外功能,以ParameterHandler来说
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, 
							Object object, BoundSql sql, InterceptorChain interceptorChain) {
	//创建了一个parameterHandler对象					
    ParameterHandler parameterHandler = 
    					mappedStatement.getLang().createParameterHandler(mappedStatement, object, sql);
    //每个创建出来的parameterHandler对象不是直接返回的,
    //而是使用interceptorChain.plugAll(paramterHandler)进行了处理
    //这个pluginAll是怎么进行处理的?请看下面这部分代码					
    parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
    //通过pluginAll方法返回的代理对象,最中还是要赋值给parameterHandler
    //最中我们要返回的就是parameterHandler这个代理对象(重重代理对象)
    return parameterHandler;
}

pluginAll方法如何处理的

//获取所有的Interceptor(拦截器)插件需要实现接口,调用interceptor.plugin(target);返回target包装后的对象
public Object pluginAll(Object target) {
	//首先在这个方法中遍历了所有的拦截器,另外target是上面传过来的原生对象
    for (Interceptor interceptor : interceptors) {
    	//将原生对象进行了处理,怎么处理的就是使用了动态代理,产生了一个代理对象target
        target = interceptor.plugin(target);
    }
    //最终返回的也是代理对象
    return target;
}

4.Mybatis自定义插件

在这里插入图片描述
如果想要拦截Excutor中的query方法,可以这样定义插件

  1. 参数
    1. type:表示要拦截的接口类型
    2. method:表示要拦截接口中的哪个方法
    3. args:表示该方法的入参
      在这里插入图片描述
  2. 定义完插件之后,还需要将插件配置到Mybatis全局配置文件(sqlMapConfig.xml)中去
    <plugins> 
    	<plugin interceptor="com.lagou.plugin.ExamplePlugin">
    	</plugin>
    </plugins>
    
    在这里插入图片描述

5.Mybatis插件总结

1.因为返回的Excutor是代理对象,代理对象执行方法其实是调用底层的invoke方法
2.在invoke方法中,我们首先可以完成一个原方法的调用,但是在原方法调用的前和后可以进行对相关逻辑的增强
3.Mybatis允许拦截的核心对象一共就4个,主要是通过拦截四个核心组件当中的方法,来完成方法的动态增强。

6.Mybatis自定义插件

Mybatis的插件接口——Interceptor(拦截器)
在这里插入图片描述

7.自定义插件实例

@Intercepts({
        //@Signature:这个注解可以配置很多个,可以拦截多个核心对象里面的方法
        @Signature(type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class})
})
public class MyPlugin implements Interceptor {

    /**
     * 拦截方法:只要被拦截的目标对象的目标方法被执行时,每次都会执行这个Intercept方法,
     *          增强的逻辑写在intercept中即可
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("对方法进行了增强");
        //proceed让原方法执行
        return invocation.proceed();
    }

    /**
     * 主要是把当前的拦截器生成的代理对象存到拦截器链中
     * target是被拦截的目标对象
     * 调用Plugin类中的wrap(target,interceptor)方法
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        Object wrap = Plugin.wrap(target, this);
        return wrap;
    }

    /**
     * 获取配置文件的参数
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("获取到的配置参数是"+properties);
    }
}

配置在sqlMapConfig.xml全局配置文件

<plugins>
    <plugin interceptor="com.lagou.plugin.MyPlugin">
    //property属性是设置参数的,最终执行打印出来的结果是----获取到的配置参数是{name=tom}
        <property name="name" value="tom"/>
    </plugin>
</plugins>

8.Mybatis插件机制—源码分析

  1. 先找入口,认为Plugin是入口,因为它实现了InvocationHandler接口,编写JDK动态代理时,第三个参数就是InvocationHandler,代码如下。并且发现,每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,而且
    在这里插入图片描述
    编写JDK动态代理代码实例
    Object Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            final Constructor<?> cons = cl.getConstructor(constructorParams);
    
            return cons.newInstance(new Object[]{h});
        }
    
  2. 由于Mybatis中的四大核心组件返回的都是代理对象,当代理对象调用方法的时候,底层要执行invoke方法,那么invoke方法怎么知道你当前调用的方法是否需要增强呢?
    invoke方法执行逻辑
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
        	//method.getDeclaringClass():表示根据当前调用的方法获取所在的类的class对象,获取到被拦截列表
        	//所以说当前这个Set集合存储的都是被拦截方法列表
            Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
            //判断存储方法的Set集合中是否包含被拦截的方法,如果有就调用intercept方法来进行方法的增强
            //之后才进行原方法的执行,如果没有就执行这个代理类的目标代理方法,匹配参数
            return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
        } catch (Exception var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值