MyBatis插件

MyBatis插件

MyBatis四大对象,每一个对象在创建的时候,都会经过以下语句包装

获取到所有的interceptors(拦截器),调用interceptor.plugin(target);返回target包装后的对象

插件机制:可以使用插件为目标对象创建一个代理对象,代理对象可以拦截到四大对象的每一个执行

Executor executor = (Executor)this.interceptorChain.pluginAll(executor);

		public Object pluginAll(Object target) {
		    for (Interceptor interceptor : interceptors) {
		      target = interceptor.plugin(target);
		    }
		    return target;
		  }

插件的编写

  1. 创建Interceptor接口的实现类

    public class MyFirstPlugin implements Interceptor {
        // 拦截目标对象方法的目标方法执行
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
             System.out.println("MyFirstPlugin===>intercept:" + invocation.getMethod());
            //执行目标方法
            Object proceed = invocation.proceed();
            return proceed;
        }
    
        //包装目标对象,为目标对象生成一个代理对象
        @Override
        public Object plugin(Object target) {
            System.out.println("MyFirstPlugin===>plugin:" + target);
            //调用Plugin.wrap来使用当前的interceptor包装目标对象
            Object wrap = Plugin.wrap(target, this);
            //返回为当前target创建的动态代理
            return wrap;
        }
    	//将插件注册时 的property属性设置进来
        @Override
        public void setProperties(Properties properties) {
            System.out.println("MyFirstPlugin===>setProperties:插件的配置信息:" + properties);
        }
    }
    
  2. 使用@Intercepts注解完成插件签名

    @Intercepts的参数为@Signature的集合,Signature的第一个参数是要拦截的对象,第二个参数是拦截对象的方法,第三个参数为方法的形参

    @Intercepts(
            {
                    @Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
            }
    )
    public class MyFirstPlugin implements Interceptor {
    }
    
  3. 将写好的插件注册到全局配置文件中

    	<!--配置mybatis插件-->
    	<plugins>
    		<plugin interceptor="com.yellowstar.plugins.MyFirstPlugin">
    			<!--可以注册属性-->
    			<property name="username" value="root"/>
    			<property name="password" value="123456"/>
    		</plugin>
    	</plugins>
    

单个插件的运行

先看一下配置了插件之后,执行查询语句的输出结果

MyFirstPlugin===>setProperties:插件的配置信息:{password=123456, username=root}
MyFirstPlugin===>plugin:org.apache.ibatis.executor.CachingExecutor@2781e022
MyFirstPlugin===>plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@42d8062c
MyFirstPlugin===>plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@12cdcf4
MyFirstPlugin===>plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@5f3a4b84
MyFirstPlugin===>intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
org.apache.ibatis.executor.statement.RoutingStatementHandler@5f3a4b84

断点调试,看以下源码,signatureMap是我们注册的目标对象(StatementHandler),type是当前运行到的对象(假设当前为Executor),那么判断条件不成立,会直接返回target,如果当前运行的对象跟目标对象相同,则为目标对象创建一个代理对象

    public static Object wrap(Object target, Interceptor interceptor) {
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }

在这里插入图片描述

创建完代理对象后,接着往下走会执行我们的目标方法,这里我们可以看出plugin方法是用来包装目标对象,intercept方法是执行目标对象的方法

在这里插入图片描述

多个插件的执行

假设场景:两个插件同时执行,为了确保目标对象和目标方法一致,复制一个插件,并在xml文件中进行配置

	<plugins>
		<plugin interceptor="com.yellowstar.plugins.MyFirstPlugin">
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
		<plugin interceptor="com.yellowstar.plugins.MySecondPlugin"></plugin>
	</plugins>

先来看一下运行结果,我们发现执行顺序是先由配置顺序决定的,当符合拦截条件时,会对目标对象进行包装,这里需要注意的是第二个插件对对象进行包装时,是对一个插件包装后的对象进行包装,所以在执行intercept方法时,会倒序执行

MyFirstPlugin===>setProperties:插件的配置信息:{password=123456, username=root}
MyFirstPlugin===>plugin:org.apache.ibatis.executor.CachingExecutor@4493d195
MySecondPlugin===>plugin:org.apache.ibatis.executor.CachingExecutor@4493d195
MyFirstPlugin===>plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@45820e51
MySecondPlugin===>plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@45820e51
MyFirstPlugin===>plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@25af5db5
MySecondPlugin===>plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@25af5db5
MyFirstPlugin===>plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@5bcea91b
MySecondPlugin===>plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@5bcea91b
MySecondPlugin===>intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
MyFirstPlugin===>intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException

在这里插入图片描述

简单的插件制作

场景:根据员工id查询员工数据,无论传过来的id是多少,我们都通过插件将他设置为查询id为5的员工

我们知道,执行目标方法的方法是intercept(),设置sql语句的参数是通过ParameterHandler设置的,其中parameterObject用来设置参数值,所以需要渠道parameterObject对象进行修改

 	@Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyFirstPlugin===>intercept:" + invocation.getMethod());
        //原始的sql查询id为1的员工,现将他修改成查询id为5的员工
        //获取当前拦截的对象
        Object target = invocation.getTarget();
        System.out.println(target);
        //拿到:StatementHandler==>ParameterHandler===>parameterObject
        //拿到target的元数据,相当于拿到StatementHandler
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        System.out.println("原始参数为:" + value);
        metaObject.setValue("parameterHandler.parameterObject",5);
        //执行目标方法
        Object proceed = invocation.proceed();
        return proceed;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值