mybatis源码配置文件解析之四:解析plugins标签
在前边的博客在分析了mybatis解析typeAliases标签,《mybatis源码配置文件解析之三:解析typeAliases标签》。下面来看解析plugins标签的过程。 一、概述在mybatis的核心配置文件(mybatis-config.xml)文件中,有关plugins的配置如下, <!-- 拦截器 --> <plugins> plugin interceptor="cn.com.mybatis.plugins.MyInterceptor" /> </> 在mybatis的plugins叫做插件,其实也可以理解为拦截器。在plugins标签中配置plugin子标签,plugin子标签可以配置多个,多个子标签是有顺序的。除了上面的配置方式在每个plugin下还可以配置property子标签, /> ="cn.com.mybatis.plugins.MyInterceptor2"property name="name" value="mybatis"="age"="10"plugin> > 上面的配置在第二个拦截器中新增了两个属性name和age,这两个属性会被解析为Properties对象。 在上面可以看到一个重要的配置是plugin标签中的interceptor属性,该属性配置的是一个拦截器类,对应该类有什么要求那,一个普通的类就可以吗,答案是就是一个普通的类,但必须满足下面的几个条件,才可以被mybatis作为插件使用,
@Intercepts({@Signature(type=Executor.class,method="query",args= {MappedStatement.class})}) 拦截的是实现了Executor接口的query方法,后面的args配置的该方法的入参。在mybatis中默认拦截以下接口的方法, Executor (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
ParameterHandler (getParameterObject,setParameters)
ResultSetHandler (handleResultSets,handleOutputParameters)
StatementHandler (prepare,parameterize,batch,update,query)
Executor(执行器)、ParameterHandler(参数处理器)、ResultSetHandler(结果集处理器)、StatementHandler(SQL语法构建器)在mybatis中是四个顶级接口,贯穿了mybatis执行sql的全过程,从这里可以看出mybatis的拦截器的强大。 下面看下我的拦截器MyInterceptor的源码, package cn.com.mybatis.plugins; import java.util.Properties; org.apache.ibatis.executor.Executor; org.apache.ibatis.mapping.MappedStatement; org.apache.ibatis.plugin.Interceptor; org.apache.ibatis.plugin.Intercepts; org.apache.ibatis.plugin.Invocation; org.apache.ibatis.plugin.Plugin; org.apache.ibatis.plugin.Signature; org.apache.ibatis.session.ResultHandler; org.apache.ibatis.session.RowBounds; @Intercepts({@Signature(type=Executor.class})}) public class MyInterceptor implements Interceptor{ /** * incation 封装了拦截的类,方法,方法参数 */ @Override public Object intercept(Invocation invocation) throws Throwable { // TODO Auto-generated method stub 执行被拦截的类中的方法 Object o=invocation.proceed(); return o; } * 返回一个JDK代理对象 public Object plugin(Object target) { TODO Auto-generated method stub return Plugin.wrap(target,this); } * 允许在配置文件中配置一些属性即 * <plugin interceptor=""> * <property name ="" value =""/> * </plugin> void setProperties(Properties properties) { TODO Auto-generated method stub } } ? 二、详述上面,了解了plugis标签的基本配置及使用,下面看mybatis是如何解析的。 在XMLConfigBuilder类中的parseConfiguration方法 private parseConfiguration(XNode root) { try { issue #117 read properties first 解析properties标签 propertiesElement(root.evalNode("properties")); 解析settings标签,1、把<setting>标签解析为Properties对象 Properties settings = settingsAsProperties(root.evalNode("settings"/*2、对<settings>标签中的<setting>标签中的内容进行解析,这里解析的是<setting name="vfsImpl" value=","> * VFS是mybatis中用来表示虚拟文件系统的一个抽象类,用来查找指定路径下的资源。上面的key为vfsImpl的value可以是VFS的具体实现,必须 * 是权限类名,多个使用逗号隔开,如果存在则设置到configuration中的vfsImpl属性中,如果存在多个,则设置到configuration中的仅是最后一个 * loadCustomVfs(settings); 解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/> typeAliasesElement(root.evalNode("typeAliases"解析插件标签 pluginElement(root.evalNode("plugins"解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置 则默认使用DefaultObjectFactory来创建,设置之后使用设置的 objectFactoryElement(root.evalNode("objectFactory"解析objectWrapperFactory标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"解析reflectorFactory标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); read it after objectFactory and objectWrapperFactory issue #631 解析environments标签 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers"解析<mappers>标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e,e); } } 从上面的代码中,我们找到下面这行解析plugins标签的代码, 解析插件标签
pluginElement(root.evalNode("plugins"));
上面折行代码即在解析配置文件中的plugis标签,其定义如下 * 解析<plugin>标签 * @param parent * @throws Exception */ void pluginElement(XNode parent) Exception { if (parent != null) { for (XNode child : parent.getChildren()) { 1、解析标签从上面的代码中可以看到在循环plugin标签,取得interceptor配置的类的全限类名,然后获得plugin标签下的property标签的内容,调用getChildrenAsProperties方法, Properties getChildrenAsProperties() { Properties properties = new Properties(); (XNode child : getChildren()) { String name = child.getStringAttribute("name"); String value = child.getStringAttribute("value"); if (name != null && value != ) { properties.setProperty(name,value); } } properties; } 解析完property标签后,下面会生成interceptor实例。 2、生成interceptor实例上面解析完plugin标签的内容后会生成interceptor实例,且调用其setProperty方法, 执行interceptor的setProperties方法,把配置的properties标签中的值设置到setProperties中
interceptorInstance.setProperties(properties);
?从上面的代码可以看到把配置的interceptor全限类名解析为一个Class对象,然后调用其默认的构造方法获得一个实例,然后调用了其setProperties方法,即把在标签中配置的property属性值注入到interceptor实例中,在我的例子中即调用下面的方法, this.properties=properties; } 这样就可以把标签中的内容转化到interceptor实例中的属性。 ?3、添加到configuration中经过上面的两个步骤的分析,拦截器已经从配置文件中的配置转化为了一个实例,下面就是要放入核心配置类configuration中, configuration.addInterceptor(interceptorInstance); 看addInterceptor方法,该方法是configuration类中的方法, addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
调用了interceptorChain的addInterceptor方法,interceptorChain使用下面的方式进行初始化, 拦截器链 protected final InterceptorChain interceptorChain = new InterceptorChain(); 其addInterceptor方法如下, 可以看到最终放到了interceptors中,这是一个ArrayList。 最终完成了解析plugins标签并把拦截器实例放到configuration中的过程。 三、总结本文分析了mybatis中plugins标签的解析过程,该标签的解析过程相对简单,重点是拦截器背后的原理,是如何起到拦截作用,待下次分析。 ? 有不正之处,欢迎指正,感谢! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |