常见的网站空间主要有,南昌专业制作网站,江苏网站建设找拉米拉,天眼查企业查询官网登录*************************************优雅的分割线 **********************************
分享一波:程序员赚外快-必看的巅峰干货
如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程
请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…*************************************优雅的分割线 **********************************
分享一波:程序员赚外快-必看的巅峰干货
如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程
请关注微信公众号: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) {ResolverUtilClass? resolverUtil new ResolverUtil();// 扫描指定包下所有继承了superType的类resolverUtil.find(new ResolverUtil.IsA(superType), packageName);// 获取匹配到的所有的类SetClass? 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 ResolverUtilT find(Test test, String packageName) {String path getPackagePath(packageName);try {// 获取指定包下所有的文件名ListString 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((ClassT) 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