MyBatis启动:SqlSessionFactory的建立过程
来源:互联网 发布:mysql 导入数据乱码 编辑:程序博客网 时间:2024/05/22 08:39
MyBatis的启动,就是建立SqlSessionFactory的过程,读取配置信息,通过会话工程建造者创建会话工厂。SqlSessionFactory接口定义了从数据源、链接中打开会话的方法以及获取配置信息的方法。以DefaultSqlSessionFactory为例,创建过程就是创建Configuration对象,赋值给工程对象。
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }创建Configuration对象的过程,是解析XML文件的过程,并且在过程中创建对象所依赖的对象。
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());
首先创建XMLConfigBuidler对象用来解析XML配置文件工作,这个对象中持有XPathParser对象,这个才是真正用来解析XML的工具类。底层采用JDK实现的XPath的API来对XML进行解析工作,此处不深入说明。重点关注Configuration对象的创建过程。
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }这个XML创建者将XML文档创建结束后,调用parse()方法进行解析,方法中调用parseConfiguration()方法对configuration节点进行解析。parser.evalNode其实就是解析XML文件返回代表configuration节点的XNode对象,然后再对XNode对象进行粒度更细的解析。
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); } }
解析configuration节点下的所有可能配置的子节点。首先是Properties,可以从文件中读取,也可以通过URL从网络中获取,还有就是从配置文件中获取。获取到的Properties对象将注入到configuration对象中。
private void propertiesElement(XNode context) throws Exception { if (context != null) { Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } }接下来解析类型的别名,可以配置包名,MyBatis会通过反射获取包下的所有类型,并且通过typeAliasRegistry注册类型的别名。这个TypeAliasRegistry内部持有一个Map,key用来保存别名alias,value用来保存类信息。比如:"byte", Byte.class。
private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { 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); } } } } }解析插件plugins。
private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } }
public <T> Class<T> resolveAlias(String string) { try { if (string == null) return null; String key = string.toLowerCase(Locale.ENGLISH); // issue #748 Class<T> value; if (TYPE_ALIASES.containsKey(key)) { value = (Class<T>) TYPE_ALIASES.get(key); } else { value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } }解析配置拦截器的名称,首先别名注册器上寻找,找到直接返回类信息Class对象,找不到则通过类加载器去编译路径找寻找对应的类信息。找到后调用newInstance得到实例对象。然后将拦截器添加到configuration对象的拦截器链上。
private void objectFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties properties = context.getChildrenAsProperties(); ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); factory.setProperties(properties); configuration.setObjectFactory(factory); } }解析对象工厂,并设置到configuration对象,可以覆盖原来对象工厂的行为。接下来是解析objectWrapperFactory节点,和解析工厂类似。
解析setting节点,这个将会改变configuration对象上的很多默认熟悉,也会改变mybatis的行为。
private void settingsElement(XNode context) throws Exception { if (context != null) { Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); 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"), true)); 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.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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty("injectionFilterEnabled"), false)); configuration.setInjectionFilter(parseExpression(props.getProperty("injectionFilter"), "^[a-zA-Z0-9._]*$")); } }接着解析environments这个比较重要,不仅是新建environments对象设置到configuration中,而且还将创建事务工厂对象,以及数据源对象。
private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }接下来解析databaseIdProvider,通过配置数据库供应商节点,可以在statement语句上标识对应数据库的语句,执行时将根据不同的数据库执行对应的语句。
解析typeHandlers,将处理Java类型和数据库类型的转换关系。解析TyptHandler的同样通过一个注册器来管理信息。
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); } } } } }
解析mappers。
private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
同样可以通过类路径查找,网络资源查找mapper的信息,然后使用mapper注册器注册。看一下最后一种方式,Class<?> mapperInterface = Resources.classForName(mapperClass);先加载类文件信息,然后调用configuration.addMapper(mapperInterface);来注册。而configuration中mapperRegistry.addMapper(type);通过注册器来注册。
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }mapper的注册也是放到一个map中,key是类型名称,value是类型的代理工厂。然后解析mapper的xml文件。关于mapper的创建过程,将在下一篇中分析。
- MyBatis启动:SqlSessionFactory的建立过程
- MyBatis启动:SqlSessionFactory的建立过程
- 在MyBatis中 SqlSessionFactory的创建过程
- Mybatis SqlSessionFactory创建过程
- mybatis 3.4.2 启动过程-配置文件的解析与SqlSessionFactory的获得
- mybatis的sqlsessionFactory
- mybatis源码分析——SqlSessionFactory实例的产生过程
- Mybatis 源码分析一、 SqlSessionFactory的创建过程
- MyBatis源码分析——SqlSessionFactory实例的产生过程
- MyBatis核心SqlSessionFactory的创建
- MyBatis核心SqlSessionFactory的创建
- MyBatis核心SqlSessionFactory的创建
- spring配置mybatis的sqlsessionfactory
- MyBatis SqlSessionFactory
- mybatis源码学习之执行过程分析(1)——SqlSessionFactory及SqlSession的创建
- mybatis源码分析(1)——SqlSessionFactory实例的产生过程
- mybatis的探索过程之SqlSessionFactoryBuilder,SqlSessionFactory,SqlSession作用域和生命周期
- MyBatis的SqlSessionFactory的创建问题
- 一个测试者的忏悔
- 【Qt】制作应用插件
- C# 对象与JSON串互相转换
- 【大数问题】 HDOJ 4927 Series 1
- MyEclipse 8.5 注册--取消MyEclipse Trial Expired解决办法
- MyBatis启动:SqlSessionFactory的建立过程
- Yale开放课程博弈论5
- naze32 MWC
- LeetCode-Partition List
- 动态添加文字
- 出现java.lang.UnsupportedClassVersionError 错误的原因
- OSG中抓取屏幕保存为图片
- 下拉菜单
- IOS学习笔记29—提示框第三方库之MBProgressHUD