从myBatis Plugin看责任链模式
[TOC]
一、介绍
在mybatis
中从sql
的解析到最后结果集的返回,经过了一系列的内部组件,比如参数处理器parameterHandler
,语句处理器StatementHandler
,结果集处理器ResultSetHandler
等。若开发者需要对SQL
执行的某一环节进行一些特定的处理,比如参数类型的转换,数据分页功能,打印执行的SQL
语句等都可以通过mybatis
的插件机制实现。
二、mybatis的责任链
mybatis
中就是对内部的一个List
数组做拦截,业务方通过实现Interceptor
接口后,将具体的实现类通过InterceptorChain#addInterceptor
添加到责任链中,当mybatis
初始化资源时,会调用InterceptorChain#pluginAll
通过代理的方式,将所有的插件通过逐层代理的方式将内部核心组件(比如ParameterHandler
)包裹返回一个代理对象。
真正执行的地方是由于将内部核心组件都包装成了代理类,所以在调用执行方法时,会被代理对象拦截进入invoke
方法,根据执行方法所属类以及注解等判断是否执行拦截器或者是执行原方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties); }
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
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); } }
|
三、过滤器相关责任链
在权限校验等一些拦截器中,通常的做法是有多层拦截,比如简单的登录过程,先校验用户名密码是否正确,在校验是否拥有某项操作的操作权限之后才会使得用户获取到资源,但是如果用户名密码校验失败,就没有必要进入第二部的操作权限校验,所以这种场景下使用mybatis
那种方式的责任链有所不妥。以下是基于在多层拦截下,若某层校验失败,直接拒绝继续往下校验的责任链模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
public interface Filter {
Object doFilter(Object target, FilterChain filterChain); }
class FilterA implements Filter{
@Override public Object doFilter(Object target, FilterChain filterChain){ System.out.println("A"); return filterChain.doFilter(target); } }
class FilterB implements Filter{
@Override public Object doFilter(Object target, FilterChain filterChain){ System.out.println("B"); return ""; } }
public static class FilterChain{
private List<Filter> filters = new ArrayList<>();
private Iterator iterator;
public Object doFilter(Object target){
if(iterator == null){ iterator = filters.iterator(); }
if(iterator.hasNext()){ Filter filter = (Filter) iterator.next(); filter.doFilter(target, this); } return target; }
public void addFilter(Filter filter){ filters.add(filter); } }
public static void main(String[] args) { FilterChain filterChain = new FilterChain(); FilterA filterA = new FilterA(); FilterB filterB = new FilterB(); filterChain.addFilter(filterA); filterChain.addFilter(filterB); filterChain.doFilter(""); }
|
四、总结
以上两种责任链的不同形式,其实是应对于不同的业务场景,当需要所有的拦截都走一轮,则采用第一种;当在某个拦截器失败后不继续进行,则采用第二种。在实际的场景中需要综合考虑,采取最符合业务场景的形式进行编码。