优秀的编程知识分享平台

网站首页 > 技术文章 正文

Mybatis插件(1) - 解析plugins(mybatis mapper插件)

nanyue 2024-08-21 19:46:02 技术文章 4 ℃

1.XMLConfigBuilder中解析plugins

XMLConfigBuilder的parseConfiguration中,通过XPath解析Document的plugins节点得到XNode对象,然后调用pluginElement方法进行处理

    private void parseConfiguration(XNode root) {
    	...
		this.pluginElement(root.evalNode("plugins"));
		...
    }

pluginElement方法中,获取plugins的所有子配置,然后依次进行处理

获取插件的配置interceptor和属性,然后通过反射的方式进行实例化,然后强转为Interceptor类型,并使用Properties对Interceptor属性进行赋值,最后添加到Configuration中

    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String interceptor = child.getStringAttribute("interceptor");
                Properties properties = child.getChildrenAsProperties();
                Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).getDeclaredConstructor().newInstance();
                interceptorInstance.setProperties(properties);
                this.configuration.addInterceptor(interceptorInstance);
            }
        }
    }

resolveClass方法是通过别名字符串获取到Configuration中的typeAliasRegistry获取对应的类对象,得到后即可通过反射获取构造方法来进行实例化

    protected <T> Class<? extends T> resolveClass(String alias) {
        if (alias == null) {
            return null;
        } else {
            try {
                return this.resolveAlias(alias);
            } catch (Exception var3) {
                throw new BuilderException("Error resolving class. Cause: " + var3, var3);
            }
        }
    }

2.Interceptor接口

由上述过程可以知道,配置的Plugins - interceptor在创建对象后,会强制转型为Interceptor类型,Interceptor是一个接口,包括以下三个方法

通过setProperties方法将配置的属性进行赋值

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

    default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
    }
}

3.添加到Configuration

最后通过addInterceptor将创建的Interceptor添加到interceptorChain中,interceptorChain是InterceptorChain类型的实例

public class Configuration {
	protected final InterceptorChain interceptorChain;

    public Configuration() {
        ...
        this.interceptorChain = new InterceptorChain();
		...
	}

    public void addInterceptor(Interceptor interceptor) {
        this.interceptorChain.addInterceptor(interceptor);
    }
	...
}

4.InterceptorChain类

InterceptorChain中有一个Interceptor的集合对象,

public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList();

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

5.Interceptor执行流程

InterceptorChain在pluginAll方法中会依次调用Interceptor的plugin方法对传入的参数进行处理

    public Object pluginAll(Object target) {
        Interceptor interceptor;
        for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
            interceptor = (Interceptor)var2.next();
        }
        return target;
    }

Interceptor中的plugin方法通过Plugin的warp来执行,传入参数target和Interceptor的引用

	default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

Plugin实现了Java动态代理接口InvocationHandler,在wrap方法中通过动态代理方式创建对象,代理对象就是其自身的实例

最后在方法执行时,会先判断signatureMap有没有该方法,有的话就调用interceptor中的intercept方法,否则正常执行方法即可

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

    private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = 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);
        return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
            return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
        } catch (Exception var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }
}

getSignatureMap方法中对Interceptor的注解进行解析为signatureMap

    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        Intercepts interceptsAnnotation = (Intercepts)interceptor.getClass().getAnnotation(Intercepts.class);
        if (interceptsAnnotation == null) {
            throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
        } else {
            Signature[] sigs = interceptsAnnotation.value();
            Map<Class<?>, Set<Method>> signatureMap = new HashMap();
            Signature[] var4 = sigs;
            int var5 = sigs.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Signature sig = var4[var6];
                Set methods = (Set)signatureMap.computeIfAbsent(sig.type(), (k) -> {
                    return new HashSet();
                });

                try {
                    Method method = sig.type().getMethod(sig.method(), sig.args());
                    methods.add(method);
                } catch (NoSuchMethodException var10) {
                    throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + var10, var10);
                }
            }
            return signatureMap;
        }
    }

6.拦截器的执行

查看Configuration中的代码,一下四处代码中调用InterceptorChain的pluginAll方法,也就是说插件暂时只支持处理Executor,ParameterHandler,ResultSetHandler,StatementHandler四个参数

7.总结

Mybatis插件是通过拦截器来实现的,自定义插件时,可以配置对Mybatis四种类型的的方法进行拦截,然后在对象执行拦截方法时,对调用Interceptor的intercept方法,我们可以通过自定义拦截器重写intercept方法来实现对应的业务逻辑,而拦截器则是通过Java动态代理实现

后续对于Mybatis插件的开发,先要熟悉Executor,ParameterHandler,ResultSetHandler,StatementHandler四个组件的方法的作用与调用时机,才可对具体方法添加拦截器以实现附加功能

Tags:

最近发表
标签列表