zoukankan      html  css  js  c++  java
  • Mybatis的核心实现Plugin、Interceptor

    本章讲Interceptor其实在分享怎样的设计才是具备良好的拓展性的程序,注重内功修为的童靴可能更感兴趣。Mybatis中的插件允许你针对核心组件接口Executor 、StatementHandler、ParameterHandler、ResultSetHandler中任何一个方法进行拦截调用。而每个Interceptor(拦截的接口)其实是通过JDK的动态代理技术生成的代理类,每当执行这4种接口中的方法时,就会进入拦截方法(具体就是InvocationHandler的invoke()方法)。

    关于Mybatis的系统架构及Executor、ParameterHandler、ResultSetHandler、StatementHandler可参阅之前的相关的分享。

    责任链模式

    责任链模式是一种对象行为模式,插件用的是它,很多对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。

    用法
    用法一点也不复杂:只需实现 Interceptor 接口,并指定想要拦截的方法签名即可,更多用法可参阅官方教程

    除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。

    <!-- mybatis-config.xml -->
    <plugins>
    <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
    </plugin>
    </plugins>
    //执行Executor.update时拦截
    @Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {//干你想干的事情
    return invocation.proceed();
    }
    public Object plugin(Object target) {
    return Plugin.wrap(target, this);
    }
    public void setProperties(Properties properties) {
    }
    }
    源码
    在上面的源码中Plugin.wrap(),它就是当前拦截器(ExamplePlugin)的代理类。而InterceptorChain.pluginAll()则定义了一个组件接口(比如:ParameterHandler)执行的责任链。

    public interface Interceptor {
    //拦截器具体实现
    Object intercept(Invocation invocation) throws Throwable;
    //拦截器的代理类
    Object plugin(Object target);
    //添加属性
    void setProperties(Properties properties);
    }
    //源码Plugin.wrap其实是一个代理
    public class Plugin implements InvocationHandler {
    private Object target;
    private Interceptor interceptor;//拦截器
    private Map<Class<?>, Set<Method>> signatureMap;
    //为拦截器生成代理类
    public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {//目标列的接口与拦截器匹配,则生成代理类
    return Proxy.newProxyInstance(
    type.getClassLoader(),
    interfaces,
    new Plugin(target, interceptor, signatureMap));
    }
    return target;//原生类返回
    }
    //代理类执行入口
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    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);
    } catch (Exception e) {
    throw ExceptionUtil.unwrapThrowable(e);
    }
    }
    //处理拦截器类上的各种注解
    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
    throw new PluginException("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);
    }
    try {
    Method method = sig.type().getMethod(sig.method(), sig.args());
    methods.add(method);
    } catch (NoSuchMethodException e) {
    throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
    }
    }
    return signatureMap;
    }
    }
    //拦截器的请求
    public class Invocation {
    private Object target;
    private Method method;
    private Object[] args;
    //调用原先真实方法(非代理)
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
    }
    }
    拦截器链执行过程

    拦截器代理类对象->拦截器->目标方法,即:InvocationHandler.invoke()->Interceptor.intercept()->Invocation.proceed()。

    也就是说其实在执行每个Executor、ParameterHandler、ResultSetHandler、StatementHandler都会构建一个拦截器链,如果针对每个配置了plugin,一个sql执行下来会陆续经过四个拦截器链。

    //拦截器管理类
    public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
    //构建责任链
    public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {//存在拦截器则构建
    //有多个同类型的拦截器则构成链,将代理类作为target再成代理类
    target = interceptor.plugin(target);
    }
    return target;//可以没有拦截器
    }
    public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
    }
    public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
    }
    }
    //重要的配置类
    public class Configuration {
    protected final InterceptorChain interceptorChain = new InterceptorChain();
    //有匹配的拦截器则构建责任链,没有则不生成
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
    }
    //同newParameterHandler
    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;
    }
    //同newParameterHandler
    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;
    }

    public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
    }
    //同newParameterHandler
    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;
    }
    }
    //负责解析Mybatis 全局配置文件
    public class XMLConfigBuilder extends BaseBuilder {
    private void pluginElement(XNode parent) throws Exception {//解析interceptor
    if (parent != null) {
    for (XNode child : parent.getChildren()) {
    String interceptor = child.getStringAttribute("interceptor");
    Properties properties = child.getChildrenAsProperties();
    Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
    interceptorInstance.setProperties(properties);
    configuration.addInterceptor(interceptorInstance);
    }
    }
    }
    }
    public class SimpleExecutor extends BaseExecutor {
    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
    Configuration configuration = ms.getConfiguration();
    //生成责任链
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);//可拦截query()
    } finally {
    closeStatement(stmt);
    }
    }
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());//可拦截prepare()
    handler.parameterize(stmt);//可拦截parameterize(http://www.my516.com)
    return stmt;
    }
    }
    mybatis用拦截器的设计非常巧妙的利用了jdk的动态代理,其实复杂度并不高。
    ---------------------

  • 相关阅读:
    MOSS中的User的Title, LoginName, DisplayName, SID之间的关系
    如何在Network Monitor中高亮间隔时间过长的帧?
    SharePoint服务器如果需要安装杀毒软件, 需要注意什么?
    如何查看SQL Profiler? 如何查看SQL死锁?
    什么是Telnet
    The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain.
    Windows SharePoint Service 3.0的某个Web Application无搜索结果
    网络连接不上, 有TCP错误, 如果操作系统是Windows Server 2003, 请尝试一下这里
    在WinDBG中查看内存的命令
    The virtual machine could not be started because the hypervisor is not running
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11102143.html
Copyright © 2011-2022 走看看