转自:http://blog.youkuaiyun.com/xushucan/article/details/38986283
MyBatis的启动,就是建立SqlSessionFactory的过程,读取配置信息,通过会话工程建造者创建会话工厂。SqlSessionFactory接口定义了从数据源、链接中打开会话的方法以及获取配置信息的方法。以DefaultSqlSessionFactory为例,创建过程就是创建Configuration对象,赋值给工程对象。
- public SqlSessionFactory build(Configuration config) {
- return new DefaultSqlSessionFactory(config);
- }
- 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;
- }
- 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);
- }
- }
- 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);
- }
- }
- }
- }
- }
- 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);
- }
- }
- 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);
- }
- }
解析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._]*$"));
- }
- }
- 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());
- }
- }
- }
- }
解析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);
- }
- }
- }
- }