Mybatis源码阅读(一):Mybatis初始化1.2 —— 解析别名、插件、对象工厂、反射工具箱、环境

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
SimpleExecutor
接上一节 上一节:解析properties和settings

解析typeAliases

typeAliases节点用于配置别名。别名在mapper中使用resultType时会使用到,是对实体类的简写。

别名有两种配置方式

1.通过package,直接扫描指定包下所有的类,注册别名
2.通过typeAliase,指定某个类为其注册别名
别名注册代码如下

/*** 解析typeAliases节点** @param parent*/private void typeAliasesElement(XNode parent) {if (parent != null) {// 遍历所有子节点// typeAliases节点有两个子节点,分别是package和typeAliasfor (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {// 获取name属性,package的name属性指定的是包名String typeAliasPackage = child.getStringAttribute("name");// 将这个包下的所有类注册别名configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} else {// 如果配置的是typeAlias节点,就将该节点的类单独注册String alias = child.getStringAttribute("alias");String type = child.getStringAttribute("type");try {Class<?> clazz = Resources.classForName(type);if (alias == null) {typeAliasRegistry.registerAlias(clazz);} else {typeAliasRegistry.registerAlias(alias, clazz);}} catch (ClassNotFoundException e) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}}

注册别名

扫包后获取到包下所有的类之后,会为这些类生成别名,并将其注册到Configuration中

/*** 指定包名,将这个包下所有的类都注册别名** @param packageName*/public void registerAliases(String packageName) {registerAliases(packageName, Object.class);}/*** 为指定包下所有的类注册别名** @param packageName* @param superType*/public void registerAliases(String packageName, Class<?> superType) {ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();// 扫描指定包下所有继承了superType的类resolverUtil.find(new ResolverUtil.IsA(superType), packageName);// 获取匹配到的所有的类Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();for (Class<?> type : typeSet) {// 过滤掉内部类、接口、抽象类if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {registerAlias(type);}}}/*** 注册指定类的别名* @param type*/public void registerAlias(Class<?> type) {// 得到类的简写名称,即不带包名的名称// 因此在mybatis扫描包下,不允许有同样类名的类存在// 否则在启动时就会报错String alias = type.getSimpleName();Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {// 如果有Alias注解,就以Alias注解指定的别名为准// 该注解可以用于解决被扫描包下含有相同名称类的问题alias = aliasAnnotation.value();}registerAlias(alias, type);}/*** 注册别名* @param alias 别名* @param value 指定的类*/public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// 别名转为小写String key = alias.toLowerCase(Locale.ENGLISH);// 如果已经有了这个别名,并且这个别名中取到的值不为null,并且取到的值和传进来的类不相同就报错if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");}// 将别名放到typeAliases中。key是别名,因此别名不可以重复typeAliases.put(key, value);}

在注册别名时,会使用到ResolverUtil工具类。该工具类可以根据指定的条件去查找指定包下的类。该类有个内部接口Test,接口中只有一个matches方法,用于根据指定的规则去匹配。Test接口有两个实现。ISA用于检测该类是否继承了指定的类或者接口,而AnnotatedWith则用于检测是否添加了指定的注释,代码比较简单这里就不贴了。这里使用到了find方法,用于匹配指定包下所有继承了superType的类

public ResolverUtil<T> find(Test test, String packageName) {String path = getPackagePath(packageName);try {// 获取指定包下所有的文件名List<String> children = VFS.getInstance().list(path);for (String child : children) {if (child.endsWith(".class")) {addIfMatching(test, child);}}} catch (IOException ioe) {log.error("Could not read package: " + packageName, ioe);}return this;}/*** 如果匹配成功,就添加到matches中** @param test the test used to determine if the class matches* @param fqn  the fully qualified name of a class*/@SuppressWarnings("unchecked")protected void addIfMatching(Test test, String fqn) {try {String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');ClassLoader loader = getClassLoader();if (log.isDebugEnabled()) {log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");}Class<?> type = loader.loadClass(externalName);if (test.matches(type)) {matches.add((Class<T>) type);}} catch (Throwable t) {log.warn("Could not examine class '" + fqn + "'" + " due to a " +t.getClass().getName() + " with message: " + t.getMessage());}}

解析plugins

mybatis拥有强大的插件机制,可以通过配置mybatis拦截器来统一对sql、参数、返回集等进行处理,该功能广泛运用与分页、创建人等字段赋值、逻辑删除、乐观锁等插件的编写中。mybatis的拦截器编写难度比spring mvc高得多,想要熟练地编写mybatis拦截器,需要对源码比较熟悉。

解析拦截器的代码比较简单,plugin节点需要配置一个interceptor属性,该属性是自定义拦截器的全类名。在该方法中会先获取到该属性,通过该属性对应拦截器的默认构造去创建实例,并添加到Configuration中。

 /*** 解析plugins节点* plugin节点用于配置插件* 即 mybatis拦截器** @param parent* @throws Exception*/private void pluginElement(XNode parent) throws Exception {if (parent != null) {// 获取子节点,子节点就是所配置的拦截器for (XNode child : parent.getChildren()) {// 获得拦截器全类名String interceptor = child.getStringAttribute("interceptor");// 将节点下的节点封装成propertiesProperties properties = child.getChildrenAsProperties();// 根据拦截器的全类名,通过默认构造方法创建一个实例Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();// 将拦截器节点下的properties放到拦截器中interceptorInstance.setProperties(properties);// 将拦截器添加到配置中configuration.addInterceptor(interceptorInstance);}}}

解析ObjectFactory节点

objectFactory用来处理查询得到的结果集,创建对象去将结果集封装到对象中。

mybatis默认的对象工厂是用无参构造或者有参构造去创建对象,而如果开发者想在创建对象前对其进行一些初始化操作或者处理一些业务方面的逻辑,就可以自定义对象工厂并进行配置。对象工厂的解析比较简单,拿到type属性去创建一个实例并添加到Configuration即可。

/*** 解析objectFactory节点* objectFactory用来处理查询得到的结果集* 创建对象去将结果集封装到对象中* mybatis默认的object工厂是用无参构造或者有参构造去创建对象* 而如果开发者想在创建对象前对其中的一些属性做初始化操作* 或者做一些业务方面的逻辑* 就可以自己去创建对象工厂并进行配置** @param context* @throws Exception*/private void objectFactoryElement(XNode context) throws Exception {if (context != null) {// 拿到objectFactory节点的type属性,该属性为对象工厂的全类名String type = context.getStringAttribute("type");// 拿到节点下所有的propertiesProperties properties = context.getChildrenAsProperties();// 根据type对应的类,通过默认构造去创建一个实例ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();// 将properties放入对象工厂factory.setProperties(properties);// 将对象工厂添加到配置中去configuration.setObjectFactory(factory);}}

解析objectWrapperFactory和reflectorFactory

这两个节点的解析很简单,这里只贴上代码给读者去阅读,很容易就能理解。

/*** 解析objectWrapperFactory节点** @param context* @throws Exception*/private void objectWrapperFactoryElement(XNode context) throws Exception {if (context != null) {// 获取到type属性String type = context.getStringAttribute("type");// 根据type属性对应的类去创建对象ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();// 将对象放到配置中configuration.setObjectWrapperFactory(factory);}}/*** 解析reflectorFactory节点。代码比较简单就不写了。* 解析流程和objectWrapperFactory一毛一样** @param context* @throws Exception*/private void reflectorFactoryElement(XNode context) throws Exception {if (context != null) {String type = context.getStringAttribute("type");ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();configuration.setReflectorFactory(factory);}}

处理settings节点

在前一篇文章,已经将解析settings节点的代码讲解完毕,该方法则是用来将解析后的settings节点中的配置,一一添加到Configuration。代码简单粗暴,就是一堆set

/*** 将settings节点的配置set到Configuration中** @param props*/private void settingsElement(Properties props) {configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));configuration.setLogPrefix(props.getProperty("logPrefix"));configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));}

解析environments

该节点标识环境配置。所谓环境,就是只运行时需要的一系列参数,也可以理解成开发中常说的“开发环境”“测试环境”“生产环境”。环境最直观的就是在不同环境下连接不同的数据库。

environments节点下提供了数据源和事务配置。

/*** 解析environments节点* 该节点表示环境配置* 所谓环境,就是指运行时环境,即开发环境、测试环境、生产环境* 环境最直观的提现就是在不同环境下数据库不同* environments节点下就提供了数据源和事务配置** @param context* @throws Exception*/private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {// 获取默认的环境idenvironment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {// 拿到子节点的id。父节点的default属性对应的就是子节点idString id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {// 解析事务管理器TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));// 解析数据工厂DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));// 从工厂中拿到数据库DataSource dataSource = dsFactory.getDataSource();// 创建环境并set到Configuration中去Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}

解析的逻辑中,需要一并解析事务工厂和DataSource工厂。代码比较简单,和objectWrapperFactory一样,这里就不贴了

解析databaseIdProvider

该节点用于提供多数据源支持。这里的多数据源并非指多个数据库,而是指多个数据库产品。这里的代码和objectWrapperFactory比较类似,不做过多解释。

 /*** 解析databaseIdProvider节点* 该节点用于提供多数据源支持* 这里的多数据源并非指多个数据库* 而是指多个数据库产品* 这里的代码和objectFactory类似** @param context* @throws Exception*/private void databaseIdProviderElement(XNode context) throws Exception {DatabaseIdProvider databaseIdProvider = null;if (context != null) {String type = context.getStringAttribute("type");// awful patch to keep backward compatibilityif ("VENDOR".equals(type)) {type = "DB_VENDOR";}Properties properties = context.getChildrenAsProperties();databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();databaseIdProvider.setProperties(properties);}// 获取当前环境Environment environment = configuration.getEnvironment();if (environment != null && databaseIdProvider != null) {String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());configuration.setDatabaseId(databaseId);}}

结语

昨天加班填坑加到了12点,就没有继续为代码加注释,今天在空闲的时候就继续填坑了。目前对mybatis-config.xml文件的解析基本接近尾声,还差typeHandlers和mappers两个节点没有进行注释。相信看了这两篇文章的读者对于解析配置文件的逻辑已经有了一定的理解,因此自己阅读后面两个节点的代码解析应该不难。明天或者后天会将最后的两个节点的解析补上

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
SimpleExecutor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Google 修改 Chrome API,防止隐身模式检测

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; 在使用 Chrome 浏览网页时&#xff0c;某些网站会使用某种方法来确定访问者是否处于隐身模式&#xff0c;这是一种隐私泄漏行为。Google 目前正在考虑修改 Chrome 的相关 API&#xff0c;来杜绝…

Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

JavaScript异步基础

唯一比不知道代码为什么崩溃更可怕的事情是&#xff0c;不知道为什么一开始它是工作的&#xff01;在 ECMA 规范的最近几次版本里不断有新成员加入&#xff0c;尤其在处理异步的问题上&#xff0c;更是不断推陈出新。然而&#xff0c;我们在享受便利的同时&#xff0c;也应该了…

Flutter、ReactNative、uniapp对比

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

多线程中ThreadLocal的使用

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

注解版poi操作工具

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Kali Linux 2019.1 发布,Metasploit 更新到 5.0 版本

百度智能云 云生态狂欢季 热门云产品1折起>>> Kali Linux 2019.1 发布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一个基于 Debian 的 Linux 发行版&#xff0c;主要用于信息安全行业&#xff0c;其包含了一系列安全、渗透测试和取证工具。此版本 Linux 内核…

peewee mysql_scrapy中利用peewee插入Mysql

前两天老大布置一个任务&#xff0c;说爬下来的数据要存入数据库中&#xff0c;丢给我一个peewee&#xff0c;说用这个。当时的我两眼一抹黑&#xff0c;这是个什么东西呀&#xff0c;我知道scrapy的数据存入数据库是在pipelines中进行设置但是peewee是什么东西呢。经过两天不懈…

Java版数据结构与算法——线性表

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

基于 CODING 的 Spring Boot 持续集成项目

本文作者&#xff1a;CODING 用户 - 廖石荣 持续集成的概念 持续集成(Continuous integration,简称 CI&#xff09;是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;通常每个成员每天至少集成一次&#xff0c;也就意味着每天可能会发生多次集成。每…

Mybatis组成部分

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

一年java工作经验-面试总结

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

linux mysql python包_03_mysql-python模块, linux环境下python2,python3的

---恢复内容开始---1、Python2 正常[rootIP ~]#pip install mysql-pythonDEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 wont be maintained after that date. A future version of pip will drop …

两年Java工作经验应该会些什么技术

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

centos 6 mysql 5.7.13 编译安装_Centos 6.5 下面 源码编译 安装 Mysql 5.7.13

安装软件依赖包yum -y install gcc gcc-c ncurses ncurses-devel cmake下载软件包cd /usr/local/srcwget https://downloads.mysql.com/archives/get/file/mysql-5.7.13.tar.gz --no-check-certificate下载 boost 库&#xff0c;MySQL 5.7.5 开始Boost库是必需的cd /usr/loca…

LeetCode 237. 删除链表中的节点(Python3)

题目&#xff1a; 请编写一个函数&#xff0c;使其可以删除某个链表中给定的&#xff08;非末尾&#xff09;节点&#xff0c;你将只被给定要求被删除的节点。 现有一个链表 -- head [4,5,1,9]&#xff0c;它可以表示为: 示例 1: 输入: head [4,5,1,9], node 5 输出: [4,1,9…

一年Java经验应该会些什么

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

mysql查询各类课程的总学分_基于jsp+mysql的JSP学生选课信息管理系统

运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。IDE环境&#xff1a; Eclipse,Myeclipse,IDEA都可以硬件环境&#xff1a; windows 7/8/10 2G内存以上(推荐4G&#xff0c;4G以上更好)可以实现&#xff1a; 学生&#xff0c;教师角色的…

三年java经验应该会什么?

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

python加法运算符可以用来连接字符串并生成新字符串_中国大学MOOCPython语言入门网课答案...

中国大学MOOCPython语言入门网课答案表达式int(40.5)的值为____________。表达式160.5的值为____________________。python程序只能使用源代码进行运行&#xff0c;不能打包成可执行文件。python语句list(range(1,10,3))执行结果为___________________。pip命令也支持扩展名为.…