1.目标
本章节的目标就是用户在xml配置了类,我们需要实现在某处拦截执行用户配置的实现类,而这部分功能就是mybaties的插件扩展。
插件主要的设计理念就是依赖倒置,插件功能依赖于接口,而不是具体的实现,不依赖具体实现,在这个过程中对抽象进行编程,不对实现进行编程,这样就降低了客户与实现模块间的耦合。
而技术实现就需要用动态代理,我们需要用户配置被代理对象,被代理的方法以及参数,框架进行拦截操作,通过配置的方式把实现添加到Mybaties中(如图一),这样在执行插件代理方法invoke()时,就可以直接实现对应的实现类。看图二,用户注解的配置中,StatementHandler当作代理对象,方法是prepare,参数是Connection,那么当我们执行StatementHandler和其子类时并且方法是prepare的化就会执行代理invoke(),此时就会判断调度到用户的实现类里。


2.xml类图
看xml类图说明,
1.由XMLConfigBuilder开始进行解析插件内容,为什么从这里开始,因为我们的配置在mybatis-config-datasource.xml中(如下图),解析完毕将拦截器(用户实现)对象放入Configuration的拦截器链条中(InterceptorChain),

2.在执行对应Sql之前(newStatementHandler方法处),我们在此设置包装了代理方法,代理的是PreparedStatementHandler,是StatementHandler的子类,进入包装代理方法就会读取到用户实现类的注释,如Intercepts以及Signature注释得到被代理的类和方法以及参数,最后返回代理类。
3.此时StatementHandler已经被代理,所以执行prepare()时直接执行Plugin类下的incoke()方法,这时就执行用户的实现操作,用户操作完放行,这时继续执行框架里的操作。
3.代码
3.1 xml插件解析
XMLConfigBuilder类里:此类添加了解析Plugin的方法,拿到了interceptor属性的插件实现类,以及对应的属性名称和值,最后将interceptor放入到拦截链条里(interceptorChain)
public class XMLConfigBuilder extends BaseBuilder {
// 省略其他方法...
public Configuration parse() {
try {
// 插件 step-16 添加
pluginElement(root.element("plugins"));
// 环境
environmentsElement(root.element("environments"));
// 解析映射器
mapperElement(root.element("mappers"));
} catch (Exception e) {
throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
return configuration;
}
// 解析插件
private void pluginElement(Element parent) throws Exception {
if (parent == null) return;
List<Element> elements = parent.elements();
for (Element element : elements) {
String interceptor = element.attributeValue("interceptor");
Properties properties = new Properties();
List<Element> propertyElementList = element.elements("property");
for (Element property : propertyElementList) {
properties.setProperty(property.attributeValue("name"), property.attributeValue("value"));
}
// 获取插件实现类并实例化:cn.bugstack.mybatis.test.plugin.TestPlugin
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(


被折叠的 条评论
为什么被折叠?



