Enford 5 jaren geleden
bovenliggende
commit
a18099b57e

+ 487 - 0
src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java

@@ -0,0 +1,487 @@
+/*
+ *    Copyright 2009-2013 the original author or authors.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+package org.apache.ibatis.builder.xml;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ibatis.builder.BaseBuilder;
+import org.apache.ibatis.builder.BuilderException;
+import org.apache.ibatis.builder.CacheRefResolver;
+import org.apache.ibatis.builder.IncompleteElementException;
+import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.apache.ibatis.builder.ResultMapResolver;
+import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.mapping.Discriminator;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.ParameterMode;
+import org.apache.ibatis.mapping.ResultFlag;
+import org.apache.ibatis.mapping.ResultMap;
+import org.apache.ibatis.mapping.ResultMapping;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.parsing.XPathParser;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeHandler;
+
+/**
+ * @author Clinton Begin
+ * @description 增加解析方法,原方法不解析相同的resource
+ */
+public class XMLMapperBuilder extends BaseBuilder {
+
+	private XPathParser parser;
+	private MapperBuilderAssistant builderAssistant;
+	private Map<String, XNode> sqlFragments;
+	private String resource;
+
+	@Deprecated
+	public XMLMapperBuilder(Reader reader, Configuration configuration,
+			String resource, Map<String, XNode> sqlFragments, String namespace) {
+		this(reader, configuration, resource, sqlFragments);
+		this.builderAssistant.setCurrentNamespace(namespace);
+	}
+
+	@Deprecated
+	public XMLMapperBuilder(Reader reader, Configuration configuration,
+			String resource, Map<String, XNode> sqlFragments) {
+		this(new XPathParser(reader, true, configuration.getVariables(),
+				new XMLMapperEntityResolver()), configuration, resource,
+				sqlFragments);
+	}
+
+	public XMLMapperBuilder(InputStream inputStream,
+			Configuration configuration, String resource,
+			Map<String, XNode> sqlFragments, String namespace) {
+		this(inputStream, configuration, resource, sqlFragments);
+		this.builderAssistant.setCurrentNamespace(namespace);
+	}
+
+	public XMLMapperBuilder(InputStream inputStream,
+			Configuration configuration, String resource,
+			Map<String, XNode> sqlFragments) {
+		this(new XPathParser(inputStream, true, configuration.getVariables(),
+				new XMLMapperEntityResolver()), configuration, resource,
+				sqlFragments);
+	}
+
+	private XMLMapperBuilder(XPathParser parser, Configuration configuration,
+			String resource, Map<String, XNode> sqlFragments) {
+		super(configuration);
+		this.builderAssistant = new MapperBuilderAssistant(configuration,
+				resource);
+		this.parser = parser;
+		this.sqlFragments = sqlFragments;
+		this.resource = resource;
+	}
+
+	public void parse() {
+		if (!configuration.isResourceLoaded(resource)) {
+			configurationElement(parser.evalNode("/mapper"));
+			configuration.addLoadedResource(resource);
+			bindMapperForNamespace();
+		}
+
+		parsePendingResultMaps();
+		parsePendingChacheRefs();
+		parsePendingStatements();
+	}
+
+	// TODO 增加解析方法,原方法不解析相同的resource
+	public void parse1() {
+		// if (!configuration.isResourceLoaded(resource)) {
+		configurationElement(parser.evalNode("/mapper"));
+		configuration.addLoadedResource(resource);
+		bindMapperForNamespace();
+		// }
+
+		parsePendingResultMaps();
+		parsePendingChacheRefs();
+		parsePendingStatements();
+	}
+
+	public XNode getSqlFragment(String refid) {
+		return sqlFragments.get(refid);
+	}
+
+	private void configurationElement(XNode context) {
+		try {
+			String namespace = context.getStringAttribute("namespace");
+			if (namespace.equals("")) {
+				throw new BuilderException("Mapper's namespace cannot be empty");
+			}
+			builderAssistant.setCurrentNamespace(namespace);
+			cacheRefElement(context.evalNode("cache-ref"));
+			cacheElement(context.evalNode("cache"));
+			parameterMapElement(context.evalNodes("/mapper/parameterMap"));
+			resultMapElements(context.evalNodes("/mapper/resultMap"));
+			sqlElement(context.evalNodes("/mapper/sql"));
+			buildStatementFromContext(context
+					.evalNodes("select|insert|update|delete"));
+		} catch (Exception e) {
+			throw new BuilderException("Error parsing Mapper XML. Cause: " + e,
+					e);
+		}
+	}
+
+	private void buildStatementFromContext(List<XNode> list) {
+		if (configuration.getDatabaseId() != null) {
+			buildStatementFromContext(list, configuration.getDatabaseId());
+		}
+		buildStatementFromContext(list, null);
+	}
+
+	private void buildStatementFromContext(List<XNode> list,
+			String requiredDatabaseId) {
+		for (XNode context : list) {
+			final XMLStatementBuilder statementParser = new XMLStatementBuilder(
+					configuration, builderAssistant, context,
+					requiredDatabaseId);
+			try {
+				statementParser.parseStatementNode();
+			} catch (IncompleteElementException e) {
+				configuration.addIncompleteStatement(statementParser);
+			}
+		}
+	}
+
+	private void parsePendingResultMaps() {
+		Collection<ResultMapResolver> incompleteResultMaps = configuration
+				.getIncompleteResultMaps();
+		synchronized (incompleteResultMaps) {
+			Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
+			while (iter.hasNext()) {
+				try {
+					iter.next().resolve();
+					iter.remove();
+				} catch (IncompleteElementException e) {
+					// ResultMap is still missing a resource...
+				}
+			}
+		}
+	}
+
+	private void parsePendingChacheRefs() {
+		Collection<CacheRefResolver> incompleteCacheRefs = configuration
+				.getIncompleteCacheRefs();
+		synchronized (incompleteCacheRefs) {
+			Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
+			while (iter.hasNext()) {
+				try {
+					iter.next().resolveCacheRef();
+					iter.remove();
+				} catch (IncompleteElementException e) {
+					// Cache ref is still missing a resource...
+				}
+			}
+		}
+	}
+
+	private void parsePendingStatements() {
+		Collection<XMLStatementBuilder> incompleteStatements = configuration
+				.getIncompleteStatements();
+		synchronized (incompleteStatements) {
+			Iterator<XMLStatementBuilder> iter = incompleteStatements
+					.iterator();
+			while (iter.hasNext()) {
+				try {
+					iter.next().parseStatementNode();
+					iter.remove();
+				} catch (IncompleteElementException e) {
+					// Statement is still missing a resource...
+				}
+			}
+		}
+	}
+
+	private void cacheRefElement(XNode context) {
+		if (context != null) {
+			configuration.addCacheRef(builderAssistant.getCurrentNamespace(),
+					context.getStringAttribute("namespace"));
+			CacheRefResolver cacheRefResolver = new CacheRefResolver(
+					builderAssistant, context.getStringAttribute("namespace"));
+			try {
+				cacheRefResolver.resolveCacheRef();
+			} catch (IncompleteElementException e) {
+				configuration.addIncompleteCacheRef(cacheRefResolver);
+			}
+		}
+	}
+
+	private void cacheElement(XNode context) throws Exception {
+		if (context != null) {
+			String type = context.getStringAttribute("type", "PERPETUAL");
+			Class<? extends Cache> typeClass = typeAliasRegistry
+					.resolveAlias(type);
+			String eviction = context.getStringAttribute("eviction", "LRU");
+			Class<? extends Cache> evictionClass = typeAliasRegistry
+					.resolveAlias(eviction);
+			Long flushInterval = context.getLongAttribute("flushInterval");
+			Integer size = context.getIntAttribute("size");
+			boolean readWrite = !context.getBooleanAttribute("readOnly", false);
+			Properties props = context.getChildrenAsProperties();
+			builderAssistant.useNewCache(typeClass, evictionClass,
+					flushInterval, size, readWrite, props);
+		}
+	}
+
+	private void parameterMapElement(List<XNode> list) throws Exception {
+		for (XNode parameterMapNode : list) {
+			String id = parameterMapNode.getStringAttribute("id");
+			String type = parameterMapNode.getStringAttribute("type");
+			Class<?> parameterClass = resolveClass(type);
+			List<XNode> parameterNodes = parameterMapNode
+					.evalNodes("parameter");
+			List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
+			for (XNode parameterNode : parameterNodes) {
+				String property = parameterNode.getStringAttribute("property");
+				String javaType = parameterNode.getStringAttribute("javaType");
+				String jdbcType = parameterNode.getStringAttribute("jdbcType");
+				String resultMap = parameterNode
+						.getStringAttribute("resultMap");
+				String mode = parameterNode.getStringAttribute("mode");
+				String typeHandler = parameterNode
+						.getStringAttribute("typeHandler");
+				Integer numericScale = parameterNode
+						.getIntAttribute("numericScale");
+				ParameterMode modeEnum = resolveParameterMode(mode);
+				Class<?> javaTypeClass = resolveClass(javaType);
+				JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+				@SuppressWarnings("unchecked")
+				Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
+				ParameterMapping parameterMapping = builderAssistant
+						.buildParameterMapping(parameterClass, property,
+								javaTypeClass, jdbcTypeEnum, resultMap,
+								modeEnum, typeHandlerClass, numericScale);
+				parameterMappings.add(parameterMapping);
+			}
+			builderAssistant.addParameterMap(id, parameterClass,
+					parameterMappings);
+		}
+	}
+
+	private void resultMapElements(List<XNode> list) throws Exception {
+		for (XNode resultMapNode : list) {
+			try {
+				resultMapElement(resultMapNode);
+			} catch (IncompleteElementException e) {
+				// ignore, it will be retried
+			}
+		}
+	}
+
+	private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
+		return resultMapElement(resultMapNode,
+				Collections.<ResultMapping> emptyList());
+	}
+
+	private ResultMap resultMapElement(XNode resultMapNode,
+			List<ResultMapping> additionalResultMappings) throws Exception {
+		ErrorContext.instance().activity(
+				"processing " + resultMapNode.getValueBasedIdentifier());
+		String id = resultMapNode.getStringAttribute("id",
+				resultMapNode.getValueBasedIdentifier());
+		String type = resultMapNode.getStringAttribute("type", resultMapNode
+				.getStringAttribute("ofType", resultMapNode.getStringAttribute(
+						"resultType",
+						resultMapNode.getStringAttribute("javaType"))));
+		String extend = resultMapNode.getStringAttribute("extends");
+		Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
+		Class<?> typeClass = resolveClass(type);
+		Discriminator discriminator = null;
+		List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
+		resultMappings.addAll(additionalResultMappings);
+		List<XNode> resultChildren = resultMapNode.getChildren();
+		for (XNode resultChild : resultChildren) {
+			if ("constructor".equals(resultChild.getName())) {
+				processConstructorElement(resultChild, typeClass,
+						resultMappings);
+			} else if ("discriminator".equals(resultChild.getName())) {
+				discriminator = processDiscriminatorElement(resultChild,
+						typeClass, resultMappings);
+			} else {
+				ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
+				if ("id".equals(resultChild.getName())) {
+					flags.add(ResultFlag.ID);
+				}
+				resultMappings.add(buildResultMappingFromContext(resultChild,
+						typeClass, flags));
+			}
+		}
+		ResultMapResolver resultMapResolver = new ResultMapResolver(
+				builderAssistant, id, typeClass, extend, discriminator,
+				resultMappings, autoMapping);
+		try {
+			return resultMapResolver.resolve();
+		} catch (IncompleteElementException e) {
+			configuration.addIncompleteResultMap(resultMapResolver);
+			throw e;
+		}
+	}
+
+	private void processConstructorElement(XNode resultChild,
+			Class<?> resultType, List<ResultMapping> resultMappings)
+			throws Exception {
+		List<XNode> argChildren = resultChild.getChildren();
+		for (XNode argChild : argChildren) {
+			ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
+			flags.add(ResultFlag.CONSTRUCTOR);
+			if ("idArg".equals(argChild.getName())) {
+				flags.add(ResultFlag.ID);
+			}
+			resultMappings.add(buildResultMappingFromContext(argChild,
+					resultType, flags));
+		}
+	}
+
+	private Discriminator processDiscriminatorElement(XNode context,
+			Class<?> resultType, List<ResultMapping> resultMappings)
+			throws Exception {
+		String column = context.getStringAttribute("column");
+		String javaType = context.getStringAttribute("javaType");
+		String jdbcType = context.getStringAttribute("jdbcType");
+		String typeHandler = context.getStringAttribute("typeHandler");
+		Class<?> javaTypeClass = resolveClass(javaType);
+		@SuppressWarnings("unchecked")
+		Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
+		JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+		Map<String, String> discriminatorMap = new HashMap<String, String>();
+		for (XNode caseChild : context.getChildren()) {
+			String value = caseChild.getStringAttribute("value");
+			String resultMap = caseChild.getStringAttribute("resultMap",
+					processNestedResultMappings(caseChild, resultMappings));
+			discriminatorMap.put(value, resultMap);
+		}
+		return builderAssistant
+				.buildDiscriminator(resultType, column, javaTypeClass,
+						jdbcTypeEnum, typeHandlerClass, discriminatorMap);
+	}
+
+	private void sqlElement(List<XNode> list) throws Exception {
+		if (configuration.getDatabaseId() != null) {
+			sqlElement(list, configuration.getDatabaseId());
+		}
+		sqlElement(list, null);
+	}
+
+	private void sqlElement(List<XNode> list, String requiredDatabaseId)
+			throws Exception {
+		for (XNode context : list) {
+			String databaseId = context.getStringAttribute("databaseId");
+			String id = context.getStringAttribute("id");
+			id = builderAssistant.applyCurrentNamespace(id, false);
+			if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId))
+				sqlFragments.put(id, context);
+		}
+	}
+
+	private boolean databaseIdMatchesCurrent(String id, String databaseId,
+			String requiredDatabaseId) {
+		if (requiredDatabaseId != null) {
+			if (!requiredDatabaseId.equals(databaseId)) {
+				return false;
+			}
+		} else {
+			if (databaseId != null) {
+				return false;
+			}
+			// skip this fragment if there is a previous one with a not null
+			// databaseId
+			if (this.sqlFragments.containsKey(id)) {
+				XNode context = this.sqlFragments.get(id);
+				if (context.getStringAttribute("databaseId") != null) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	private ResultMapping buildResultMappingFromContext(XNode context,
+			Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
+		String property = context.getStringAttribute("property");
+		String column = context.getStringAttribute("column");
+		String javaType = context.getStringAttribute("javaType");
+		String jdbcType = context.getStringAttribute("jdbcType");
+		String nestedSelect = context.getStringAttribute("select");
+		String nestedResultMap = context.getStringAttribute(
+				"resultMap",
+				processNestedResultMappings(context,
+						Collections.<ResultMapping> emptyList()));
+		String notNullColumn = context.getStringAttribute("notNullColumn");
+		String columnPrefix = context.getStringAttribute("columnPrefix");
+		String typeHandler = context.getStringAttribute("typeHandler");
+		String resulSet = context.getStringAttribute("resultSet");
+		String foreignColumn = context.getStringAttribute("foreignColumn");
+		boolean lazy = "lazy".equals(context.getStringAttribute("fetchType",
+				configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
+		Class<?> javaTypeClass = resolveClass(javaType);
+		@SuppressWarnings("unchecked")
+		Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
+		JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
+		return builderAssistant.buildResultMapping(resultType, property,
+				column, javaTypeClass, jdbcTypeEnum, nestedSelect,
+				nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass,
+				flags, resulSet, foreignColumn, lazy);
+	}
+
+	private String processNestedResultMappings(XNode context,
+			List<ResultMapping> resultMappings) throws Exception {
+		if ("association".equals(context.getName())
+				|| "collection".equals(context.getName())
+				|| "case".equals(context.getName())) {
+			if (context.getStringAttribute("select") == null) {
+				ResultMap resultMap = resultMapElement(context, resultMappings);
+				return resultMap.getId();
+			}
+		}
+		return null;
+	}
+
+	private void bindMapperForNamespace() {
+		String namespace = builderAssistant.getCurrentNamespace();
+		if (namespace != null) {
+			Class<?> boundType = null;
+			try {
+				boundType = Resources.classForName(namespace);
+			} catch (ClassNotFoundException e) {
+				// ignore, bound type is not required
+			}
+			if (boundType != null) {
+				if (!configuration.hasMapper(boundType)) {
+					// Spring may not know the real resource name so we set a
+					// flag
+					// to prevent loading again this resource from the mapper
+					// interface
+					// look at MapperAnnotationBuilder#loadXmlResource
+					configuration.addLoadedResource("namespace:" + namespace);
+					configuration.addMapper(boundType);
+				}
+			}
+		}
+	}
+
+}

+ 881 - 0
src/main/java/org/apache/ibatis/session/Configuration.java

@@ -0,0 +1,881 @@
+/*
+ *    Copyright 2009-2014 the original author or authors.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+package org.apache.ibatis.session;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ibatis.binding.MapperRegistry;
+import org.apache.ibatis.builder.CacheRefResolver;
+import org.apache.ibatis.builder.ResultMapResolver;
+import org.apache.ibatis.builder.annotation.MethodResolver;
+import org.apache.ibatis.builder.xml.XMLStatementBuilder;
+import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.decorators.FifoCache;
+import org.apache.ibatis.cache.decorators.LruCache;
+import org.apache.ibatis.cache.decorators.SoftCache;
+import org.apache.ibatis.cache.decorators.WeakCache;
+import org.apache.ibatis.cache.impl.PerpetualCache;
+import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
+import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
+import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
+import org.apache.ibatis.executor.BatchExecutor;
+import org.apache.ibatis.executor.CachingExecutor;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.ReuseExecutor;
+import org.apache.ibatis.executor.SimpleExecutor;
+import org.apache.ibatis.executor.keygen.KeyGenerator;
+import org.apache.ibatis.executor.loader.ProxyFactory;
+import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
+import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
+import org.apache.ibatis.executor.resultset.ResultSetHandler;
+import org.apache.ibatis.executor.statement.RoutingStatementHandler;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
+import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
+import org.apache.ibatis.logging.log4j.Log4jImpl;
+import org.apache.ibatis.logging.log4j2.Log4j2Impl;
+import org.apache.ibatis.logging.nologging.NoLoggingImpl;
+import org.apache.ibatis.logging.slf4j.Slf4jImpl;
+import org.apache.ibatis.logging.stdout.StdOutImpl;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.Environment;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMap;
+import org.apache.ibatis.mapping.ResultMap;
+import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.InterceptorChain;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
+import org.apache.ibatis.reflection.factory.ObjectFactory;
+import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.apache.ibatis.scripting.LanguageDriverRegistry;
+import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
+import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
+import org.apache.ibatis.transaction.Transaction;
+import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
+import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
+import org.apache.ibatis.type.JdbcType;
+import org.apache.ibatis.type.TypeAliasRegistry;
+import org.apache.ibatis.type.TypeHandlerRegistry;
+
+/**
+ * @author Clinton Begin
+ * @description 重写put,实现刷新的功能
+ */
+public class Configuration {
+
+	protected Environment environment;
+
+	protected boolean safeRowBoundsEnabled = false;
+	protected boolean safeResultHandlerEnabled = true;
+	protected boolean mapUnderscoreToCamelCase = false;
+	protected boolean aggressiveLazyLoading = true;
+	protected boolean multipleResultSetsEnabled = true;
+	protected boolean useGeneratedKeys = false;
+	protected boolean useColumnLabel = true;
+	protected boolean cacheEnabled = true;
+	protected boolean callSettersOnNulls = false;
+	protected String logPrefix;
+	protected Class<? extends Log> logImpl;
+	protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
+	protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
+	protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(
+			Arrays.asList(new String[] { "equals", "clone", "hashCode",
+					"toString" }));
+	protected Integer defaultStatementTimeout;
+	protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
+	protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
+
+	protected Properties variables = new Properties();
+	protected ObjectFactory objectFactory = new DefaultObjectFactory();
+	protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
+	protected MapperRegistry mapperRegistry = new MapperRegistry(this);
+
+	protected boolean lazyLoadingEnabled = false;
+	protected ProxyFactory proxyFactory;
+
+	protected String databaseId;
+	/**
+	 * Configuration factory class. Used to create Configuration for loading
+	 * deserialized unread properties.
+	 * 
+	 * @see <a
+	 *      href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue
+	 *      300</a> (google code)
+	 */
+	protected Class<?> configurationFactory;
+
+	protected final InterceptorChain interceptorChain = new InterceptorChain();
+	protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
+	protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
+	protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
+
+	protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
+			"Mapped Statements collection");
+	protected final Map<String, Cache> caches = new StrictMap<Cache>(
+			"Caches collection");
+	protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>(
+			"Result Maps collection");
+	protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>(
+			"Parameter Maps collection");
+	protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>(
+			"Key Generators collection");
+
+	protected final Set<String> loadedResources = new HashSet<String>();
+	protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>(
+			"XML fragments parsed from previous mappers");
+
+	protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
+	protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
+	protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
+	protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
+
+	/*
+	 * A map holds cache-ref relationship. The key is the namespace that
+	 * references a cache bound to another namespace and the value is the
+	 * namespace which the actual cache is bound to.
+	 */
+	protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
+
+	public Configuration(Environment environment) {
+		this();
+		this.environment = environment;
+	}
+
+	public Configuration() {
+		typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
+		typeAliasRegistry.registerAlias("MANAGED",
+				ManagedTransactionFactory.class);
+
+		typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
+		typeAliasRegistry
+				.registerAlias("POOLED", PooledDataSourceFactory.class);
+		typeAliasRegistry.registerAlias("UNPOOLED",
+				UnpooledDataSourceFactory.class);
+
+		typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
+		typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
+		typeAliasRegistry.registerAlias("LRU", LruCache.class);
+		typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
+		typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
+
+		typeAliasRegistry.registerAlias("DB_VENDOR",
+				VendorDatabaseIdProvider.class);
+
+		typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
+		typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
+
+		typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
+		typeAliasRegistry.registerAlias("COMMONS_LOGGING",
+				JakartaCommonsLoggingImpl.class);
+		typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
+		typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
+		typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
+		typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
+		typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
+
+		typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
+		typeAliasRegistry.registerAlias("JAVASSIST",
+				JavassistProxyFactory.class);
+
+		languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
+		languageRegistry.register(RawLanguageDriver.class);
+	}
+
+	public String getLogPrefix() {
+		return logPrefix;
+	}
+
+	public void setLogPrefix(String logPrefix) {
+		this.logPrefix = logPrefix;
+	}
+
+	public Class<? extends Log> getLogImpl() {
+		return logImpl;
+	}
+
+	@SuppressWarnings("unchecked")
+	public void setLogImpl(Class<?> logImpl) {
+		if (logImpl != null) {
+			this.logImpl = (Class<? extends Log>) logImpl;
+			LogFactory.useCustomLogging(this.logImpl);
+		}
+	}
+
+	public boolean isCallSettersOnNulls() {
+		return callSettersOnNulls;
+	}
+
+	public void setCallSettersOnNulls(boolean callSettersOnNulls) {
+		this.callSettersOnNulls = callSettersOnNulls;
+	}
+
+	public String getDatabaseId() {
+		return databaseId;
+	}
+
+	public void setDatabaseId(String databaseId) {
+		this.databaseId = databaseId;
+	}
+
+	public Class<?> getConfigurationFactory() {
+		return configurationFactory;
+	}
+
+	public void setConfigurationFactory(Class<?> configurationFactory) {
+		this.configurationFactory = configurationFactory;
+	}
+
+	public boolean isSafeResultHandlerEnabled() {
+		return safeResultHandlerEnabled;
+	}
+
+	public void setSafeResultHandlerEnabled(boolean safeResultHandlerEnabled) {
+		this.safeResultHandlerEnabled = safeResultHandlerEnabled;
+	}
+
+	public boolean isSafeRowBoundsEnabled() {
+		return safeRowBoundsEnabled;
+	}
+
+	public void setSafeRowBoundsEnabled(boolean safeRowBoundsEnabled) {
+		this.safeRowBoundsEnabled = safeRowBoundsEnabled;
+	}
+
+	public boolean isMapUnderscoreToCamelCase() {
+		return mapUnderscoreToCamelCase;
+	}
+
+	public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
+		this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
+	}
+
+	public void addLoadedResource(String resource) {
+		loadedResources.add(resource);
+	}
+
+	public boolean isResourceLoaded(String resource) {
+		return loadedResources.contains(resource);
+	}
+
+	public Environment getEnvironment() {
+		return environment;
+	}
+
+	public void setEnvironment(Environment environment) {
+		this.environment = environment;
+	}
+
+	public AutoMappingBehavior getAutoMappingBehavior() {
+		return autoMappingBehavior;
+	}
+
+	public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) {
+		this.autoMappingBehavior = autoMappingBehavior;
+	}
+
+	public boolean isLazyLoadingEnabled() {
+		return lazyLoadingEnabled;
+	}
+
+	public void setLazyLoadingEnabled(boolean lazyLoadingEnabled) {
+		this.lazyLoadingEnabled = lazyLoadingEnabled;
+	}
+
+	public ProxyFactory getProxyFactory() {
+		if (proxyFactory == null) {
+			// makes sure CGLIB is not needed unless explicitly requested
+			proxyFactory = new CglibProxyFactory();
+		}
+		return proxyFactory;
+	}
+
+	public void setProxyFactory(ProxyFactory proxyFactory) {
+		this.proxyFactory = proxyFactory;
+	}
+
+	public boolean isAggressiveLazyLoading() {
+		return aggressiveLazyLoading;
+	}
+
+	public void setAggressiveLazyLoading(boolean aggressiveLazyLoading) {
+		this.aggressiveLazyLoading = aggressiveLazyLoading;
+	}
+
+	public boolean isMultipleResultSetsEnabled() {
+		return multipleResultSetsEnabled;
+	}
+
+	public void setMultipleResultSetsEnabled(boolean multipleResultSetsEnabled) {
+		this.multipleResultSetsEnabled = multipleResultSetsEnabled;
+	}
+
+	public Set<String> getLazyLoadTriggerMethods() {
+		return lazyLoadTriggerMethods;
+	}
+
+	public void setLazyLoadTriggerMethods(Set<String> lazyLoadTriggerMethods) {
+		this.lazyLoadTriggerMethods = lazyLoadTriggerMethods;
+	}
+
+	public boolean isUseGeneratedKeys() {
+		return useGeneratedKeys;
+	}
+
+	public void setUseGeneratedKeys(boolean useGeneratedKeys) {
+		this.useGeneratedKeys = useGeneratedKeys;
+	}
+
+	public ExecutorType getDefaultExecutorType() {
+		return defaultExecutorType;
+	}
+
+	public void setDefaultExecutorType(ExecutorType defaultExecutorType) {
+		this.defaultExecutorType = defaultExecutorType;
+	}
+
+	public boolean isCacheEnabled() {
+		return cacheEnabled;
+	}
+
+	public void setCacheEnabled(boolean cacheEnabled) {
+		this.cacheEnabled = cacheEnabled;
+	}
+
+	public Integer getDefaultStatementTimeout() {
+		return defaultStatementTimeout;
+	}
+
+	public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
+		this.defaultStatementTimeout = defaultStatementTimeout;
+	}
+
+	public boolean isUseColumnLabel() {
+		return useColumnLabel;
+	}
+
+	public void setUseColumnLabel(boolean useColumnLabel) {
+		this.useColumnLabel = useColumnLabel;
+	}
+
+	public LocalCacheScope getLocalCacheScope() {
+		return localCacheScope;
+	}
+
+	public void setLocalCacheScope(LocalCacheScope localCacheScope) {
+		this.localCacheScope = localCacheScope;
+	}
+
+	public JdbcType getJdbcTypeForNull() {
+		return jdbcTypeForNull;
+	}
+
+	public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
+		this.jdbcTypeForNull = jdbcTypeForNull;
+	}
+
+	public Properties getVariables() {
+		return variables;
+	}
+
+	public void setVariables(Properties variables) {
+		this.variables = variables;
+	}
+
+	public TypeHandlerRegistry getTypeHandlerRegistry() {
+		return typeHandlerRegistry;
+	}
+
+	public TypeAliasRegistry getTypeAliasRegistry() {
+		return typeAliasRegistry;
+	}
+
+	/**
+	 * @since 3.2.2
+	 */
+	public MapperRegistry getMapperRegistry() {
+		return mapperRegistry;
+	}
+
+	public ObjectFactory getObjectFactory() {
+		return objectFactory;
+	}
+
+	public void setObjectFactory(ObjectFactory objectFactory) {
+		this.objectFactory = objectFactory;
+	}
+
+	public ObjectWrapperFactory getObjectWrapperFactory() {
+		return objectWrapperFactory;
+	}
+
+	public void setObjectWrapperFactory(
+			ObjectWrapperFactory objectWrapperFactory) {
+		this.objectWrapperFactory = objectWrapperFactory;
+	}
+
+	/**
+	 * @since 3.2.2
+	 */
+	public List<Interceptor> getInterceptors() {
+		return interceptorChain.getInterceptors();
+	}
+
+	public LanguageDriverRegistry getLanguageRegistry() {
+		return languageRegistry;
+	}
+
+	public void setDefaultScriptingLanguage(Class<?> driver) {
+		if (driver == null) {
+			driver = XMLLanguageDriver.class;
+		}
+		getLanguageRegistry().setDefaultDriverClass(driver);
+	}
+
+	public LanguageDriver getDefaultScriptingLanuageInstance() {
+		return languageRegistry.getDefaultDriver();
+	}
+
+	public MetaObject newMetaObject(Object object) {
+		return MetaObject
+				.forObject(object, objectFactory, objectWrapperFactory);
+	}
+
+	public ParameterHandler newParameterHandler(
+			MappedStatement mappedStatement, Object parameterObject,
+			BoundSql boundSql) {
+		ParameterHandler parameterHandler = mappedStatement.getLang()
+				.createParameterHandler(mappedStatement, parameterObject,
+						boundSql);
+		parameterHandler = (ParameterHandler) interceptorChain
+				.pluginAll(parameterHandler);
+		return parameterHandler;
+	}
+
+	public ResultSetHandler newResultSetHandler(Executor executor,
+			MappedStatement mappedStatement, RowBounds rowBounds,
+			ParameterHandler parameterHandler, ResultHandler resultHandler,
+			BoundSql boundSql) {
+		ResultSetHandler resultSetHandler = new DefaultResultSetHandler(
+				executor, mappedStatement, parameterHandler, resultHandler,
+				boundSql, rowBounds);
+		resultSetHandler = (ResultSetHandler) interceptorChain
+				.pluginAll(resultSetHandler);
+		return resultSetHandler;
+	}
+
+	public StatementHandler newStatementHandler(Executor executor,
+			MappedStatement mappedStatement, Object parameterObject,
+			RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+		StatementHandler statementHandler = new RoutingStatementHandler(
+				executor, mappedStatement, parameterObject, rowBounds,
+				resultHandler, boundSql);
+		statementHandler = (StatementHandler) interceptorChain
+				.pluginAll(statementHandler);
+		return statementHandler;
+	}
+
+	public Executor newExecutor(Transaction transaction) {
+		return newExecutor(transaction, defaultExecutorType);
+	}
+
+	public Executor newExecutor(Transaction transaction,
+			ExecutorType executorType) {
+		executorType = executorType == null ? defaultExecutorType
+				: executorType;
+		executorType = executorType == null ? ExecutorType.SIMPLE
+				: executorType;
+		Executor executor;
+		if (ExecutorType.BATCH == executorType) {
+			executor = new BatchExecutor(this, transaction);
+		} else if (ExecutorType.REUSE == executorType) {
+			executor = new ReuseExecutor(this, transaction);
+		} else {
+			executor = new SimpleExecutor(this, transaction);
+		}
+		if (cacheEnabled) {
+			executor = new CachingExecutor(executor);
+		}
+		executor = (Executor) interceptorChain.pluginAll(executor);
+		return executor;
+	}
+
+	public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
+		keyGenerators.put(id, keyGenerator);
+	}
+
+	public Collection<String> getKeyGeneratorNames() {
+		return keyGenerators.keySet();
+	}
+
+	public Collection<KeyGenerator> getKeyGenerators() {
+		return keyGenerators.values();
+	}
+
+	public KeyGenerator getKeyGenerator(String id) {
+		return keyGenerators.get(id);
+	}
+
+	public boolean hasKeyGenerator(String id) {
+		return keyGenerators.containsKey(id);
+	}
+
+	public void addCache(Cache cache) {
+		caches.put(cache.getId(), cache);
+	}
+
+	public Collection<String> getCacheNames() {
+		return caches.keySet();
+	}
+
+	public Collection<Cache> getCaches() {
+		return caches.values();
+	}
+
+	public Cache getCache(String id) {
+		return caches.get(id);
+	}
+
+	public boolean hasCache(String id) {
+		return caches.containsKey(id);
+	}
+
+	public void addResultMap(ResultMap rm) {
+		resultMaps.put(rm.getId(), rm);
+		checkLocallyForDiscriminatedNestedResultMaps(rm);
+		checkGloballyForDiscriminatedNestedResultMaps(rm);
+	}
+
+	public Collection<String> getResultMapNames() {
+		return resultMaps.keySet();
+	}
+
+	public Collection<ResultMap> getResultMaps() {
+		return resultMaps.values();
+	}
+
+	public ResultMap getResultMap(String id) {
+		return resultMaps.get(id);
+	}
+
+	public boolean hasResultMap(String id) {
+		return resultMaps.containsKey(id);
+	}
+
+	public void addParameterMap(ParameterMap pm) {
+		parameterMaps.put(pm.getId(), pm);
+	}
+
+	public Collection<String> getParameterMapNames() {
+		return parameterMaps.keySet();
+	}
+
+	public Collection<ParameterMap> getParameterMaps() {
+		return parameterMaps.values();
+	}
+
+	public ParameterMap getParameterMap(String id) {
+		return parameterMaps.get(id);
+	}
+
+	public boolean hasParameterMap(String id) {
+		return parameterMaps.containsKey(id);
+	}
+
+	public void addMappedStatement(MappedStatement ms) {
+		mappedStatements.put(ms.getId(), ms);
+	}
+
+	public Collection<String> getMappedStatementNames() {
+		buildAllStatements();
+		return mappedStatements.keySet();
+	}
+
+	public Collection<MappedStatement> getMappedStatements() {
+		buildAllStatements();
+		return mappedStatements.values();
+	}
+
+	public Collection<XMLStatementBuilder> getIncompleteStatements() {
+		return incompleteStatements;
+	}
+
+	public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
+		incompleteStatements.add(incompleteStatement);
+	}
+
+	public Collection<CacheRefResolver> getIncompleteCacheRefs() {
+		return incompleteCacheRefs;
+	}
+
+	public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
+		incompleteCacheRefs.add(incompleteCacheRef);
+	}
+
+	public Collection<ResultMapResolver> getIncompleteResultMaps() {
+		return incompleteResultMaps;
+	}
+
+	public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
+		incompleteResultMaps.add(resultMapResolver);
+	}
+
+	public void addIncompleteMethod(MethodResolver builder) {
+		incompleteMethods.add(builder);
+	}
+
+	public Collection<MethodResolver> getIncompleteMethods() {
+		return incompleteMethods;
+	}
+
+	public MappedStatement getMappedStatement(String id) {
+		return this.getMappedStatement(id, true);
+	}
+
+	public MappedStatement getMappedStatement(String id,
+			boolean validateIncompleteStatements) {
+		if (validateIncompleteStatements) {
+			buildAllStatements();
+		}
+		return mappedStatements.get(id);
+	}
+
+	public Map<String, XNode> getSqlFragments() {
+		return sqlFragments;
+	}
+
+	public void addInterceptor(Interceptor interceptor) {
+		interceptorChain.addInterceptor(interceptor);
+	}
+
+	public void addMappers(String packageName, Class<?> superType) {
+		mapperRegistry.addMappers(packageName, superType);
+	}
+
+	public void addMappers(String packageName) {
+		mapperRegistry.addMappers(packageName);
+	}
+
+	public <T> void addMapper(Class<T> type) {
+		mapperRegistry.addMapper(type);
+	}
+
+	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
+		return mapperRegistry.getMapper(type, sqlSession);
+	}
+
+	public boolean hasMapper(Class<?> type) {
+		return mapperRegistry.hasMapper(type);
+	}
+
+	public boolean hasStatement(String statementName) {
+		return hasStatement(statementName, true);
+	}
+
+	public boolean hasStatement(String statementName,
+			boolean validateIncompleteStatements) {
+		if (validateIncompleteStatements) {
+			buildAllStatements();
+		}
+		return mappedStatements.containsKey(statementName);
+	}
+
+	public void addCacheRef(String namespace, String referencedNamespace) {
+		cacheRefMap.put(namespace, referencedNamespace);
+	}
+
+	/*
+	 * Parses all the unprocessed statement nodes in the cache. It is
+	 * recommended to call this method once all the mappers are added as it
+	 * provides fail-fast statement validation.
+	 */
+	protected void buildAllStatements() {
+		if (!incompleteResultMaps.isEmpty()) {
+			synchronized (incompleteResultMaps) {
+				// This always throws a BuilderException.
+				incompleteResultMaps.iterator().next().resolve();
+			}
+		}
+		if (!incompleteCacheRefs.isEmpty()) {
+			synchronized (incompleteCacheRefs) {
+				// This always throws a BuilderException.
+				incompleteCacheRefs.iterator().next().resolveCacheRef();
+			}
+		}
+		if (!incompleteStatements.isEmpty()) {
+			synchronized (incompleteStatements) {
+				// This always throws a BuilderException.
+				incompleteStatements.iterator().next().parseStatementNode();
+			}
+		}
+		if (!incompleteMethods.isEmpty()) {
+			synchronized (incompleteMethods) {
+				// This always throws a BuilderException.
+				incompleteMethods.iterator().next().resolve();
+			}
+		}
+	}
+
+	/*
+	 * Extracts namespace from fully qualified statement id.
+	 * 
+	 * @param statementId
+	 * 
+	 * @return namespace or null when id does not contain period.
+	 */
+	protected String extractNamespace(String statementId) {
+		int lastPeriod = statementId.lastIndexOf('.');
+		return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
+	}
+
+	// Slow but a one time cost. A better solution is welcome.
+	protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
+		if (rm.hasNestedResultMaps()) {
+			for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
+				Object value = entry.getValue();
+				if (value instanceof ResultMap) {
+					ResultMap entryResultMap = (ResultMap) value;
+					if (!entryResultMap.hasNestedResultMaps()
+							&& entryResultMap.getDiscriminator() != null) {
+						Collection<String> discriminatedResultMapNames = entryResultMap
+								.getDiscriminator().getDiscriminatorMap()
+								.values();
+						if (discriminatedResultMapNames.contains(rm.getId())) {
+							entryResultMap.forceNestedResultMaps();
+						}
+					}
+				}
+			}
+		}
+	}
+
+	// Slow but a one time cost. A better solution is welcome.
+	protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
+		if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
+			for (Map.Entry<String, String> entry : rm.getDiscriminator()
+					.getDiscriminatorMap().entrySet()) {
+				String discriminatedResultMapName = entry.getValue();
+				if (hasResultMap(discriminatedResultMapName)) {
+					ResultMap discriminatedResultMap = resultMaps
+							.get(discriminatedResultMapName);
+					if (discriminatedResultMap.hasNestedResultMaps()) {
+						rm.forceNestedResultMaps();
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	protected static class StrictMap<V> extends HashMap<String, V> {
+
+		private static final long serialVersionUID = -4950446264854982944L;
+		private String name;
+
+		public StrictMap(String name, int initialCapacity, float loadFactor) {
+			super(initialCapacity, loadFactor);
+			this.name = name;
+		}
+
+		public StrictMap(String name, int initialCapacity) {
+			super(initialCapacity);
+			this.name = name;
+		}
+
+		public StrictMap(String name) {
+			super();
+			this.name = name;
+		}
+
+		public StrictMap(String name, Map<String, ? extends V> m) {
+			super(m);
+			this.name = name;
+		}
+
+		// TODO 如果现在状态为刷新,则刷新(先删除后添加)
+		@SuppressWarnings("unchecked")
+		public V put(String key, V value) {
+			if (org.apache.ibatis.thread.Runnable.isRefresh()) {
+				remove(key);
+				org.apache.ibatis.thread.Runnable.log.debug("refresh key:"
+						+ key.substring(key.lastIndexOf(".") + 1));
+			}
+			if (containsKey(key))
+				throw new IllegalArgumentException(name
+						+ " already contains value for " + key);
+			if (key.contains(".")) {
+				final String shortKey = getShortName(key);
+				if (super.get(shortKey) == null) {
+					super.put(shortKey, value);
+				} else {
+					super.put(shortKey, (V) new Ambiguity(shortKey));
+				}
+			}
+			return super.put(key, value);
+		}
+
+		public V get(Object key) {
+			V value = super.get(key);
+			if (value == null) {
+				throw new IllegalArgumentException(name
+						+ " does not contain value for " + key);
+			}
+			if (value instanceof Ambiguity) {
+				throw new IllegalArgumentException(
+						((Ambiguity) value).getSubject()
+								+ " is ambiguous in "
+								+ name
+								+ " (try using the full name including the namespace, or rename one of the entries)");
+			}
+			return value;
+		}
+
+		private String getShortName(String key) {
+			final String[] keyparts = key.split("\\.");
+			final String shortKey = keyparts[keyparts.length - 1];
+			return shortKey;
+		}
+
+		protected static class Ambiguity {
+			private String subject;
+
+			public Ambiguity(String subject) {
+				this.subject = subject;
+			}
+
+			public String getSubject() {
+				return subject;
+			}
+		}
+	}
+
+}

+ 31 - 0
src/main/java/org/apache/ibatis/thread/PropertiesUtil.java

@@ -0,0 +1,31 @@
+package org.apache.ibatis.thread;
+
+import java.util.Properties;
+
+public class PropertiesUtil {
+
+	private static String filename = "/mybatis-refresh.properties";
+	private static Properties pro = new Properties();
+	static {
+		try {
+			pro.load(PropertiesUtil.class.getResourceAsStream(filename));
+		} catch (Exception e) {
+			e.printStackTrace();
+			System.out.println("Load mybatis-refresh “"+filename+"” file error.");
+		}
+	}
+
+	public static int getInt(String key) {
+		int i = 0;
+		try {
+			i = Integer.parseInt(getString(key));
+		} catch (Exception e) {
+		}
+		return i;
+	}
+
+	public static String getString(String key) {
+		return pro == null ? null : pro.getProperty(key);
+	}
+
+}

+ 194 - 0
src/main/java/org/apache/ibatis/thread/Runnable.java

@@ -0,0 +1,194 @@
+package org.apache.ibatis.thread;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.session.Configuration;
+import org.apache.log4j.Logger;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.springframework.core.NestedIOException;
+
+/**
+ * 刷新使用进程
+ * 
+ * @author liubaoquan
+ * 
+ */
+public class Runnable implements java.lang.Runnable {
+
+	public static Logger log = Logger.getLogger(Runnable.class);
+
+	private String location;
+	private Configuration configuration;
+
+	private Long beforeTime = 0L; // 上一次刷新时间
+	private static boolean refresh = false; // 是否执行刷新
+
+	private static String mappingPath = "mappings"; // xml文件夹匹配字符串,需要根据需要修改
+	private static int delaySeconds = 10;// 延迟刷新秒数
+	private static int sleepSeconds = 1;// 休眠时间
+	
+	private static boolean enabled = false;
+
+	static {
+		delaySeconds = PropertiesUtil.getInt("delaySeconds");
+		sleepSeconds = PropertiesUtil.getInt("sleepSeconds");
+		mappingPath = PropertiesUtil.getString("mappingPath");
+		enabled = "true".equals(PropertiesUtil.getString("enabled"));
+		
+		delaySeconds = delaySeconds == 0 ? 50 : delaySeconds;
+		sleepSeconds = sleepSeconds == 0 ? 1 : sleepSeconds;
+		mappingPath = StringUtils.isBlank(mappingPath) ? "mappings"
+				: mappingPath;
+
+		log.debug("[delaySeconds] " + delaySeconds);
+		log.debug("[sleepSeconds] " + sleepSeconds);
+		log.debug("[mappingPath] " + mappingPath);
+
+	}
+
+	public static boolean isRefresh() {
+		return refresh;
+	}
+
+	public Runnable(String location, Configuration configuration) {
+		this.location = location.replaceAll("\\\\", "/");
+		this.configuration = configuration;
+	}
+
+	@Override
+	public void run() {
+		location = location.substring("file [".length(),
+				location.lastIndexOf(mappingPath) + mappingPath.length());
+		beforeTime = System.currentTimeMillis();
+
+		log.debug("[location] " + location);
+		log.debug("[configuration] " + configuration);
+		
+		if (enabled){
+			start(this);
+		}
+	}
+
+	public void start(final Runnable runnable) {
+
+		new Thread(new java.lang.Runnable() {
+
+			@Override
+			public void run() {
+
+				try {
+					Thread.sleep(delaySeconds * 1000);
+				} catch (InterruptedException e2) {
+					e2.printStackTrace();
+				}
+				refresh = true;
+
+				System.out.println("========= Enabled refresh mybatis mapper =========");
+
+				while (true) {
+					try {
+						runnable.refresh(location, beforeTime);
+					} catch (Exception e1) {
+						e1.printStackTrace();
+					}
+
+					try {
+						Thread.sleep(sleepSeconds * 1000);
+					} catch (InterruptedException e) {
+						e.printStackTrace();
+					}
+
+				}
+			}
+		}).start();
+	}
+
+	/**
+	 * 执行刷新
+	 * 
+	 * @param filePath
+	 *            刷新目录
+	 * @param beforeTime
+	 *            上次刷新时间
+	 * @throws NestedIOException
+	 *             解析异常
+	 * @throws FileNotFoundException
+	 *             文件未找到
+	 */
+	public void refresh(String filePath, Long beforeTime) throws Exception {
+
+		// 本次刷新时间
+		Long refrehTime = System.currentTimeMillis();
+
+		List<File> refreshs = this.getRefreshFile(new File(filePath),
+				beforeTime);
+		if (refreshs.size() > 0) {
+			log.debug("refresh files:" + refreshs.size());
+		}
+		for (int i = 0; i < refreshs.size(); i++) {
+			System.out.println("Refresh file: "
+					+ mappingPath
+					+ StringUtils.substringAfterLast(refreshs.get(i)
+							.getAbsolutePath(), mappingPath));
+			log.debug("refresh file:" + refreshs.get(i).getAbsolutePath());
+			log.debug("refresh filename:" + refreshs.get(i).getName());
+			SqlSessionFactoryBean.refresh(new FileInputStream(refreshs.get(i)),
+					refreshs.get(i).getAbsolutePath(), configuration);
+		}
+		// 如果刷新了文件,则修改刷新时间,否则不修改
+		if (refreshs.size() > 0) {
+			this.beforeTime = refrehTime;
+		}
+	}
+
+	/**
+	 * 获取需要刷新的文件列表
+	 * 
+	 * @param dir
+	 *            目录
+	 * @param beforeTime
+	 *            上次刷新时间
+	 * @return 刷新文件列表
+	 */
+	public List<File> getRefreshFile(File dir, Long beforeTime) {
+		List<File> refreshs = new ArrayList<File>();
+
+		File[] files = dir.listFiles();
+		for (int i = 0; i < files.length; i++) {
+			File file = files[i];
+			if (file.isDirectory()) {
+				refreshs.addAll(this.getRefreshFile(file, beforeTime));
+			} else if (file.isFile()) {
+				if (this.check(file, beforeTime)) {
+					refreshs.add(file);
+				}
+			} else {
+				System.out.println("error file." + file.getName());
+			}
+		}
+
+		return refreshs;
+	}
+
+	/**
+	 * 判断文件是否需要刷新
+	 * 
+	 * @param file
+	 *            文件
+	 * @param beforeTime
+	 *            上次刷新时间
+	 * @return 需要刷新返回true,否则返回false
+	 */
+	public boolean check(File file, Long beforeTime) {
+		if (file.lastModified() > beforeTime) {
+			return true;
+		}
+		return false;
+	}
+
+}

+ 607 - 0
src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java

@@ -0,0 +1,607 @@
+/*
+ *    Copyright 2010-2013 the original author or authors.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+package org.mybatis.spring;
+
+import static org.springframework.util.Assert.notNull;
+import static org.springframework.util.ObjectUtils.isEmpty;
+import static org.springframework.util.StringUtils.hasLength;
+import static org.springframework.util.StringUtils.tokenizeToStringArray;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import org.apache.ibatis.builder.xml.XMLConfigBuilder;
+import org.apache.ibatis.builder.xml.XMLMapperBuilder;
+import org.apache.ibatis.executor.ErrorContext;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.mapping.DatabaseIdProvider;
+import org.apache.ibatis.mapping.Environment;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.reflection.factory.ObjectFactory;
+import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.transaction.TransactionFactory;
+import org.apache.ibatis.type.TypeHandler;
+import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.NestedIOException;
+import org.springframework.core.io.Resource;
+import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
+
+/**
+ * {@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}. This
+ * is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a
+ * Spring application context; the SqlSessionFactory can then be passed to
+ * MyBatis-based DAOs via dependency injection.
+ * 
+ * Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager}
+ * can be used for transaction demarcation in combination with a
+ * {@code SqlSessionFactory}. JTA should be used for transactions which span
+ * multiple databases or when container managed transactions (CMT) are being
+ * used.
+ * 
+ * @author Putthibong Boonbong
+ * @author Hunter Presnall
+ * @author Eduardo Macarron
+ * 
+ * @see #setConfigLocation
+ * @see #setDataSource
+ * @version $Id$
+ * @desctiption 刷新xml文件
+ */
+public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>,
+		InitializingBean, ApplicationListener<ApplicationEvent> {
+
+	private static final Log logger = LogFactory
+			.getLog(SqlSessionFactoryBean.class);
+
+	private Resource configLocation;
+
+	private Resource[] mapperLocations;
+
+	private DataSource dataSource;
+
+	private TransactionFactory transactionFactory;
+
+	private Properties configurationProperties;
+
+	private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
+
+	private SqlSessionFactory sqlSessionFactory;
+
+	private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1
+
+	private boolean failFast;
+
+	private Interceptor[] plugins;
+
+	private TypeHandler<?>[] typeHandlers;
+
+	private String typeHandlersPackage;
+
+	private Class<?>[] typeAliases;
+
+	private String typeAliasesPackage;
+
+	private Class<?> typeAliasesSuperType;
+
+	private DatabaseIdProvider databaseIdProvider; // issue #19. No default provider.
+
+	private ObjectFactory objectFactory;
+
+	private ObjectWrapperFactory objectWrapperFactory;
+
+	/**
+	 * Sets the ObjectFactory.
+	 * 
+	 * @since 1.1.2
+	 * @param objectFactory
+	 */
+	public void setObjectFactory(ObjectFactory objectFactory) {
+		this.objectFactory = objectFactory;
+	}
+
+	/**
+	 * Sets the ObjectWrapperFactory.
+	 * 
+	 * @since 1.1.2
+	 * @param objectWrapperFactory
+	 */
+	public void setObjectWrapperFactory(
+			ObjectWrapperFactory objectWrapperFactory) {
+		this.objectWrapperFactory = objectWrapperFactory;
+	}
+
+	/**
+	 * Gets the DatabaseIdProvider
+	 * 
+	 * @since 1.1.0
+	 * @return
+	 */
+	public DatabaseIdProvider getDatabaseIdProvider() {
+		return databaseIdProvider;
+	}
+
+	/**
+	 * Sets the DatabaseIdProvider. As of version 1.2.2 this variable is not
+	 * initialized by default.
+	 * 
+	 * @since 1.1.0
+	 * @param databaseIdProvider
+	 */
+	public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) {
+		this.databaseIdProvider = databaseIdProvider;
+	}
+
+	/**
+	 * Mybatis plugin list.
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param plugins
+	 *            list of plugins
+	 * 
+	 */
+	public void setPlugins(Interceptor[] plugins) {
+		this.plugins = plugins;
+	}
+
+	/**
+	 * Packages to search for type aliases.
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param typeAliasesPackage
+	 *            package to scan for domain objects
+	 * 
+	 */
+	public void setTypeAliasesPackage(String typeAliasesPackage) {
+		this.typeAliasesPackage = typeAliasesPackage;
+	}
+
+	/**
+	 * Super class which domain objects have to extend to have a type alias
+	 * created. No effect if there is no package to scan configured.
+	 * 
+	 * @since 1.1.2
+	 * 
+	 * @param typeAliasesSuperType
+	 *            super class for domain objects
+	 * 
+	 */
+	public void setTypeAliasesSuperType(Class<?> typeAliasesSuperType) {
+		this.typeAliasesSuperType = typeAliasesSuperType;
+	}
+
+	/**
+	 * Packages to search for type handlers.
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param typeHandlersPackage
+	 *            package to scan for type handlers
+	 * 
+	 */
+	public void setTypeHandlersPackage(String typeHandlersPackage) {
+		this.typeHandlersPackage = typeHandlersPackage;
+	}
+
+	/**
+	 * Set type handlers. They must be annotated with {@code MappedTypes} and
+	 * optionally with {@code MappedJdbcTypes}
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param typeHandlers
+	 *            Type handler list
+	 */
+	public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
+		this.typeHandlers = typeHandlers;
+	}
+
+	/**
+	 * List of type aliases to register. They can be annotated with
+	 * {@code Alias}
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param typeAliases
+	 *            Type aliases list
+	 */
+	public void setTypeAliases(Class<?>[] typeAliases) {
+		this.typeAliases = typeAliases;
+	}
+
+	/**
+	 * If true, a final check is done on Configuration to assure that all mapped
+	 * statements are fully loaded and there is no one still pending to resolve
+	 * includes. Defaults to false.
+	 * 
+	 * @since 1.0.1
+	 * 
+	 * @param failFast
+	 *            enable failFast
+	 */
+	public void setFailFast(boolean failFast) {
+		this.failFast = failFast;
+	}
+
+	/**
+	 * Set the location of the MyBatis {@code SqlSessionFactory} config file. A
+	 * typical value is "WEB-INF/mybatis-configuration.xml".
+	 */
+	public void setConfigLocation(Resource configLocation) {
+		this.configLocation = configLocation;
+	}
+
+	/**
+	 * Set locations of MyBatis mapper files that are going to be merged into
+	 * the {@code SqlSessionFactory} configuration at runtime.
+	 * 
+	 * This is an alternative to specifying "&lt;sqlmapper&gt;" entries in an
+	 * MyBatis config file. This property being based on Spring's resource
+	 * abstraction also allows for specifying resource patterns here: e.g.
+	 * "classpath*:sqlmap/*-mapper.xml".
+	 */
+	public void setMapperLocations(Resource[] mapperLocations) {
+		this.mapperLocations = mapperLocations;
+	}
+
+	/**
+	 * Set optional properties to be passed into the SqlSession configuration,
+	 * as alternative to a {@code &lt;properties&gt;} tag in the configuration
+	 * xml file. This will be used to resolve placeholders in the config file.
+	 */
+	public void setConfigurationProperties(
+			Properties sqlSessionFactoryProperties) {
+		this.configurationProperties = sqlSessionFactoryProperties;
+	}
+
+	/**
+	 * Set the JDBC {@code DataSource} that this instance should manage
+	 * transactions for. The {@code DataSource} should match the one used by the
+	 * {@code SqlSessionFactory}: for example, you could specify the same JNDI
+	 * DataSource for both.
+	 * 
+	 * A transactional JDBC {@code Connection} for this {@code DataSource} will
+	 * be provided to application code accessing this {@code DataSource}
+	 * directly via {@code DataSourceUtils} or
+	 * {@code DataSourceTransactionManager}.
+	 * 
+	 * The {@code DataSource} specified here should be the target
+	 * {@code DataSource} to manage transactions for, not a
+	 * {@code TransactionAwareDataSourceProxy}. Only data access code may work
+	 * with {@code TransactionAwareDataSourceProxy}, while the transaction
+	 * manager needs to work on the underlying target {@code DataSource}. If
+	 * there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in,
+	 * it will be unwrapped to extract its target {@code DataSource}.
+	 * 
+	 */
+	public void setDataSource(DataSource dataSource) {
+		if (dataSource instanceof TransactionAwareDataSourceProxy) {
+			// If we got a TransactionAwareDataSourceProxy, we need to perform
+			// transactions for its underlying target DataSource, else data
+			// access code won't see properly exposed transactions (i.e.
+			// transactions for the target DataSource).
+			this.dataSource = ((TransactionAwareDataSourceProxy) dataSource)
+					.getTargetDataSource();
+		} else {
+			this.dataSource = dataSource;
+		}
+	}
+
+	/**
+	 * Sets the {@code SqlSessionFactoryBuilder} to use when creating the
+	 * {@code SqlSessionFactory}.
+	 * 
+	 * This is mainly meant for testing so that mock SqlSessionFactory classes
+	 * can be injected. By default, {@code SqlSessionFactoryBuilder} creates
+	 * {@code DefaultSqlSessionFactory} instances.
+	 * 
+	 */
+	public void setSqlSessionFactoryBuilder(
+			SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
+		this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
+	}
+
+	/**
+	 * Set the MyBatis TransactionFactory to use. Default is
+	 * {@code SpringManagedTransactionFactory}
+	 * 
+	 * The default {@code SpringManagedTransactionFactory} should be appropriate
+	 * for all cases: be it Spring transaction management, EJB CMT or plain JTA.
+	 * If there is no active transaction, SqlSession operations will execute SQL
+	 * statements non-transactionally.
+	 * 
+	 * <b>It is strongly recommended to use the default
+	 * {@code TransactionFactory}.</b> If not used, any attempt at getting an
+	 * SqlSession through Spring's MyBatis framework will throw an exception if
+	 * a transaction is active.
+	 * 
+	 * @see SpringManagedTransactionFactory
+	 * @param transactionFactory
+	 *            the MyBatis TransactionFactory
+	 */
+	public void setTransactionFactory(TransactionFactory transactionFactory) {
+		this.transactionFactory = transactionFactory;
+	}
+
+	/**
+	 * <b>NOTE:</b> This class <em>overrides</em> any {@code Environment} you
+	 * have set in the MyBatis config file. This is used only as a placeholder
+	 * name. The default value is
+	 * {@code SqlSessionFactoryBean.class.getSimpleName()}.
+	 * 
+	 * @param environment
+	 *            the environment name
+	 */
+	public void setEnvironment(String environment) {
+		this.environment = environment;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void afterPropertiesSet() throws Exception {
+		notNull(dataSource, "Property 'dataSource' is required");
+		notNull(sqlSessionFactoryBuilder,
+				"Property 'sqlSessionFactoryBuilder' is required");
+
+		this.sqlSessionFactory = buildSqlSessionFactory();
+	}
+
+	/**
+	 * Build a {@code SqlSessionFactory} instance.
+	 * 
+	 * The default implementation uses the standard MyBatis
+	 * {@code XMLConfigBuilder} API to build a {@code SqlSessionFactory}
+	 * instance based on an Reader.
+	 * 
+	 * @return SqlSessionFactory
+	 * @throws IOException
+	 *             if loading the config file failed
+	 */
+	protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
+
+		Configuration configuration;
+
+		XMLConfigBuilder xmlConfigBuilder = null;
+		if (this.configLocation != null) {
+			xmlConfigBuilder = new XMLConfigBuilder(
+					this.configLocation.getInputStream(), null,
+					this.configurationProperties);
+			configuration = xmlConfigBuilder.getConfiguration();
+		} else {
+			if (logger.isDebugEnabled()) {
+				logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
+			}
+			configuration = new Configuration();
+			configuration.setVariables(this.configurationProperties);
+		}
+
+		if (this.objectFactory != null) {
+			configuration.setObjectFactory(this.objectFactory);
+		}
+
+		if (this.objectWrapperFactory != null) {
+			configuration.setObjectWrapperFactory(this.objectWrapperFactory);
+		}
+
+		if (hasLength(this.typeAliasesPackage)) {
+			String[] typeAliasPackageArray = tokenizeToStringArray(
+					this.typeAliasesPackage,
+					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
+			for (String packageToScan : typeAliasPackageArray) {
+				configuration.getTypeAliasRegistry().registerAliases(
+						packageToScan,
+						typeAliasesSuperType == null ? Object.class
+								: typeAliasesSuperType);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Scanned package: '" + packageToScan
+							+ "' for aliases");
+				}
+			}
+		}
+
+		if (!isEmpty(this.typeAliases)) {
+			for (Class<?> typeAlias : this.typeAliases) {
+				configuration.getTypeAliasRegistry().registerAlias(typeAlias);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Registered type alias: '" + typeAlias + "'");
+				}
+			}
+		}
+
+		if (!isEmpty(this.plugins)) {
+			for (Interceptor plugin : this.plugins) {
+				configuration.addInterceptor(plugin);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Registered plugin: '" + plugin + "'");
+				}
+			}
+		}
+
+		if (hasLength(this.typeHandlersPackage)) {
+			String[] typeHandlersPackageArray = tokenizeToStringArray(
+					this.typeHandlersPackage,
+					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
+			for (String packageToScan : typeHandlersPackageArray) {
+				configuration.getTypeHandlerRegistry().register(packageToScan);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Scanned package: '" + packageToScan
+							+ "' for type handlers");
+				}
+			}
+		}
+
+		if (!isEmpty(this.typeHandlers)) {
+			for (TypeHandler<?> typeHandler : this.typeHandlers) {
+				configuration.getTypeHandlerRegistry().register(typeHandler);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Registered type handler: '" + typeHandler
+							+ "'");
+				}
+			}
+		}
+
+		if (xmlConfigBuilder != null) {
+			try {
+				xmlConfigBuilder.parse();
+
+				if (logger.isDebugEnabled()) {
+					logger.debug("Parsed configuration file: '"
+							+ this.configLocation + "'");
+				}
+			} catch (Exception ex) {
+				throw new NestedIOException("Failed to parse config resource: "
+						+ this.configLocation, ex);
+			} finally {
+				ErrorContext.instance().reset();
+			}
+		}
+
+		if (this.transactionFactory == null) {
+			this.transactionFactory = new SpringManagedTransactionFactory();
+		}
+
+		Environment environment = new Environment(this.environment,
+				this.transactionFactory, this.dataSource);
+		configuration.setEnvironment(environment);
+
+		if (this.databaseIdProvider != null) {
+			try {
+				configuration.setDatabaseId(this.databaseIdProvider
+						.getDatabaseId(this.dataSource));
+			} catch (SQLException e) {
+				throw new NestedIOException("Failed getting a databaseId", e);
+			}
+		}
+		// TODO 增加location 获取加载xml的路径,也可配置
+		String location = null;
+		if (!isEmpty(this.mapperLocations)) {
+			for (Resource mapperLocation : this.mapperLocations) {
+				if (location == null) {
+					location = mapperLocation.toString();
+				}
+				if (mapperLocation == null) {
+					continue;
+				}
+
+				try {
+					XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
+							mapperLocation.getInputStream(), configuration,
+							mapperLocation.toString(),
+							configuration.getSqlFragments());
+					xmlMapperBuilder.parse();
+				} catch (Exception e) {
+					e.printStackTrace(); // 出现错误抛出异常
+					throw new NestedIOException(
+							"Failed to parse mapping resource: '"
+									+ mapperLocation + "'", e);
+				} finally {
+					ErrorContext.instance().reset();
+				}
+
+				if (logger.isDebugEnabled()) {
+					logger.debug("Parsed mapper file: '" + mapperLocation + "'");
+				}
+			}
+		} else {
+			if (logger.isDebugEnabled()) {
+				logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
+			}
+		}
+
+		// TODO 编译sqlsession时,启动定时器
+		new org.apache.ibatis.thread.Runnable(location, configuration).run();
+
+		return this.sqlSessionFactoryBuilder.build(configuration);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public SqlSessionFactory getObject() throws Exception {
+		if (this.sqlSessionFactory == null) {
+			afterPropertiesSet();
+		}
+
+		return this.sqlSessionFactory;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Class<? extends SqlSessionFactory> getObjectType() {
+		return this.sqlSessionFactory == null ? SqlSessionFactory.class
+				: this.sqlSessionFactory.getClass();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isSingleton() {
+		return true;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void onApplicationEvent(ApplicationEvent event) {
+		if (failFast && event instanceof ContextRefreshedEvent) {
+			// fail-fast -> check all statements are completed
+			this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
+		}
+	}
+
+	/**
+	 * TODO 刷新
+	 * 
+	 * @param inputStream
+	 * @param resource
+	 * @param configuration
+	 * @throws NestedIOException
+	 */
+	public static void refresh(java.io.InputStream inputStream,
+			String resource, Configuration configuration)
+			throws NestedIOException {
+
+		try {
+			XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
+					inputStream, configuration, resource,
+					configuration.getSqlFragments());
+			xmlMapperBuilder.parse1();
+		} catch (Exception e) {
+			throw new NestedIOException("Failed to parse mapping resource: '"
+					+ resource + "'", e);
+		} finally {
+			ErrorContext.instance().reset();
+		}
+
+	}
+
+}