您的位置:首页 > 其它

修改MyBatis源码实现扫描注册枚举-分析源码

2016-06-07 01:24 686 查看

前言

             在上一篇中mybatis枚举自动转换(通用转换处理器实现),我们知道了可以定义一个万能的枚举装换器来转换成我们想要的枚举类型。但是我们觉得还是不够方便,为什么呢,虽然我们定义了一个万能的枚举转换器,但是我们还是需要为每一个我们的枚举注册类型处理器(typeHandler),然而想到我们项目中有上百个枚举的时候,我就想到要吐血,特别是项目经理在催你赶进度的时候,那么多配置要写,这么无聊又多的typeHandler需要注册,要是能变成扫描注册那该多好啊,不然的话,一个项目里面的mybatis的配置文件中,要写一百多个typeHandler,想想就受罪。所以趁着业余时间,我就开始了分析源码,下载源码,修改源码,然后编译打包,然后融入到项目中使用。

            首先我们先来看看,如果想要为万能枚举转换处理器实现扫描注册枚举,不需要在mybatis的配置文件中再一行行的添加typeHandler,我们应该从哪里下手,不知道的话,我们先来看看源码,以及mybatis为typeHandler注册的实现逻辑。

typeHandler的注册

           typeHandler的注册是随着SqlSessionFactory的创建时候而注册的,所以它注册的入口是在SqlSessionFactoryBean类的afterPropertiesSet方法,由于SqlSessionFactoryBean实现了InitializingBean接口,所以当spring容器启动的时候,便会调用afterPropertiesSet方法,那么我们也从这个方法开始查看它的调用逻辑,先我们来看看整体的调用时序图:



重点步骤说明:

1)我们先看XMLConfigBuilder类下面的parseConfiguration方法,可以看到,这个方法就是解析各个mybatis配置文件中各个元素的入口,在下面我们就可以看到解析typeHandler的程序入口,让我们跟着入口继续往下看。

private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}


2)typeHandlers标签下面,可以放package元素,也可以放typeHandler元素,这两个只能选一个,在下面的程序逻辑就可以明显看出。

如果使用typeHandler标签,那么就会从typeHandler标签里面直接取javaType和Handler以及jdbcType出来注解为枚举注册类型转换处理器。

如果使用package注册,枚举,那么的话就会去扫描包名称下面的所有的类型转换处理器,及找到想要的枚举类型,然后为其注册。

注册完成之后,这些枚举以及对应的枚举转换处理器会注册在一个map里面,当需要使用的时候会直接从map里面取出来

private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}


好了,注册成功之后,我们在查询获取结果的时候,程序就会回调typeHandler的对应方法,从注册好的类型转换处理器中取出对应的typeHandler进行类型的转换,最后得到我们想要的结果。

 设计自动扫描枚举

             注册的大体流程我们可以说是了解了个大概了,那么如何实现我们想要的效果呢,一开始,我是不想改动mybatis的源码,我是直接想扩展spring,在spring容器初始化的时候我去截取到注册的方法,然后在注册的时候,实现自动添加项目中的枚举。但是经过一番挣扎后,其中尝试过,通过实现BeanDefinitionRegistryPostProcessor接口,还有ApplicationListener<ApplicationContextEvent>接口等方法做拦截,获取beanDefinition,但是无法再往已经注册好typeHandler的Map中插入(注册)typeHandler,里面已经封装死了。
             想了下,还是直接修改源码算了,方便快捷。修改的源码的实现的步骤其实比较简单,入手的方法就是typeHandlerElement方法,在里面我们添加多一个包扫描功能,为万能转换处理器注册所指定的包下面所有的枚举,具体怎么实现呢,mybatis源码怎么编译打包呢,下一篇我会告诉大家,并把源码生产的jar公开给大家。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: