【深入理解MyBatis】- 03Mybatis 从0开始实现Mybatis 插件(plugins)功能

Mybatis 插件(plugins)简介

Mybatis 插件是个拦截器,本质上是JDK动态代理的封装,返回了代理对象,起到了拦截器作用,接下来将按照从0开始实现Mybatis 插件(plugins)功能,再使用一下Mybatis插件应用一下,这里实现并不是与Mybatis Plugin代码一模一样,有一些简化和优化,更加方便理解

简单版拦截器功能 v0.1

定义拦截器

public interface Interceptor {
	 Object intercept(Object target, Method method, Object[] args) throws Throwable;
}

通用动态代理实现

public class Plugin implements InvocationHandler {
	
	private final Object target;
	private final Interceptor interceptor;
	
	public Plugin(Object target, Interceptor interceptor) {
		this.target = target;
		this.interceptor = interceptor;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return interceptor.intercept(target, method, args);
	}
}

封装生成代理类工具类

public class PluginUtils {
	public static <T> T wrap(T target, Interceptor interceptor) {
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				new Plugin(target, interceptor));
	}
}

编写业务测试类

public interface IPerson {
	String sayHello(String name);
	void test();
}

public class PersonImpl implements IPerson {
	@Override
	public String sayHello(String name) {
		String msg = "hello, " + name;
		System.out.println(msg);
		return msg;
	}
	
	@Override
	public void test() {
		System.out.println("test");
	}
}

测试类

实现自定义拦截器

public class MyInterceptor implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("before.");
		Object invoke = method.invoke(target, args);
		System.out.println("after.");
		return invoke;
	}
}

测试类

public class MyTest {
	public static void main(String[] args) {
		Interceptor interceptor = new MyInterceptor();
		IPerson person = new PersonImpl();
		IPerson personProxy = PluginUtils.wrap(person, interceptor);
		String message = personProxy.sayHello("zhangsan");
		System.out.println("return: " + message);
	}
}

测试结果,拦截成功

before.
hello, zhangsan
after.
return: hello, zhangsan

实现注解功能 v0.2

拦截器注解@Intercepts

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
	Signature[] value();
}

标记拦截哪些类注解@Signature

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
	Class<?> type();

	SignatureMethod[] method();
}

标记拦截哪些方法注解@SignatureMethod

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface SignatureMethod {
	String method();

	Class<?>[] args();
}

优化代理生成工具类,添加解析注解信息并传入给Plugin类

public class PluginUtils {
	public static <T> T wrap(T target, Interceptor interceptor) {
		Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				new Plugin(target, interceptor, signatureMap));
	}
	
	// 解析注解,获取对应的类和方法
	private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
		Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
		if (interceptsAnnotation == null) {
			throw new RuntimeException(
					"No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
		}
		
		Signature[] sigs = interceptsAnnotation.value();
		Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
		for (Signature sig : sigs) {
			Set<Method> methods = signatureMap.get(sig.type());
			if (methods == null) {
				methods = new HashSet<Method>();
				signatureMap.put(sig.type(), methods);
			}

			SignatureMethod[] signatureMethod = sig.method();
			for (int i = 0; i < signatureMethod.length; i++) {
				try {
					Method method = sig.type().getMethod(signatureMethod[i].method(), signatureMethod[i].args());
					methods.add(method);
				} catch (NoSuchMethodException e) {
					throw new RuntimeException(
							"Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
				}
			}
		}
		return signatureMap;
	}
}

优化自定义拦截器,对方法进行控制,只拦截注解对应的方法

public class Plugin implements InvocationHandler {
	private final Object target;
	private final Interceptor interceptor;
	private final Map<Class<?>, Set<Method>> signatureMap;

	public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
		this.target = target;
		this.interceptor = interceptor;
		this.signatureMap = signatureMap;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Set<Method> methods = signatureMap.get(method.getDeclaringClass());
		if (methods != null && methods.contains(method)) {
			return interceptor.intercept(target, method, args);
		}
		return method.invoke(target, args);
	}
}

测试类

实现自定义拦截器

@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("3333.");
		Object invoke = method.invoke(target, args);
		return invoke;
	}
}

测试类

public class MyTest {
	public static void main(String[] args) {
		Interceptor interceptor = new MyInterceptor3();
		IPerson person = new PersonImpl();
		IPerson personProxy = PluginUtils.wrap(person, interceptor);
		String message = personProxy.sayHello("zhangsan");
		System.out.println("return: " + message);
	}
}

测试结果,拦截成功

3333.
hello, zhangsan
return: hello, zhangsan

修改拦截器自定义拦截器,改为@SignatureMethod(method = "toString", args = {})

@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "test", args = {}) }) })
public class MyInterceptor3 implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("3333.");
		Object invoke = method.invoke(target, args);
		return invoke;
	}
}

执行测试类

public class MyTest3 {

	public static void main(String[] args) {
		Interceptor interceptor = new MyInterceptor3();
		IPerson person = new PersonImpl();
		IPerson personProxy = PluginUtils.wrap(person, interceptor);
		String message = personProxy.sayHello("zhangsan");
		System.out.println("return: " + message);
		personProxy.test();
	}
}

测试结果,可以看出,只代理了test方法,没有代理sayHello

hello, zhangsan
return: hello, zhangsan
3333.
test

若想代理,需要修改注解,再次执行测试类即可

@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }),
				@SignatureMethod(method = "test", args = { }) }) })

拦截器链实现 v0.3

链式封装

public class InterceptorChain {

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

	public Object wrapAll(Object target) {
		for (Interceptor interceptor : interceptors) {
			target = PluginUtils.wrap(target, interceptor);
		}
		return target;
	}

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

添加实现自定义拦截器

@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("3333.");
		Object invoke = method.invoke(target, args);
		return invoke;
	}
}
@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor4 implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("4444.");
		Object invoke = method.invoke(target, args);
		return invoke;
	}
}
@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor5 implements Interceptor {
	@Override
	public Object intercept(Object target, Method method, Object[] args) throws Throwable {
		System.out.println("5555.");
		Object invoke = method.invoke(target, args);
		return invoke;
	}
}

测试类

public class MyTest2 {
	public static void main(String[] args) {
		InterceptorChain chain = new InterceptorChain();
		chain.addInterceptor(new MyInterceptor3());
		chain.addInterceptor(new MyInterceptor4());
		chain.addInterceptor(new MyInterceptor5());
		IPerson person = new PersonImpl();
		IPerson personProxy = (IPerson) chain.wrapAll(person);
		String message = personProxy.sayHello("zhangsan");
		System.out.println("return: " + message);
	}
}

测试结果,多拦截器效果

5555.
4444.
3333.
hello, zhangsan
return: hello, zhangsan

可以思考一下为什么倒序输出5555. 4444. 3333. ?

拦截器链实现 v0.4

封装一下拦截器Interceptor的参数

public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  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);
  }

}

修改拦截器Interceptor参数

public interface Interceptor {
	 Object intercept(Invocation invocation) throws Throwable;
}

插件Plugin封装修改return interceptor.intercept(new Invocation(target, method, args));

public class Plugin implements InvocationHandler {
	private final Object target;
	private final Interceptor interceptor;
	private final Map<Class<?>, Set<Method>> signatureMap;

	public Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
		this.target = target;
		this.interceptor = interceptor;
		this.signatureMap = signatureMap;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Set<Method> methods = signatureMap.get(method.getDeclaringClass());
		if (methods != null && methods.contains(method)) {
			return interceptor.intercept(new Invocation(target, method, args));
		}
		return method.invoke(target, args);
	}
}

修改自定义拦截器实现Object invoke = invocation.proceed();

@Intercepts({
		@Signature(type = IPerson.class, method = { 
				@SignatureMethod(method = "sayHello", args = { String.class }) }) })
public class MyInterceptor3 implements Interceptor {
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		System.out.println("3333.");
		Object invoke = invocation.proceed();
		return invoke;
	}
}

测试类

	public static void main(String[] args) {
		Interceptor interceptor = new MyInterceptor3();
		IPerson person = new PersonImpl();
		IPerson personProxy = PluginUtils.wrap(person, interceptor);
		String message = personProxy.sayHello("zhangsan");
		System.out.println("return: " + message);
	}
}

测试类

3333.
hello, zhangsan
return: hello, zhangsan

接下来分析一下官网的使用

Mybatis官网插件(plugins)分析

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

所有我们注册的类只能是上面的四个接口,ExamplePlugin.java拦截器例子

@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need, 拦截前
    Object returnObject = invocation.proceed();
    // implement post processing if need, 拦截后
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}

那么从源代码实现来看,为什么Mybatis plugins功能只支持以前四个接口呢,我们可以从类org.apache.ibatis.session.Configuration中明白

  • Executor,这个一个获取Executor的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的Executor对象为代理对象
	public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
		executorType = executorType == null ? defaultExecutorType : executorType;
		executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
		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;
	}
  • StatementHandler,这个一个获取StatementHandler的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的StatementHandler对象为代理对象
	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;
	}
  • ParameterHandler,这个一个获取ParameterHandler的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的ParameterHandler对象为代理对象
	public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
			BoundSql boundSql) {
		ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,
				parameterObject, boundSql);
		parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
		return parameterHandler;
	}
  • ResultSetHandler ,这个一个获取ResultSetHandler 的方法,其他的代码可以不用理解,只用查看最后两行代码即可,也就是获取到的ResultSetHandler 对象为代理对象
	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;
	}

完整代码例子

  1. 执行命令从github上面拉取代码:git clone git@github.com:dengjili/mybatis-3-mybatis-3.4.6.git
  2. 相关代码目录
    src/test/java priv.mybatis.example03
    src/test/java priv.mybatis.example04
    src/test/java priv.mybatis.example05
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值