SqlSessionFactory初始化 博客分类: Mybatis mybatis
程序员文章站
2024-02-13 12:52:40
...
mybatis 使用教程:http://donald-draper.iteye.com/blog/2330429
Mybatis加载解析Mapper(xml)文件第一讲:http://donald-draper.iteye.com/blog/2333125
Mybatis加载解析Mapper(xml)文件第二讲:http://donald-draper.iteye.com/blog/2333191
在使用教程,我们有一个静态方法,初始化SqlSessionFactory
这篇文章,主要解决SqlSessionFactory是如何初始化的问题?我们从这一句开始
先看解析配置文件
//Mybatis配置项
//BaseBuilder
来看new XPathParser(reader, true, props, new XMLMapperEntityResolver()这一句构造XPathParser
//XNode
现在回到XMLConfigBuilder的解析mybatisConfig.xml文件
下面依次来看properties,typeAliases,plugins,objectFactory,objectWrapperFactory
,settings,environments,databaseIdProvider,typeHandlers,mappers的解析初始化
初始化properties
在XMLConfigBuilder的构造函数中初始化过Variables,首先加载properties标签中
resource为Properties,如果在构造SqlSessionFactoryBuilder
(sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader))
的过程中,传入了Properties文件,
则覆盖掉resource的Properties。
初始化typeAliases
//TypeAliasRegistry
//初始化plugins
来看插件的添加
//Configuration
//InterceptorChain
//Interceptor
//初始化ObjectFactory
//ObjectFactory
//初始化ObjectWrapperFactory
//ObjectWrapperFactory
//初始化settings
//初始化environments
//Environment
初始化typeHandler
//TypeHandlerRegistry
//TypeHandler
//JdbcType
从上面可以看出;初始化typeHandler,就是初始化javaType,jdbcType,及TypeHandler的映射关系
初始化mappers
回到SqlSessionFactoryBuilder,可以看到builder实际上是返回的是DefaultSqlSessionFactory
总结:
从上,可以出SqlSessionFactoryBuilder做的主要工作是,加载全局配置文件,根据
properties,typeAliases,plugins,objectFactory,objectWrapperFactory
,settings,environments,databaseIdProvider,typeHandlers,mappers配置项,
初始化Configuration,由于mappers解析内容较多,我们放在下一篇来讲。
附:Mapper解析的引篇
//Configuration
//MapperRegistry
//MapperProxyFactory
//MapperProxy
从上可以看出Configuration添加MapperInterface,就是MapperRegistry注册到其
HashMap<Class,MapperProxyFactory>中,MapperProxyFactory是MapperInterface的代理,
生成MapperInterface的MapperProxy代理实例,MapperProxy中利用Map<Method,MapperMethod>,
实现二级缓存
//MapperAnnotationBuilder,MapperInterface的注解处理
Mybatis加载解析Mapper(xml)文件第一讲:http://donald-draper.iteye.com/blog/2333125
Mybatis加载解析Mapper(xml)文件第二讲:http://donald-draper.iteye.com/blog/2333191
在使用教程,我们有一个静态方法,初始化SqlSessionFactory
static{ try{ reader = Resources.getResourceAsReader("mybatisConfig.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } }
这篇文章,主要解决SqlSessionFactory是如何初始化的问题?我们从这一句开始
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader) public class SqlSessionFactoryBuilder { public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } //创建SqlSessionFactory public SqlSessionFactory build(Reader reader, String environment, Properties properties) { SqlSessionFactory sqlsessionfactory; try { //解析配置文件 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //根据Configuration,创建sqlsessionfactory sqlsessionfactory = build(parser.parse()); } catch(Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } ErrorContext.instance().reset(); } //根据Configuration,创建sqlsessionfactory public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
先看解析配置文件
public class XMLConfigBuilder extends BaseBuilder { private boolean parsed;//解析状态 private XPathParser parser; private String environment;//环境 public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } //初始化XMLConfigBuilder private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); configuration.setVariables(props); parsed = false; this.environment = environment; this.parser = parser; } //获取配置信息Configuration public Configuration parse() { if(parsed) { throw new BuilderException("Each MapperConfigParser can only be used once."); } else { parsed = true; //解析mybatisConfig.xml文件 parseConfiguration(parser.evalNode("/configuration")); return configuration; } } //解析mybatisConfig.xml文件,properties,typeAliases,plugins,objectFactory,objectWrapperFactory //settings,environments,databaseIdProvider,typeHandlers,mappers private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch(Exception e) { throw new BuilderException((new StringBuilder()).append("Error parsing SQL Mapper Configuration. Cause: ").append(e).toString(), e); } } }
//Mybatis配置项
public class Configuration{ protected Environment environment;//环境 protected boolean safeRowBoundsEnabled; protected boolean safeResultHandlerEnabled; protected boolean mapUnderscoreToCamelCase; protected boolean aggressiveLazyLoading; protected boolean multipleResultSetsEnabled; protected boolean useGeneratedKeys;//允许JDBC支持生成的键 protected boolean useColumnLabel; protected boolean cacheEnabled;//全局的映射器启用或禁用缓存 protected boolean callSettersOnNulls; protected String logPrefix; protected Class logImpl; protected LocalCacheScope localCacheScope;//本地一级缓存作用域 protected JdbcType jdbcTypeForNull; protected Set lazyLoadTriggerMethods; protected Integer defaultStatementTimeout; protected ExecutorType defaultExecutorType; protected AutoMappingBehavior autoMappingBehavior; protected Properties variables; protected ObjectFactory objectFactory; protected ObjectWrapperFactory objectWrapperFactory; protected MapperRegistry mapperRegistry; protected boolean lazyLoadingEnabled; protected ProxyFactory proxyFactory; protected String databaseId; protected final InterceptorChain interceptorChain;//插件链 protected final TypeHandlerRegistry typeHandlerRegistry;//类型处理注册器 protected final TypeAliasRegistry typeAliasRegistry;//类型别名注册器 protected final LanguageDriverRegistry languageRegistry; protected final Map mappedStatements;// protected final Map caches; protected final Map resultMaps; protected final Map parameterMaps; protected final Map keyGenerators; protected final Set loadedResources; protected final Map sqlFragments; protected final Collection incompleteStatements; protected final Collection incompleteCacheRefs; protected final Collection incompleteResultMaps; protected final Collection incompleteMethods; protected final Map cacheRefMap; //初始化配置项 public Configuration() { safeRowBoundsEnabled = false; safeResultHandlerEnabled = true; mapUnderscoreToCamelCase = false; aggressiveLazyLoading = true; multipleResultSetsEnabled = true; useGeneratedKeys = false;//允许JDBC支持生成的键 useColumnLabel = true; cacheEnabled = true;//全局的映射器启用或禁用缓存 callSettersOnNulls = false; localCacheScope = LocalCacheScope.SESSION;//本地一级缓存作用域 jdbcTypeForNull = JdbcType.OTHER; lazyLoadTriggerMethods = new HashSet(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); defaultExecutorType = ExecutorType.SIMPLE; autoMappingBehavior = AutoMappingBehavior.PARTIAL; variables = new Properties(); objectFactory = new DefaultObjectFactory(); objectWrapperFactory = new DefaultObjectWrapperFactory(); mapperRegistry = new MapperRegistry(this); lazyLoadingEnabled = false; interceptorChain = new InterceptorChain();//插件链 typeHandlerRegistry = new TypeHandlerRegistry();//类型处理注册器 typeAliasRegistry = new TypeAliasRegistry();//类型别名注册器 languageRegistry = new LanguageDriverRegistry(); mappedStatements = new StrictMap("Mapped Statements collection"); caches = new StrictMap("Caches collection"); resultMaps = new StrictMap("Result Maps collection"); parameterMaps = new StrictMap("Parameter Maps collection"); keyGenerators = new StrictMap("Key Generators collection"); loadedResources = new HashSet(); sqlFragments = new StrictMap("XML fragments parsed from previous mappers"); incompleteStatements = new LinkedList(); incompleteCacheRefs = new LinkedList(); incompleteResultMaps = new LinkedList(); incompleteMethods = new LinkedList(); cacheRefMap = new HashMap(); typeAliasRegistry.registerAlias("JDBC", org/apache/ibatis/transaction/jdbc/JdbcTransactionFactory); typeAliasRegistry.registerAlias("MANAGED", org/apache/ibatis/transaction/managed/ManagedTransactionFactory); typeAliasRegistry.registerAlias("JNDI", org/apache/ibatis/datasource/jndi/JndiDataSourceFactory); typeAliasRegistry.registerAlias("POOLED", org/apache/ibatis/datasource/pooled/PooledDataSourceFactory); typeAliasRegistry.registerAlias("UNPOOLED", org/apache/ibatis/datasource/unpooled/UnpooledDataSourceFactory); typeAliasRegistry.registerAlias("PERPETUAL", org/apache/ibatis/cache/impl/PerpetualCache); typeAliasRegistry.registerAlias("FIFO", org/apache/ibatis/cache/decorators/FifoCache); typeAliasRegistry.registerAlias("LRU", org/apache/ibatis/cache/decorators/LruCache); typeAliasRegistry.registerAlias("SOFT", org/apache/ibatis/cache/decorators/SoftCache); typeAliasRegistry.registerAlias("WEAK", org/apache/ibatis/cache/decorators/WeakCache); typeAliasRegistry.registerAlias("VENDOR", org/apache/ibatis/mapping/VendorDatabaseIdProvider); typeAliasRegistry.registerAlias("XML", org/apache/ibatis/scripting/xmltags/XMLLanguageDriver); typeAliasRegistry.registerAlias("RAW", org/apache/ibatis/scripting/defaults/RawLanguageDriver); typeAliasRegistry.registerAlias("SLF4J", org/apache/ibatis/logging/slf4j/Slf4jImpl); typeAliasRegistry.registerAlias("COMMONS_LOGGING", org/apache/ibatis/logging/commons/JakartaCommonsLoggingImpl); typeAliasRegistry.registerAlias("LOG4J", org/apache/ibatis/logging/log4j/Log4jImpl); typeAliasRegistry.registerAlias("JDK_LOGGING", org/apache/ibatis/logging/jdk14/Jdk14LoggingImpl); typeAliasRegistry.registerAlias("STDOUT_LOGGING", org/apache/ibatis/logging/stdout/StdOutImpl); typeAliasRegistry.registerAlias("NO_LOGGING", org/apache/ibatis/logging/nologging/NoLoggingImpl); typeAliasRegistry.registerAlias("CGLIB", org/apache/ibatis/executor/loader/CglibProxyFactory); typeAliasRegistry.registerAlias("JAVASSIST", org/apache/ibatis/executor/loader/JavassistProxyFactory); languageRegistry.setDefaultDriverClass(org/apache/ibatis/scripting/xmltags/XMLLanguageDriver); languageRegistry.register(org/apache/ibatis/scripting/defaults/RawLanguageDriver); } //设置Configuration,属性变量variables public void setVariables(Properties variables) { this.variables = variables; } }
//BaseBuilder
public abstract class BaseBuilder { public BaseBuilder(Configuration configuration) { this.configuration = configuration; typeAliasRegistry = this.configuration.getTypeAliasRegistry(); typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); } protected final Configuration configuration;//Mybatis配置项 protected final TypeAliasRegistry typeAliasRegistry; protected final TypeHandlerRegistry typeHandlerRegistry; }
来看new XPathParser(reader, true, props, new XMLMapperEntityResolver()这一句构造XPathParser
public class XPathParser { private Document document;//文档 private boolean validation; private EntityResolver entityResolver;//实体解决器 private Properties variables; private XPath xpath; //构造XPathParser public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); document = createDocument(new InputSource(reader)); } //初始化entityResolver,xpath private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) { this.validation = validation; this.entityResolver = entityResolver; this.variables = variables; XPathFactory factory = XPathFactory.newInstance(); xpath = factory.newXPath(); } //创建Document private Document createDocument(InputSource inputSource) { DocumentBuilder builder; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { public void error(SAXParseException exception) throws SAXException { throw exception; } public void fatalError(SAXParseException exception) throws SAXException { throw exception; } public void warning(SAXParseException saxparseexception) throws SAXException { } final XPathParser this$0; { this$0 = XPathParser.this; super(); } }); return builder.parse(inputSource); Exception e; e; throw new BuilderException((new StringBuilder()).append("Error creating document instance. Cause: ").append(e).toString(), e); } //根据表达式返回document对应的XNode public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { Node node = (Node)evaluate(expression, root, XPathConstants.NODE); if(node == null) return null; else return new XNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { return xpath.evaluate(expression, root, returnType); } }
//XNode
public class XNode { public XNode(XPathParser xpathParser, Node node, Properties variables) { this.xpathParser = xpathParser; this.node = node; name = node.getNodeName(); this.variables = variables; attributes = parseAttributes(node); body = parseBody(node); } private Node node; private String name; private String body; private Properties attributes; private Properties variables; private XPathParser xpathParser; }
现在回到XMLConfigBuilder的解析mybatisConfig.xml文件
parseConfiguration(parser.evalNode("/configuration")); private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } }
下面依次来看properties,typeAliases,plugins,objectFactory,objectWrapperFactory
,settings,environments,databaseIdProvider,typeHandlers,mappers的解析初始化
初始化properties
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); } }
在XMLConfigBuilder的构造函数中初始化过Variables,首先加载properties标签中
resource为Properties,如果在构造SqlSessionFactoryBuilder
(sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader))
的过程中,传入了Properties文件,
则覆盖掉resource的Properties。
初始化typeAliases
private void typeAliasesElement(XNode parent) { if(parent != null) { for(Iterator i$ = parent.getChildren().iterator(); i$.hasNext();) { XNode child = (XNode)i$.next(); 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); } } } } }
//TypeAliasRegistry
public class TypeAliasRegistry { //实际上类型别名是存储在一个Map中 //HashMap<String,Classs>,key为alias,Class为类Type private final HashMap TYPE_ALIASES = new HashMap(); public TypeAliasRegistry() { //系统默认类型别名 registerAlias("string", java/lang/String); registerAlias("byte", java/lang/Byte); registerAlias("long", java/lang/Long); registerAlias("short", java/lang/Short); registerAlias("int", java/lang/Integer); registerAlias("integer", java/lang/Integer); registerAlias("double", java/lang/Double); registerAlias("float", java/lang/Float); registerAlias("boolean", java/lang/Boolean); registerAlias("byte[]", [Ljava/lang/Byte;); registerAlias("long[]", [Ljava/lang/Long;); registerAlias("short[]", [Ljava/lang/Short;); registerAlias("int[]", [Ljava/lang/Integer;); registerAlias("integer[]", [Ljava/lang/Integer;); registerAlias("double[]", [Ljava/lang/Double;); registerAlias("float[]", [Ljava/lang/Float;); registerAlias("boolean[]", [Ljava/lang/Boolean;); registerAlias("_byte", Byte.TYPE); registerAlias("_long", Long.TYPE); registerAlias("_short", Short.TYPE); registerAlias("_int", Integer.TYPE); registerAlias("_integer", Integer.TYPE); registerAlias("_double", Double.TYPE); registerAlias("_float", Float.TYPE); registerAlias("_boolean", Boolean.TYPE); registerAlias("_byte[]", [B); registerAlias("_long[]", [J); registerAlias("_short[]", [S); registerAlias("_int[]", [I); registerAlias("_integer[]", [I); registerAlias("_double[]", [D); registerAlias("_float[]", [F); registerAlias("_boolean[]", [Z); registerAlias("date", java/util/Date); registerAlias("decimal", java/math/BigDecimal); registerAlias("bigdecimal", java/math/BigDecimal); registerAlias("biginteger", java/math/BigInteger); registerAlias("object", java/lang/Object); registerAlias("date[]", [Ljava/util/Date;); registerAlias("decimal[]", [Ljava/math/BigDecimal;); registerAlias("bigdecimal[]", [Ljava/math/BigDecimal;); registerAlias("biginteger[]", [Ljava/math/BigInteger;); registerAlias("object[]", [Ljava/lang/Object;); registerAlias("map", java/util/Map); registerAlias("hashmap", java/util/HashMap); registerAlias("list", java/util/List); registerAlias("arraylist", java/util/ArrayList); registerAlias("collection", java/util/Collection); registerAlias("iterator", java/util/Iterator); registerAlias("ResultSet", java/sql/ResultSet); } //类型别名注册器,注册类型别名 public void registerAlias(String alias, Class value) { if(alias == null) throw new TypeException("The parameter alias cannot be null"); String key = alias.toLowerCase(Locale.ENGLISH); if(TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !((Class)TYPE_ALIASES.get(key)).equals(value)) { throw new TypeException((new StringBuilder()).append("The alias '").append(alias).append("' is already mapped to the value '").append(((Class)TYPE_ALIASES.get(key)).getName()).append("'.").toString()); } else { TYPE_ALIASES.put(key, value); return; } } }
//初始化plugins
private void pluginElement(XNode parent) throws Exception { if(parent != null) { Interceptor interceptorInstance; //添加插件configuration.addInterceptor(interceptorInstance) for(Iterator i$ = parent.getChildren().iterator(); i$.hasNext(); configuration.addInterceptor(interceptorInstance)) { XNode child = (XNode)i$.next(); //获取插件类 String interceptor = child.getStringAttribute("interceptor"); //获取插件属性 Properties properties = child.getChildrenAsProperties(); interceptorInstance = (Interceptor)resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); } } }
来看插件的添加
//Configuration
public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); }
//InterceptorChain
public class InterceptorChain { public Object pluginAll(Object target) { for(Iterator i$ = interceptors.iterator(); i$.hasNext();) { Interceptor interceptor = (Interceptor)i$.next(); target = interceptor.plugin(target); } return target; } public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } //实际上是添加到一个List<Interceptor>中 private final List interceptors = new ArrayList(); }
//Interceptor
public interface Interceptor { public abstract Object intercept(Invocation invocation) throws Throwable; public abstract Object plugin(Object obj); public abstract void setProperties(Properties properties); }
//初始化ObjectFactory
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); } }
//ObjectFactory
public interface ObjectFactory { public abstract void setProperties(Properties properties); public abstract Object create(Class class1); public abstract Object create(Class class1, List list, List list1); public abstract boolean isCollection(Class class1); }
//初始化ObjectWrapperFactory
private void objectWrapperFactoryElement(XNode context) throws Exception { if(context != null) { String type = context.getStringAttribute("type"); ObjectWrapperFactory factory = (ObjectWrapperFactory)resolveClass(type).newInstance(); configuration.setObjectWrapperFactory(factory); } }
//ObjectWrapperFactory
public interface ObjectWrapperFactory { public abstract boolean hasWrapperFor(Object obj); public abstract ObjectWrapper getWrapperFor(MetaObject metaobject, Object obj); }
//初始化settings
private void settingsElement(XNode context) throws Exception { if(context != null) { Properties props = context.getChildrenAsProperties(); MetaClass metaConfig = MetaClass.forClass(org/apache/ibatis/session/Configuration); for(Iterator i$ = props.keySet().iterator(); i$.hasNext();) { Object key = i$.next(); if(!metaConfig.hasSetter(String.valueOf(key))) throw new BuilderException((new StringBuilder()).append("The setting ").append(key).append(" is not known. Make sure you spelled it correctly (case sensitive).").toString()); } //配置configuration,是否懒加载,自动生成主键,全局映射缓存,本地以及缓存, configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), Boolean.valueOf(true)).booleanValue()); //proxyFactory configuration.setProxyFactory((ProxyFactory)createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), Boolean.valueOf(false)).booleanValue()); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), Boolean.valueOf(true)).booleanValue()); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), Boolean.valueOf(true)).booleanValue()); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), Boolean.valueOf(true)).booleanValue()); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), Boolean.valueOf(false)).booleanValue()); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), Boolean.valueOf(false)).booleanValue()); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), Boolean.valueOf(false)).booleanValue()); //localCacheScope,一级缓存作用域 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.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), Boolean.valueOf(false)).booleanValue()); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); } }
//初始化environments
private void environmentsElement(XNode context) throws Exception { if(context != null) { if(environment == null) environment = context.getStringAttribute("default"); Iterator i$ = context.getChildren().iterator(); do { if(!i$.hasNext()) break; XNode child = (XNode)i$.next(); String id = child.getStringAttribute("id"); if(isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); javax.sql.DataSource dataSource = dsFactory.getDataSource(); org.apache.ibatis.mapping.Environment.Builder environmentBuilder = (new org.apache.ibatis.mapping.Environment.Builder(id)).transactionFactory(txFactory).dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } while(true); } }
//Environment
public final class Environment { public static class Builder { public Builder transactionFactory(TransactionFactory transactionFactory) { this.transactionFactory = transactionFactory; return this; } public Builder dataSource(DataSource dataSource) { this.dataSource = dataSource; return this; } public String id() { return id; } public Environment build() { return new Environment(id, transactionFactory, dataSource); } private String id; private TransactionFactory transactionFactory; private DataSource dataSource; public Builder(String id) { this.id = id; } } private final String id; private final TransactionFactory transactionFactory; private final DataSource dataSource; }
初始化typeHandler
private void typeHandlerElement(XNode parent) throws Exception { if(parent != null) { for(Iterator i$ = parent.getChildren().iterator(); i$.hasNext();) { XNode child = (XNode)i$.next(); 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); } } } } }
//TypeHandlerRegistry
public final class TypeHandlerRegistry { public TypeHandlerRegistry() { //初始化java,变量,JDBCTYPE,及对应的Handler映射 register(java/lang/Boolean, new BooleanTypeHandler()); register(Boolean.TYPE, new BooleanTypeHandler()); register(JdbcType.BOOLEAN, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); register(java/lang/Byte, new ByteTypeHandler()); register(Byte.TYPE, new ByteTypeHandler()); register(JdbcType.TINYINT, new ByteTypeHandler()); register(java/lang/Short, new ShortTypeHandler()); register(Short.TYPE, new ShortTypeHandler()); register(JdbcType.SMALLINT, new ShortTypeHandler()); register(java/lang/Integer, new IntegerTypeHandler()); register(Integer.TYPE, new IntegerTypeHandler()); register(JdbcType.INTEGER, new IntegerTypeHandler()); register(java/lang/Long, new LongTypeHandler()); register(Long.TYPE, new LongTypeHandler()); register(java/lang/Float, new FloatTypeHandler()); register(Float.TYPE, new FloatTypeHandler()); register(JdbcType.FLOAT, new FloatTypeHandler()); register(java/lang/Double, new DoubleTypeHandler()); register(Double.TYPE, new DoubleTypeHandler()); register(JdbcType.DOUBLE, new DoubleTypeHandler()); register(java/lang/String, new StringTypeHandler()); register(java/lang/String, JdbcType.CHAR, new StringTypeHandler()); register(java/lang/String, JdbcType.CLOB, new ClobTypeHandler()); register(java/lang/String, JdbcType.VARCHAR, new StringTypeHandler()); register(java/lang/String, JdbcType.LONGVARCHAR, new ClobTypeHandler()); register(java/lang/String, JdbcType.NVARCHAR, new NStringTypeHandler()); register(java/lang/String, JdbcType.NCHAR, new NStringTypeHandler()); register(java/lang/String, JdbcType.NCLOB, new NClobTypeHandler()); register(JdbcType.CHAR, new StringTypeHandler()); register(JdbcType.VARCHAR, new StringTypeHandler()); register(JdbcType.CLOB, new ClobTypeHandler()); register(JdbcType.LONGVARCHAR, new ClobTypeHandler()); register(JdbcType.NVARCHAR, new NStringTypeHandler()); register(JdbcType.NCHAR, new NStringTypeHandler()); register(JdbcType.NCLOB, new NClobTypeHandler()); register(java/lang/Object, JdbcType.ARRAY, new ArrayTypeHandler()); register(JdbcType.ARRAY, new ArrayTypeHandler()); register(java/math/BigInteger, new BigIntegerTypeHandler()); register(JdbcType.BIGINT, new LongTypeHandler()); register(java/math/BigDecimal, new BigDecimalTypeHandler()); register(JdbcType.REAL, new BigDecimalTypeHandler()); register(JdbcType.DECIMAL, new BigDecimalTypeHandler()); register(JdbcType.NUMERIC, new BigDecimalTypeHandler()); register([Ljava/lang/Byte;, new ByteObjectArrayTypeHandler()); register([Ljava/lang/Byte;, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler()); register([Ljava/lang/Byte;, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler()); register([B, new ByteArrayTypeHandler()); register([B, JdbcType.BLOB, new BlobTypeHandler()); register([B, JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.LONGVARBINARY, new BlobTypeHandler()); register(JdbcType.BLOB, new BlobTypeHandler()); register(java/lang/Object, UNKNOWN_TYPE_HANDLER); register(java/lang/Object, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); register(java/util/Date, new DateTypeHandler()); register(java/util/Date, JdbcType.DATE, new DateOnlyTypeHandler()); register(java/util/Date, JdbcType.TIME, new TimeOnlyTypeHandler()); register(JdbcType.TIMESTAMP, new DateTypeHandler()); register(JdbcType.DATE, new DateOnlyTypeHandler()); register(JdbcType.TIME, new TimeOnlyTypeHandler()); register(java/sql/Date, new SqlDateTypeHandler()); register(java/sql/Time, new SqlTimeTypeHandler()); register(java/sql/Timestamp, new SqlTimestampTypeHandler()); register(java/lang/Character, new CharacterTypeHandler()); register(Character.TYPE, new CharacterTypeHandler()); } //注册jdbcType和TypeHandler的映射 public void register(JdbcType jdbcType, TypeHandler handler) { //JDBC_TYPE_HANDLER_MAP = new EnumMap(org/apache/ibatis/type/JdbcType); JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler); } public void register(Class javaType, TypeHandler typeHandler) { register(((Type) (javaType)), typeHandler); } //注册javaType和TypeHandler的映射 private void register(Type javaType, TypeHandler typeHandler) { MappedJdbcTypes mappedJdbcTypes = (MappedJdbcTypes)typeHandler.getClass().getAnnotation(org/apache/ibatis/type/MappedJdbcTypes); if(mappedJdbcTypes != null) { JdbcType arr$[] = mappedJdbcTypes.value(); int len$ = arr$.length; for(int i$ = 0; i$ < len$; i$++) { JdbcType handledJdbcType = arr$[i$]; register(javaType, handledJdbcType, typeHandler); } if(mappedJdbcTypes.includeNullJdbcType()) register(javaType, null, typeHandler); } else { register(javaType, null, typeHandler); } } //注册javaTypeClass,jdbcType,typeHandlerClass的映射 public void register(Class javaTypeClass, JdbcType jdbcType, Class typeHandlerClass) { register(((Type) (javaTypeClass)), jdbcType, getInstance(javaTypeClass, typeHandlerClass)); } //获取TypeHandler,实例 public TypeHandler getInstance(Class javaTypeClass, Class typeHandlerClass) { if(javaTypeClass == null) break MISSING_BLOCK_LABEL_69; Constructor c = typeHandlerClass.getConstructor(new Class[] { java/lang/Class }); return (TypeHandler)c.newInstance(new Object[] { javaTypeClass }); } //注册javaTypeClass,jdbcType,typeHandlerClass的映射关系 private void register(Type javaType, JdbcType jdbcType, TypeHandler handler) { if(javaType != null) { Map map = (Map)TYPE_HANDLER_MAP.get(javaType); if(map == null) { map = new HashMap(); //TYPE_HANDLER_MAP,TYPE_HANDLER_MAP<javaType,Map<JdbcType,TypeHandler> TYPE_HANDLER_MAP.put(javaType, map); } map.put(jdbcType, handler); if(reversePrimitiveMap.containsKey(javaType)) register((Type)reversePrimitiveMap.get(javaType), jdbcType, handler); } ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler); } private static final Map reversePrimitiveMap = new HashMap() { private static final long serialVersionUID = 1L; { put(java/lang/Byte, Byte.TYPE); put(java/lang/Short, Short.TYPE); put(java/lang/Integer, Integer.TYPE); put(java/lang/Long, Long.TYPE); put(java/lang/Float, Float.TYPE); put(java/lang/Double, Double.TYPE); put(java/lang/Boolean, Boolean.TYPE); put(java/lang/Character, Character.TYPE); } }; private final Map JDBC_TYPE_HANDLER_MAP = new EnumMap(org/apache/ibatis/type/JdbcType); private final Map TYPE_HANDLER_MAP = new HashMap(); private final TypeHandler UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this); private final Map ALL_TYPE_HANDLERS_MAP = new HashMap(); }
//TypeHandler
public interface TypeHandler { public abstract void setParameter(PreparedStatement preparedstatement, int i, Object obj, JdbcType jdbctype) throws SQLException; public abstract Object getResult(ResultSet resultset, String s) throws SQLException; public abstract Object getResult(ResultSet resultset, int i) throws SQLException; public abstract Object getResult(CallableStatement callablestatement, int i) throws SQLException; }
//JdbcType
public final class JdbcType extends Enum { public static final JdbcType ARRAY; public static final JdbcType BIT; public static final JdbcType TINYINT; public static final JdbcType SMALLINT; public static final JdbcType INTEGER; public static final JdbcType BIGINT; public static final JdbcType FLOAT; public static final JdbcType REAL; public static final JdbcType DOUBLE; public static final JdbcType NUMERIC; public static final JdbcType DECIMAL; public static final JdbcType CHAR; public static final JdbcType VARCHAR; public static final JdbcType LONGVARCHAR; public static final JdbcType DATE; public static final JdbcType TIME; public static final JdbcType TIMESTAMP; public static final JdbcType BINARY; public static final JdbcType VARBINARY; public static final JdbcType LONGVARBINARY; public static final JdbcType NULL; public static final JdbcType OTHER; public static final JdbcType BLOB; public static final JdbcType CLOB; public static final JdbcType BOOLEAN; public static final JdbcType CURSOR; public static final JdbcType UNDEFINED; public static final JdbcType NVARCHAR; public static final JdbcType NCHAR; public static final JdbcType NCLOB; public static final JdbcType STRUCT; public final int TYPE_CODE; private static Map codeLookup; private static final JdbcType $VALUES[]; static { ARRAY = new JdbcType("ARRAY", 0, 2003); BIT = new JdbcType("BIT", 1, -7); TINYINT = new JdbcType("TINYINT", 2, -6); SMALLINT = new JdbcType("SMALLINT", 3, 5); INTEGER = new JdbcType("INTEGER", 4, 4); BIGINT = new JdbcType("BIGINT", 5, -5); FLOAT = new JdbcType("FLOAT", 6, 6); REAL = new JdbcType("REAL", 7, 7); DOUBLE = new JdbcType("DOUBLE", 8, 8); NUMERIC = new JdbcType("NUMERIC", 9, 2); DECIMAL = new JdbcType("DECIMAL", 10, 3); CHAR = new JdbcType("CHAR", 11, 1); VARCHAR = new JdbcType("VARCHAR", 12, 12); LONGVARCHAR = new JdbcType("LONGVARCHAR", 13, -1); DATE = new JdbcType("DATE", 14, 91); TIME = new JdbcType("TIME", 15, 92); TIMESTAMP = new JdbcType("TIMESTAMP", 16, 93); BINARY = new JdbcType("BINARY", 17, -2); VARBINARY = new JdbcType("VARBINARY", 18, -3); LONGVARBINARY = new JdbcType("LONGVARBINARY", 19, -4); NULL = new JdbcType("NULL", 20, 0); OTHER = new JdbcType("OTHER", 21, 1111); BLOB = new JdbcType("BLOB", 22, 2004); CLOB = new JdbcType("CLOB", 23, 2005); BOOLEAN = new JdbcType("BOOLEAN", 24, 16); CURSOR = new JdbcType("CURSOR", 25, -10); UNDEFINED = new JdbcType("UNDEFINED", 26, -2147482648); NVARCHAR = new JdbcType("NVARCHAR", 27, -9); NCHAR = new JdbcType("NCHAR", 28, -15); NCLOB = new JdbcType("NCLOB", 29, 2011); STRUCT = new JdbcType("STRUCT", 30, 2002); $VALUES = (new JdbcType[] { ARRAY, BIT, TINYINT, SMALLINT, INTEGER, BIGINT, FLOAT, REAL, DOUBLE, NUMERIC, DECIMAL, CHAR, VARCHAR, LONGVARCHAR, DATE, TIME, TIMESTAMP, BINARY, VARBINARY, LONGVARBINARY, NULL, OTHER, BLOB, CLOB, BOOLEAN, CURSOR, UNDEFINED, NVARCHAR, NCHAR, NCLOB, STRUCT }); codeLookup = new HashMap(); JdbcType arr$[] = values(); int len$ = arr$.length; for(int i$ = 0; i$ < len$; i$++) { JdbcType type = arr$[i$]; codeLookup.put(Integer.valueOf(type.TYPE_CODE), type); } } }
从上面可以看出;初始化typeHandler,就是初始化javaType,jdbcType,及TypeHandler的映射关系
初始化mappers
private void mapperElement(XNode parent) throws Exception { if(parent != null) { for(Iterator i$ = parent.getChildren().iterator(); i$.hasNext();) { XNode child = (XNode)i$.next(); 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); //加载Mapper的resource文件 InputStream inputStream = Resources.getResourceAsStream(resource); 构建XMLMapperBuilder 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); //mapperInterface信息添加到configuration configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
回到SqlSessionFactoryBuilder,可以看到builder实际上是返回的是DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
总结:
从上,可以出SqlSessionFactoryBuilder做的主要工作是,加载全局配置文件,根据
properties,typeAliases,plugins,objectFactory,objectWrapperFactory
,settings,environments,databaseIdProvider,typeHandlers,mappers配置项,
初始化Configuration,由于mappers解析内容较多,我们放在下一篇来讲。
附:Mapper解析的引篇
//Configuration
public void addMapper(Class type) { mapperRegistry.addMapper(type); }
//MapperRegistry
public class MapperRegistry { public void addMapper(Class type) { boolean loadCompleted; if(!type.isInterface()) break MISSING_BLOCK_LABEL_125; if(hasMapper(type)) throw new BindingException((new StringBuilder()).append("Type ").append(type).append(" is already known to the MapperRegistry.").toString()); loadCompleted = false; knownMappers.put(type, new MapperProxyFactory(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; if(!loadCompleted) knownMappers.remove(type); break MISSING_BLOCK_LABEL_125; Exception exception; exception; if(!loadCompleted) knownMappers.remove(type); throw exception; } private Configuration config; //HashMap<Class,MapperProxyFactory> private final Map knownMappers = new HashMap(); }
//MapperProxyFactory
public class MapperProxyFactory { public MapperProxyFactory(Class mapperInterface) { methodCache = new ConcurrentHashMap(); this.mapperInterface = mapperInterface; } protected Object newInstance(MapperProxy mapperProxy) { //代理生成实例 return Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public Object newInstance(SqlSession sqlSession) { MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } private final Class mapperInterface; private Map methodCache; }
//MapperProxy
public class MapperProxy implements InvocationHandler, Serializable { public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object args[]) throws Throwable { if(java/lang/Object.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else { MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = (MapperMethod)methodCache.get(method); if(mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class mapperInterface; //Map<Method,MapperMethod>,即二级缓存 private final Map methodCache; }
从上可以看出Configuration添加MapperInterface,就是MapperRegistry注册到其
HashMap<Class,MapperProxyFactory>中,MapperProxyFactory是MapperInterface的代理,
生成MapperInterface的MapperProxy代理实例,MapperProxy中利用Map<Method,MapperMethod>,
实现二级缓存
//MapperAnnotationBuilder,MapperInterface的注解处理
public class MapperAnnotationBuilder { public MapperAnnotationBuilder(Configuration configuration, Class type) { String resource = (new StringBuilder()).append(type.getName().replace('.', '/')).append(".java (best guess)").toString(); assistant = new MapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; sqlAnnotationTypes.add(org/apache/ibatis/annotations/Select); sqlAnnotationTypes.add(org/apache/ibatis/annotations/Insert); sqlAnnotationTypes.add(org/apache/ibatis/annotations/Update); sqlAnnotationTypes.add(org/apache/ibatis/annotations/Delete); sqlProviderAnnotationTypes.add(org/apache/ibatis/annotations/SelectProvider); sqlProviderAnnotationTypes.add(org/apache/ibatis/annotations/InsertProvider); sqlProviderAnnotationTypes.add(org/apache/ibatis/annotations/UpdateProvider); sqlProviderAnnotationTypes.add(org/apache/ibatis/annotations/DeleteProvider); } //解析CacheNamespace注解 private void parseCache() { CacheNamespace cacheDomain = (CacheNamespace)type.getAnnotation(org/apache/ibatis/annotations/CacheNamespace); if(cacheDomain != null) assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), Long.valueOf(cacheDomain.flushInterval()), Integer.valueOf(cacheDomain.size()), cacheDomain.readWrite(), null); } //解析CacheNamespaceRef注解 private void parseCacheRef() { CacheNamespaceRef cacheDomainRef = (CacheNamespaceRef)type.getAnnotation(org/apache/ibatis/annotations/CacheNamespaceRef); if(cacheDomainRef != null) assistant.useCacheRef(cacheDomainRef.value().getName()); } private String parseResultMap(Method method) { Class returnType = getReturnType(method); ConstructorArgs args = (ConstructorArgs)method.getAnnotation(org/apache/ibatis/annotations/ConstructorArgs); Results results = (Results)method.getAnnotation(org/apache/ibatis/annotations/Results); TypeDiscriminator typeDiscriminator = (TypeDiscriminator)method.getAnnotation(org/apache/ibatis/annotations/TypeDiscriminator); String resultMapId = generateResultMapName(method); applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator); return resultMapId; } //处理Statement方法method void parseStatement(Method method) {} private final Set sqlAnnotationTypes = new HashSet(); private final Set sqlProviderAnnotationTypes = new HashSet(); private Configuration configuration; private MapperBuilderAssistant assistant; private Class type; }
上一篇: spring加载多个xml配置文件 博客分类: Spring基础 Spring多配置文件xml
下一篇: 用springmvc和hibernate实现简单增删改查 springmvchibernate增删改查数据库web开发
推荐阅读
-
SqlSessionFactory初始化 博客分类: Mybatis mybatis
-
mybatis的paramType 博客分类: 框架-mybatis sqldao
-
DefaultSqlSession第二讲-更新,刷新Statement 博客分类: Mybatis MybatisDefaultSqlSession
-
mybatis xml配置文件读取不了 properties的属性 博客分类: Java mybatis xml 注入不了 ${} properties 数据源
-
mybatis xml配置文件读取不了 properties的属性 博客分类: Java mybatis xml 注入不了 ${} properties 数据源
-
mybatis oracle生成java实体类 博客分类: java mybatisoracle实体类中文注释
-
Mybatis预编译order by 语句无法生效 博客分类: Mybatis Mybatissql
-
Mybatis预编译order by 语句无法生效 博客分类: Mybatis Mybatissql
-
Spring异常之------初始化Aplicationcontext异常,"javax.ejb.Asynchronous"'s signer informat 博客分类: Spring异常 spring异常
-
详解mybatis的mapper.xml编写 博客分类: mybatis javamybatis