插件原理

在四大对象创建的时候
1、每个创建出来的对象不是直接返回的,而是经过interceptorChain.pluginAll(四大对象)
2、获取到所有的Interceptor(拦截器,插件需要实现的接口),调用interceptor.plugin(target),返回target包装后的对象
3、插件机制,使用插件为目标对象来创建一个代理对象,与AOP原理相同
即插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行方法

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

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

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

插件编写:
1、编写Interceptor的实现类
2、使用@Intercepts注解完成插件签名
3、将写好的插件注册到全局配置文件中

/**
 * 通过注解设置是哪些对象可以被该插件拦截
 * 	type指定拦截的目标对象
 * 	method指定拦截的方法
 * 	args指定拦截方法需要传入的参数类型,确定是哪个具体方法,因为方法可能存在重载
 * @author Administrator
 *
 */
@Intercepts(
	value = { 
		@Signature(
			args = { java.sql.Statement.class }, 
			method = "parameterize", 
			type =org.apache.ibatis.executor.statement.StatementHandler.class ) 
           }
)
public class MyInterceptor implements Interceptor {

	/**
	 * intercept:拦截
	 * 拦截目标对象方法的执行,可以在这里编写目标方法执行前后需要做的操作
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("*********目标方法执行前*****");
		Object obj = invocation.proceed();//执行目标方法
		System.out.println("*********目标方法执行后*****");
		return obj;
	}

	/**
	 * 创建代理对象,Plugin.wrap方法会将四大对象一一与@Intercepts注解中的属性相匹配,如果匹配上就生成代理对象,匹配不上直接返回目标对象
	 */
	@Override
	public Object plugin(Object target) {
		// TODO Auto-generated method stub
		System.out.println("*********plugin*****");
		Object obj = Plugin.wrap(target, this);
		return obj;
	}

	/**
	 * 获取插件中的property属性
	 */
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		System.out.println(properties);
	}

}

全局配置文件中注册该插件

   <plugins>
    	<plugin interceptor="com.learncmzy.interceptor.MyInterceptor">
    		<property name="username" value="yangguiyong"/>
    		<property name="password" value="123456"/>
    	</plugin>
    </plugins>

如果是编写了多个插件,并且多个插件拦截的是同一个目标对象,那么多个插件会产生多层代理。创建动态代理的时候,是按照插件在全局配置文件配置顺序创建层层代理对象,执行目标方法的时候,是按照逆向顺序执行。

使用插件实现参数查询时参数的修改,比如查询id 为1的学生,结果查询id为6的学生。查询时使用StatementHandler,但是是生成的RoutingStatementHandler,里面的PreparedStatementHandler执行parameterize方法,方法中使用ParameterHandler对参数进行赋值,此时DefaultParamenterHandler中包含了传入的参数parameterObject,修改parameterObject即可。
通过SystemMetaObject.forObject方式可以获取目标对象中的元数据。metaObject.getValue,metaObject.setValue获取和修改目标对象的元数据

public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("*********目标方法执行前*****");
		//获取目标对象
		Object  target = invocation.getTarget();
		//通过SystemMetaObject.forObject方式可以获取目标对象中的元数据,从而可以轻易的对目标对象中的元数据进行修改
		MetaObject metaObject = SystemMetaObject.forObject(target);
		//获取目标对象中的元数据
		Object param = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println(param);
		//将目标对象中的参数修改,从而实现偷梁换柱的现象
		metaObject.setValue("parameterHandler.parameterObject", 6);
		Object obj = invocation.proceed();
		System.out.println("*********目标方法执行后*****");
		return obj;
	}

注意:插件的编写需要慎重,必须要弄清楚拦截的对象的具体作用,拦截对象里面的具体元数据,以及方法执行的流程,否则破坏Mybatis底层的逻辑

PageHelper分页插件:
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值