建造者模式有四大角色java
具體的實例能夠參考 設計模式整理 node
在Mybatis的初始化的主要工做是加載並解析mybatis-config.xml的配置文件、映射配置文件以及相關的註解信息。由於使用了建造者模式,BashBuilder抽象類即爲建造者接口的角色。它的核心字段內容以下mysql
//Mybatis初始化過程的核心對象,Mybatis中幾乎所有的配置信息會保存到該對象中。該對象在Mybatis初始化過程當中建立且是全局惟一的 protected final Configuration configuration; //定義的別名都會記錄在該對象中 protected final TypeAliasRegistry typeAliasRegistry; //指定數據庫類型與Java類型的轉換器 protected final TypeHandlerRegistry typeHandlerRegistry;
通用方法sql
protected Class<?> resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } }
protected Class<?> resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias); }
protected ResultSetType resolveResultSetType(String alias) { if (alias == null) { return null; } try { return ResultSetType.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e); } }
protected ParameterMode resolveParameterMode(String alias) { if (alias == null) { return null; } try { return ParameterMode.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e); } }
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) { if (typeHandlerAlias == null) { return null; } //經過類型別名映射解析別名 Class<?> type = resolveClass(typeHandlerAlias); //若是type不爲null且type不爲TypeHandler接口的實現類,拋出異常 if (type != null && !TypeHandler.class.isAssignableFrom(type)) { throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface"); } //將type強制轉換成TypeHandler的實現類 @SuppressWarnings( "unchecked" ) // already verified it is a TypeHandler Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type; return resolveTypeHandler(javaType, typeHandlerType); }
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) { if (typeHandlerType == null) { return null; } //從類型處理器註冊器中獲取typeHandlerType類實例對應的TypeHandler對象 TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType); if (handler == null) { //若是handler對象爲null,從類型處理器註冊器中獲取以javaType爲構造參數來構造的typeHandlerType的實例對象 handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType); } return handler; }
它要構建的方法很是多,但跟咱們設計模式整理中試例不一樣的是,它並不針對一種產品進行構建,因此它並無一個抽象方法。這就須要咱們針對它不一樣的子類來分開看它的構建方法。數據庫
XMLConfigBuilder是BaseBuilder衆多子類之一,負責解析mybatis-config.xml配置文件,核心字段以下express
private boolean parsed; //標識是否已經解析過mybatis-config.xml配置文件 private final XPathParser parser; //用於解析mybatis-config.xml配置文件的XPathParser對象 private String environment; //標識<enviroment>配置的名稱,默認讀取<enviroment>標籤的default屬性 private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); //反射工廠,用於建立和緩存反射對象
解析mybatis-config.xml配置文件的入口爲parse()方法設計模式
public Configuration parse() { if (parsed) { //判斷是否已經完成對mybatis-config.xml配置文件的解析 throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //在mybatis-config.xml配置文件中查找<configuration>節點,並開始解析 parseConfiguration(parser.evalNode("/configuration")); return configuration; }
好比有這樣一份mybatis-config.xml文件數組
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties> <property name="dialect" value="mysql" /> </properties> <!-- 引入database.properties文件 --> <properties resource="database.properties"/> <!-- 爲model下面的包設置別名 --> <typeAliases> <package name="com.xxx.model"/> <package name="com.xxx.vo"/> </typeAliases> <!-- 設置運行參數 --> <settings> <!-- 全局映射器啓用緩存 --> <setting name="cacheEnabled" value="false" /> <!-- 查詢時,關閉關聯對象及時加載以提升性能 --> <setting name="lazyLoadingEnabled" value="false" /> <!-- 設置關聯對象加載的形態,此處爲按需加載字段(加載字段由SQL指定),不會加載關聯表的全部字段,以提升性能 --> <setting name="aggressiveLazyLoading" value="false" /> <!-- 對於位置的SQL查詢,容許返回不一樣的結果集以達到通用的效果 --> <setting name="multipleResultSetsEnabled" value="true" /> <!-- 容許使用列標籤代替列明 --> <setting name="useColumnLabel" value="true" /> <!-- 容許使用自定義的主鍵值(好比由程序生成的UUID 32位編碼做爲鍵值), 數據表的pk生成策略將被覆蓋 --> <setting name="useGeneratedKeys" value="false" /> <!-- 給予被嵌套的resultMap以字段-屬性的映射支持 --> <setting name="autoMappingBehavior" value="PARTIAL" /> <!-- 對於批量更新操做緩存SQL以提升性能 --> <setting name="defaultExecutorType" value="REUSE" /> <!-- 數據庫超過25000秒仍未響應則超時 --> <setting name="defaultStatementTimeout" value="25000" /> <!-- 打印查詢語句 --> <!--<setting name="logImpl" value="STDOUT_LOGGING" />--> </settings> </configuration>
private void parseConfiguration(XNode root) { try { //解析<properties>節點 propertiesElement(root.evalNode("properties")); //解析<settings>節點 Properties settings = settingsAsProperties(root.evalNode("settings")); //設置vfsImpl字段 loadCustomVfs(settings); //解析<typeAliases>節點 typeAliasesElement(root.evalNode("typeAliases")); //解析<plugins>節點 pluginElement(root.evalNode("plugins")); //解析<objectFactory>節點 objectFactoryElement(root.evalNode("objectFactory")); //解析<objectWrapperFactory>節點 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //解析<reflectorFactory>節點 reflectorFactoryElement(root.evalNode("reflectorFactory")); //將settings值設置到全局配置中,基本都是對枚舉的操做 settingsElement(settings); //解析<environments>節點 environmentsElement(root.evalNode("environments")); //解析<databaseIdProvider>節點 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //解析<typeHandlers>節點 typeHandlerElement(root.evalNode("typeHandlers")); //解析<mappers>節點 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
private void propertiesElement(XNode context) throws Exception { if (context != null) { //解析<properties>的子節點<property>的name和value屬性,並記錄到Properties中 Properties defaults = context.getChildrenAsProperties(); //解析<properties>的resource和url屬性,這兩個屬性用於肯定properties配置文件的位置 String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); //若是resource屬性和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."); } //加載resource屬性或url屬性指定的properties文件 if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } //與Configuration對象中的variables集合合併 Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } //更新XPathParser和Configuration的variables字段 parser.setVariables(defaults); configuration.setVariables(defaults); } }
private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } //解析<Settings>的子節點<Setting>的name和value屬性,並返回Properties對象 Properties props = context.getChildrenAsProperties(); //建立Configuration對應的MetaClass對象,MetaClass以前有說過是判斷類實例是否有getter,setter屬性的對象 MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); //遍歷setting標籤下的name屬性,並判斷該屬性是否有setter屬性,沒有則拋出異常 for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; }
private void loadCustomVfs(Properties props) throws ClassNotFoundException { //獲取<Setting>裏name爲vfsImpl的值 String value = props.getProperty("vfsImpl"); if (value != null) { String[] clazzes = value.split(","); for (String clazz : clazzes) { if (!clazz.isEmpty()) { @SuppressWarnings("unchecked") Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz); configuration.setVfsImpl(vfsImpl); } } } }
private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { //處理所有子節點 if ("package".equals(child.getName())) { //處理package節點 //獲取指定的包名 String typeAliasPackage = child.getStringAttribute("name"); //經過TypeAliasRegistry掃描制定包中全部的類,並解析@Alias註解,完成別名註冊,後面有詳細的分析代碼 configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { //處理<typeAlias>節點 //獲取指定的別名 String alias = child.getStringAttribute("alias"); //獲取別名對應的名稱 String type = child.getStringAttribute("type"); try { //根據別名對應的名稱進行類裝載,生成對應的Class實例 Class<?> clazz = Resources.classForName(type); if (alias == null) { //若是別名爲null,掃描@Alias註解,完成註冊 typeAliasRegistry.registerAlias(clazz); } else { //若是別名不爲null,註冊別名 typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } }
private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //遍歷全部子節點(即<plugin>節點) //獲取<plugin>節點的interceptor屬性的值 String interceptor = child.getStringAttribute("interceptor"); //獲取<plugin>節點下<properties>配置的信息,並造成Properties對象 Properties properties = child.getChildrenAsProperties(); //經過類型別名映射解析別名,並實例化爲一個攔截器對象 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); //設置攔截器對象的屬性 interceptorInstance.setProperties(properties); //在全局配置中添加攔截器對象 configuration.addInterceptor(interceptorInstance); } } }
private void objectFactoryElement(XNode context) throws Exception { if (context != null) { //獲取<objectFactory>節點的type屬性 String type = context.getStringAttribute("type"); //獲取<objectFactory>節點下配置的信息,並造成Properties對象 Properties properties = context.getChildrenAsProperties(); //經過類型別名映射解析別名,並實例化爲一個對象工廠對象 ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); //設置對象工廠的屬性 factory.setProperties(properties); //在全局配置器中添加對象工廠對象 configuration.setObjectFactory(factory); } }
private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { //獲取<objectWrapperFactory>節點的type屬性 String type = context.getStringAttribute("type"); //經過類型別名映射解析別名,並實例化爲一個對象包裝工廠對象 ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance(); //在全局配置器中添加對象包裝工廠對象 configuration.setObjectWrapperFactory(factory); } }
private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { //獲取<reflectorFactory>節點的type屬性 String type = context.getStringAttribute("type"); //經過類型別名映射解析別名,並實例化爲一個反射工廠對象 ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance(); //在全局配置器中添加反射工廠對象 configuration.setReflectorFactory(factory); } }
private void environmentsElement(XNode context) throws Exception { if (context != null) { //未設置enviroment字段,則獲取<environments>節點的default屬性 if (environment == null) { environment = context.getStringAttribute("default"); } //遍歷子節點(<environment>節點) for (XNode child : context.getChildren()) { //獲取<environment>節點id屬性 String id = child.getStringAttribute("id"); //若是設置的enviroment字段與id相同,有一項爲null則拋出異常 if (isSpecifiedEnvironment(id)) { //解析<transactionManager>節點 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //解析<dataSource>節點 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //經過數據源工廠獲取數據源 DataSource dataSource = dsFactory.getDataSource(); //建立Environment,此處使用Build方式 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); //在全局配置器中添加環境對象(其中封裝了事務工廠以及數據源) configuration.setEnvironment(environmentBuilder.build()); } } } }
關於build方式,請參考 用Build來構建對象的寫法緩存
private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } else if (id == null) { throw new BuilderException("Environment requires an id attribute."); } else if (environment.equals(id)) { return true; } return false; }
private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { //獲取<transactionManager>節點的type屬性 String type = context.getStringAttribute("type"); //獲取<transactionManager>節點下配置的信息,並造成Properties對象 Properties props = context.getChildrenAsProperties(); //經過類型別名映射解析別名,並實例化爲一個事務工廠對象 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); //設置事務工廠的屬性 factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { //獲取<dataSource>節點的type屬性 String type = context.getStringAttribute("type"); //獲取<dataSource>節點下配置的信息,並造成Properties對象 Properties props = context.getChildrenAsProperties(); //經過類型別名映射解析別名,並實例化爲一個數據源工廠對象 DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); //設置數據源工廠的屬性 factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); }
private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { //獲取<databaseIdProvider>節點的type屬性 String type = context.getStringAttribute("type"); //爲了保持兼容性,修改type取值 if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } //獲取<databaseIdProvider>節點下配置的信息,並造成Properties對象 Properties properties = context.getChildrenAsProperties(); //經過類型別名映射解析別名,並實例化爲一個數據庫類型提供者(對哪些數據庫進行支持)對象 databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance(); //設置數據庫類型提供者的屬性 databaseIdProvider.setProperties(properties); } //從全局配置器中取出帶數據源的環境對象 Environment environment = configuration.getEnvironment(); //若是環境對象以及數據庫類型提供者對象都不爲null if (environment != null && databaseIdProvider != null) { //從數據庫類型提供者的屬性中獲取數據庫id(數據庫名稱key對應的value),若是屬性中沒有直接獲取數據庫名稱 String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); //在全局配置器中添加數據庫Id字符串 configuration.setDatabaseId(databaseId); } }
private void typeHandlerElement(XNode parent) throws Exception { if (parent != null) { //若是<typeHandler>節點不爲null,遍歷其全部子節點 for (XNode child : parent.getChildren()) { //若是子節點的名稱爲package if ("package".equals(child.getName())) { //獲取其子節點的name屬性 String typeHandlerPackage = child.getStringAttribute("name"); //將該name屬性爲類名的類與數據庫字段以及類型處理器對象添加到集合中 typeHandlerRegistry.register(typeHandlerPackage); } else { //若是其子節點的名稱不爲package //獲取子節點的javaType屬性 String javaTypeName = child.getStringAttribute("javaType"); //獲取子節點的jdbcType屬性 String jdbcTypeName = child.getStringAttribute("jdbcType"); //獲取子節點的handler屬性 String handlerTypeName = child.getStringAttribute("handler"); //經過類型別名映射解析別名,獲取java類型的類實例 Class<?> javaTypeClass = resolveClass(javaTypeName); //根據jdbcType屬性獲取一個jdbcType的枚舉 JdbcType jdbcType = resolveJdbcType(jdbcTypeName); //經過類型別名映射解析別名,獲取類型處理器的類實例 Class<?> typeHandlerClass = resolveClass(handlerTypeName); //若是java類型的類實例不爲null if (javaTypeClass != null) { //若是jdbcType的枚舉爲null if (jdbcType == null) { //將java類型,空的數據庫字段類型,類型處理器對象添加到集合中 typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { //不然將java類型,數據庫字段類型,類型處理器對象添加到集合中 typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { //若是java類型的類實例爲null,將空的java類型,數據庫字段類型,類型處理器對象添加到集合中 typeHandlerRegistry.register(typeHandlerClass); } } } } }
private void mapperElement(XNode parent) throws Exception { if (parent != null) { //若是<mappers>節點不爲null,遍歷該節點的子節點 for (XNode child : parent.getChildren()) { //若是子節點名稱爲package if ("package".equals(child.getName())) { //獲取該子節點的name屬性 String mapperPackage = child.getStringAttribute("name"); //在全局配置器中增長Mappers屬性爲該子節點的name屬性 configuration.addMappers(mapperPackage); } else { //若是子節點的名稱不爲package //獲取子節點的resource屬性 String resource = child.getStringAttribute("resource"); //獲取子節點的url屬性 String url = child.getStringAttribute("url"); //獲取子節點的class屬性 String mapperClass = child.getStringAttribute("class"); //因爲這三種屬性互斥,只能出現一種屬性,當爲resource屬性時 if (resource != null && url == null && mapperClass == null) { //將resource屬性放入錯誤上下文的resource屬性中 ErrorContext.instance().resource(resource); //打開resource的URL鏈接,獲取數據流 InputStream inputStream = Resources.getResourceAsStream(resource); //建立XMLMapperBuilder對象,解析映射配置文件 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); //當爲url屬性時 } else if (resource == null && url != null && mapperClass == null) { //將url屬性放入錯誤上下文的resource屬性中 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) { //若是<mapper>節點指定了class屬性,則向全局配置器中註冊該接口 Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
在TypeAliasRegistry下網絡
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>(); //類型別名,在構造函數中將各類基本類型放入了HashMap中
public void registerAliases(String packageName){ registerAliases(packageName, Object.class); }
public void registerAliases(String packageName, Class<?> superType){ //建立一個工具類對象 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); //查找包名下的全部資源,若是爲.class,則添加到該工具對象的匹配集合中 resolverUtil.find(new ResolverUtil.IsA(superType), packageName); //獲取該工具對象的匹配集合 Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses(); //遍歷該集合 for(Class<?> type : typeSet){ // Ignore inner classes and interfaces (including package-info.java) // Skip also inner classes. See issue #6 //若是該集合項不是匿名類,接口,成員類(類內部的類) if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) { //將該集合項添加到別名映射中,若是有@Alias註解,則以該註解的value做爲別名,若是沒有則以類名(不包含包名)做爲別名,別名爲key,類實例爲value registerAlias(type); } } }
public void registerAlias(Class<?> type) { //得到類的名稱(不包含包名),作爲別名 String alias = type.getSimpleName(); //得到該類的@Alias註解 Alias aliasAnnotation = type.getAnnotation(Alias.class); if (aliasAnnotation != null) { //若是該註解不爲null,獲取該註解的配置value作爲別名 alias = aliasAnnotation.value(); } //將別名與類實例添加爲map映射 registerAlias(alias, type); }
public void registerAlias(String alias, Class<?> value) { if (alias == null) { throw new TypeException("The parameter alias cannot be null"); } // issue #748 //將別名英文大寫字符轉成小寫 String key = alias.toLowerCase(Locale.ENGLISH); //若是類型別名Map中已經有了該別名的鍵,且值不爲null,又不爲value,拋出異常 if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'."); } //將key,value加入到類型別名映射中 TYPE_ALIASES.put(key, value); }
public <T> Class<T> resolveAlias(String string) { try { if (string == null) { return null; } // issue #748 //將別名英文大寫字符轉成小寫 String key = string.toLowerCase(Locale.ENGLISH); Class<T> value; //類型別名映射中是否包含該別名,包含則取出該別名的Class實例 if (TYPE_ALIASES.containsKey(key)) { value = (Class<T>) TYPE_ALIASES.get(key); } else { //不包含則經過反射獲取一個類實例 value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } }
在ResolverUtil下
//匹配項集合 private Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>();
public ResolverUtil<T> find(Test test, String packageName) { //把包名的.號替換成/,獲取路徑 String path = getPackagePath(packageName); try { //以單例模式獲取一個VFS子類的實例,並用該實例將path下的全部資源(包括全部文件夾及子文件夾)放入列表中 List<String> children = VFS.getInstance().list(path); //遍歷該列表 for (String child : children) { //若是該列表項以.class結尾 if (child.endsWith(".class")) { //若是該列表項爲test的子類,將其添加到匹配項matches集合中 addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); } return this; }
protected void addIfMatching(Test test, String fqn) { try { //獲取類的名稱(包含包名) String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); //獲得類裝載器 ClassLoader loader = getClassLoader(); if (log.isDebugEnabled()) { log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); } //裝載該類,獲得該類的類實例 Class<?> type = loader.loadClass(externalName); //若是該類實例不爲null if (test.matches(type)) { //將其添加到匹配項集合中 matches.add((Class<T>) type); } } catch (Throwable t) { log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: " + t.getMessage()); } }
@Override public boolean matches(Class<?> type) { //返回type實例是否爲null,因爲調用處parent爲Object.class,必然爲其父類 return type != null && parent.isAssignableFrom(type); }
public Set<Class<? extends T>> getClasses() { return matches; }
在VFS中,VFS表示虛擬文件系統,是一個抽象類,因此要獲取它的實例只能獲取一個它的子類的實例
//由addimplclass(class)方法添加實現的列表,由用戶擴展使用 public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<Class<? extends VFS>>();
//內置的子類 public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
public static VFS getInstance() { //返回VFS容器的實例,這是一個單例模式的容器 return VFSHolder.INSTANCE; }
private static class VFSHolder { static final VFS INSTANCE = createVFS(); @SuppressWarnings("unchecked") static VFS createVFS() { //建立一個VFS子類的列表 List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>(); //將全部可能的子類所有添加到列表中 impls.addAll(USER_IMPLEMENTATIONS); impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS)); //遍歷全部的子類,直到有一個能實例化成對象的 VFS vfs = null; for (int i = 0; vfs == null || !vfs.isValid(); i++) { Class<? extends VFS> impl = impls.get(i); try { vfs = impl.newInstance(); if (vfs == null || !vfs.isValid()) { if (log.isDebugEnabled()) { log.debug("VFS implementation " + impl.getName() + " is not valid in this environment."); } } } catch (InstantiationException e) { log.error("Failed to instantiate " + impl, e); return null; } catch (IllegalAccessException e) { log.error("Failed to instantiate " + impl, e); return null; } } if (log.isDebugEnabled()) { log.debug("Using VFS adapter " + vfs.getClass().getName()); } return vfs; } }
public List<String> list(String path) throws IOException { List<String> names = new ArrayList<String>(); for (URL url : getResources(path)) { names.addAll(list(url, path)); } return names; }
protected static List<URL> getResources(String path) throws IOException { //經過JVM裝載器裝載path下的資源 return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path)); }
在DefaultVFS中
private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 }; //jar文件的魔數
@Override public List<String> list(URL url, String path) throws IOException { InputStream is = null; try { List<String> resources = new ArrayList<String>(); //查找指定地址的jar包的url URL jarUrl = findJarForResource(url); if (jarUrl != null) { //若是該url不爲null,打開鏈接,獲得傳輸流 is = jarUrl.openStream(); if (log.isDebugEnabled()) { log.debug("Listing " + url); } //拿到jar包裏面全部的文件列表 resources = listResources(new JarInputStream(is), path); } else { //若是該url爲null //建立一個子List List<String> children = new ArrayList<String>(); try { //若是該url自己就是一個jar文件地址 if (isJar(url)) { // Some versions of JBoss VFS might give a JAR stream even if the resource // referenced by the URL isn't actually a JAR //url打開鏈接,創建數據流 is = url.openStream(); //以該數據流創建一個jar包的數據流 JarInputStream jarInput = new JarInputStream(is); if (log.isDebugEnabled()) { log.debug("Listing " + url); } //遍歷jar包中的全部資源 for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;) { if (log.isDebugEnabled()) { log.debug("Jar entry: " + entry.getName()); } //將全部的資源名稱添加到子List中 children.add(entry.getName()); } jarInput.close(); } else { //若是自己不爲一個jar文件的url /* * Some servlet containers allow reading from directory resources like a * text file, listing the child resources one per line. However, there is no * way to differentiate between directory and file resources just by reading * them. To work around that, as each line is read, try to look it up via * the class loader as a child of the current resource. If any line fails * then we assume the current resource is not a directory. */ //打開鏈接建立一個數據流 is = url.openStream(); //建立一個緩存讀取器 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); //建立一個多行列表 List<String> lines = new ArrayList<String>(); //遍歷讀取器讀取出來的每一行 for (String line; (line = reader.readLine()) != null;) { if (log.isDebugEnabled()) { log.debug("Reader entry: " + line); } //將每一行添加到行列表中 lines.add(line); //若是JVM裝載器裝載的該行資源爲空,清空行列表,退出遍歷 if (getResources(path + "/" + line).isEmpty()) { lines.clear(); break; } } //若是該行列表不爲空 if (!lines.isEmpty()) { if (log.isDebugEnabled()) { log.debug("Listing " + url); } //將行列表添加到子列表中 children.addAll(lines); } } } catch (FileNotFoundException e) { /* * For file URLs the openStream() call might fail, depending on the servlet * container, because directories can't be opened for reading. If that happens, * then list the directory directly instead. */ //若是該url的協議爲文件協議 if ("file".equals(url.getProtocol())) { //建立該文件對象 File file = new File(url.getFile()); if (log.isDebugEnabled()) { log.debug("Listing directory " + file.getAbsolutePath()); } //若是該對象爲一個文件夾 if (file.isDirectory()) { if (log.isDebugEnabled()) { log.debug("Listing " + url); } //將該文件夾的文件數組轉成列表並賦給子列表 children = Arrays.asList(file.list()); } } else { // No idea where the exception came from so rethrow it throw e; } } //將該url轉成字符串 String prefix = url.toExternalForm(); //若是該字符串不以/結尾,添加/結尾 if (!prefix.endsWith("/")) { prefix = prefix + "/"; } //遍歷子列表 for (String child : children) { //拼接path與子列表項 String resourcePath = path + "/" + child; //將拼接後的路徑添加到資源列表中 resources.add(resourcePath); //對拼接後的子路徑生成新的url URL childUrl = new URL(prefix + child); //遞歸調用,查找子路徑的所有資源放入資源列表中 resources.addAll(list(childUrl, resourcePath)); } } return resources; } finally { if (is != null) { try { is.close(); } catch (Exception e) { // Ignore } } } }
protected URL findJarForResource(URL url) throws MalformedURLException { if (log.isDebugEnabled()) { log.debug("Find JAR URL: " + url); } // If the file part of the URL is itself a URL, then that URL probably points to the JAR try { for (;;) { //不斷循環查找一個可以以文件構建的url實例 url = new URL(url.getFile()); if (log.isDebugEnabled()) { log.debug("Inner URL: " + url); } } } catch (MalformedURLException e) { // This will happen at some point and serves as a break in the loop } //將找到的url以字符串形式轉換爲StringBuilder StringBuilder jarUrl = new StringBuilder(url.toExternalForm()); //檢查該StringBuilder是否以.jar結尾 int index = jarUrl.lastIndexOf(".jar"); if (index >= 0) { jarUrl.setLength(index + 4); if (log.isDebugEnabled()) { log.debug("Extracted JAR URL: " + jarUrl); } } else { if (log.isDebugEnabled()) { log.debug("Not a JAR: " + jarUrl); } return null; } // Try to open and test it try { //以該.jar結尾的字符串構建一個新的url實例 URL testUrl = new URL(jarUrl.toString()); //根據魔數判斷是不是jar文件,返回該url if (isJar(testUrl)) { return testUrl; } else { //若是魔數判斷不爲jar文件,卻以jar結尾 // WebLogic fix: check if the URL's file exists in the filesystem. if (log.isDebugEnabled()) { log.debug("Not a JAR: " + jarUrl); } //將testUrl的文件路徑字符串替換StringBuilder的全部字符串 jarUrl.replace(0, jarUrl.length(), testUrl.getFile()); //建立一個該文件路徑的文件對象 File file = new File(jarUrl.toString()); //若是該文件不存在,生成一個新文件 if (!file.exists()) { try { file = new File(URLEncoder.encode(jarUrl.toString(), "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Unsupported encoding? UTF-8? That's unpossible."); } } if (file.exists()) { if (log.isDebugEnabled()) { log.debug("Trying real file: " + file.getAbsolutePath()); } testUrl = file.toURI().toURL(); if (isJar(testUrl)) { return testUrl; } } } } catch (MalformedURLException e) { log.warn("Invalid JAR URL: " + jarUrl); } if (log.isDebugEnabled()) { log.debug("Not a JAR: " + jarUrl); } return null; }
protected boolean isJar(URL url) { return isJar(url, new byte[JAR_MAGIC.length]); }
protected boolean isJar(URL url, byte[] buffer) { InputStream is = null; try { //打開url的鏈接,獲得傳輸數據流 is = url.openStream(); //將前4位讀取到字節數組中 is.read(buffer, 0, JAR_MAGIC.length); //若是前4位與jar文件類型的魔數相同,返回true if (Arrays.equals(buffer, JAR_MAGIC)) { if (log.isDebugEnabled()) { log.debug("Found JAR: " + url); } return true; } } catch (Exception e) { // Failure to read the stream means this is not a JAR } finally { if (is != null) { try { is.close(); } catch (Exception e) { // Ignore } } } return false; }
protected List<String> listResources(JarInputStream jar, String path) throws IOException { //若是path不以/開頭,則增長/開頭 if (!path.startsWith("/")) { path = "/" + path; } //若是path不以/結尾,則增長/結尾 if (!path.endsWith("/")) { path = path + "/"; } // Iterate over the entries and collect those that begin with the requested path List<String> resources = new ArrayList<String>(); //遍歷jar包裏面的全部資源 for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) { //若是該資源不是一個文件夾 if (!entry.isDirectory()) { //獲取該資源的名稱 String name = entry.getName(); //若是該名稱不以/開頭,則添加/開頭 if (!name.startsWith("/")) { name = "/" + name; } //若是該名稱以path開頭 if (name.startsWith(path)) { if (log.isDebugEnabled()) { log.debug("Found resource: " + name); } //去除開頭/,並添加到List中 resources.add(name.substring(1)); } } } return resources; }
在Resources中
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper(); //類加載器包裝器
public static Class<?> classForName(String className) throws ClassNotFoundException { return classLoaderWrapper.classForName(className); }
public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream(null, resource); }
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } return in; }
在ClassLoaderWrapper中
public Class<?> classForName(String name) throws ClassNotFoundException { return classForName(name, getClassLoaders(null)); }
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException { //遍歷類加載器數組 for (ClassLoader cl : classLoader) { if (null != cl) { try { //若是該類加載器不爲null,使用該類加載器加載類實例,並初始化 Class<?> c = Class.forName(name, true, cl); if (null != c) { //若是該類實例不爲null,返回該類實例 return c; } } catch (ClassNotFoundException e) { // we'll ignore this until all classloaders fail to locate the class } } } throw new ClassNotFoundException("Cannot find class: " + name); }
ClassLoader[] getClassLoaders(ClassLoader classLoader) { //返回一個類加載器數組,包括各類可能的類加載器 return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader}; }
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader)); }
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { //遍歷類加載器數組 for (ClassLoader cl : classLoader) { if (null != cl) { // try to find the resource as passed //根據resource字符串獲取一個URL,經過該URL打開鏈接,產生一個數據流 InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource //若是未打開URL鏈接,給resource加上/前綴,從新打開鏈接,生成數據流 if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null; }
在VendorDatabaseIdProvider中
@Override public String getDatabaseId(DataSource dataSource) { if (dataSource == null) { throw new NullPointerException("dataSource cannot be null"); } try { return getDatabaseName(dataSource); } catch (Exception e) { log.error("Could not get a databaseId from dataSource", e); } return null; }
private String getDatabaseName(DataSource dataSource) throws SQLException { //獲取數據庫的產品名稱 String productName = getDatabaseProductName(dataSource); //若是設置的屬性對象不爲null if (this.properties != null) { //遍歷該屬性對象 for (Map.Entry<Object, Object> property : properties.entrySet()) { //若是數據庫的產品名稱中包含了屬性對象的key if (productName.contains((String) property.getKey())) { //返回屬性對象的value return (String) property.getValue(); } } //沒有匹配到,返回null return null; } //若是設置的屬性對象爲null,返回數據庫的產品名稱 return productName; }
private String getDatabaseProductName(DataSource dataSource) throws SQLException { Connection con = null; try { //經過數據源獲取鏈接 con = dataSource.getConnection(); //經過鏈接獲取數據庫元數據 DatabaseMetaData metaData = con.getMetaData(); //返回數據庫的產品名稱 return metaData.getDatabaseProductName(); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { // ignored } } } }
在TypeHandlerRegistry中,TypeHandlerRegistry是一個類型處理器註冊器,在構造函數中註冊了一系列的java類型和數據庫字段類型的映射關係
//Java類型與數據庫字段的映射,value爲數據庫字段與其類型處理器之間的映射 private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap(); //空映射
//全部類型與類型處理器的映射 private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
public void register(String packageName) { //建立一個工具類對象 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); //查找包名下的全部資源,若是爲TypeHandler.class,則添加到該工具對象的匹配集合中 resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName); //獲取該工具對象的匹配集合 Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses(); //遍歷該集合 for (Class<?> type : handlerSet) { //Ignore inner classes and interfaces (including package-info.java) and abstract classes //若是該集合項不是匿名類,接口,抽象類 if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { //根據實際狀況(包含集合項爲null),將集合項與數據庫字段類型(包括數據庫字段類型爲null)與類型處理器對象註冊到集合中 register(type); } } }
public void register(Class<?> typeHandlerClass) { boolean mappedTypeFound = false; //獲取類實例的@MappedTypes標籤 MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class); if (mappedTypes != null) { //若是打上了該標籤,遍歷標籤的value(該value爲一個Class數組) for (Class<?> javaTypeClass : mappedTypes.value()) { //註冊java類型,以及類型處理器類數據庫字段類型(經過@MappedJdbcTypes標籤識別),類型處理器到集合中 register(javaTypeClass, typeHandlerClass); //表示發現了java類型與數據庫字段類型的映射 mappedTypeFound = true; } } //若是未發現java類型與數據庫字段類型的映射 if (!mappedTypeFound) { //註冊null類型,空的數據庫類型以及類型處理器類實例以無參構造器進行構建的類型處理器對象到集合中 register(getInstance(null, typeHandlerClass)); } }
public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) { register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass)); }
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) { //若是構造參數類實例不爲null if (javaTypeClass != null) { try { //獲取單個類實例的類型處理器構造器 Constructor<?> c = typeHandlerClass.getConstructor(Class.class); //返回以單參類型處理器構造器構造的類型處理器實例對象 return (TypeHandler<T>) c.newInstance(javaTypeClass); } catch (NoSuchMethodException ignored) { // ignored } catch (Exception e) { throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e); } } //若是構造參數類實例爲null try { //獲取無參類型處理器構造器 Constructor<?> c = typeHandlerClass.getConstructor(); //返回無參類型處理器構造器構造的類型處理器實例對象 return (TypeHandler<T>) c.newInstance(); } catch (Exception e) { throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e); } }
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) { //獲取類型處理器類實例的@MappedJdbcTypes標籤 MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class); if (mappedJdbcTypes != null) { //若是該類打上了該標籤,遍歷該標籤的value(value爲JdbcType數組) for (JdbcType handledJdbcType : mappedJdbcTypes.value()) { //將java類型與每個數據庫類型註冊到集合中 register(javaType, handledJdbcType, typeHandler); } //若是該標籤包含空的數據庫類型,默認爲false if (mappedJdbcTypes.includeNullJdbcType()) { //將java類型與空的數據庫類型註冊到集合中 register(javaType, null, typeHandler); } } else { //若是該類沒有打上@MappedJdbcTypes標籤,將java類型與空的數據庫類型註冊到集合中 register(javaType, null, typeHandler); } }
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) { if (javaType != null) { //若是java類型不爲null,從java與數據庫字段類型映射中拿區該java類型的數據庫字段與其處理器之間的映射map Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType); //若是該映射爲null或者是一個空映射 if (map == null || map == NULL_TYPE_HANDLER_MAP) { //生成新的映射對象 map = new HashMap<JdbcType, TypeHandler<?>>(); //將該新的對象與java類型放入java與數據庫字段類型映射中 TYPE_HANDLER_MAP.put(javaType, map); } //將數據庫字段類型與類型處理器放入該映射中 map.put(jdbcType, handler); } //將類型處理器的類實例與類型處理器對象添加到全部類型與處理器之間的映射中 ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler); }
public <T> void register(TypeHandler<T> typeHandler) { boolean mappedTypeFound = false; //獲取類型處理器的@MappedTypes標籤 MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class); if (mappedTypes != null) { //若是打上了該標籤,遍歷該標籤的value(value爲一個Class數組) for (Class<?> handledType : mappedTypes.value()) { //註冊處理類型,以及類型處理器類數據庫字段類型(經過@MappedJdbcTypes標籤識別),類型處理器到集合中 register(handledType, typeHandler); //表示處理類型與數據庫字段類型映射已經發現 mappedTypeFound = true; } } // @since 3.1.0 - try to auto-discover the mapped type //若是處理類型與數據庫字段類型映射沒有被發現且類型處理器對象屬於TypeReference實例 if (!mappedTypeFound && typeHandler instanceof TypeReference) { try { //將類型處理器對象強制轉換成類型引用對象 TypeReference<T> typeReference = (TypeReference<T>) typeHandler; //註冊該引用對象引用的原生類型,以及類型處理器類數據庫字段類型(經過@MappedJdbcTypes標籤識別),類型處理器到集合中 register(typeReference.getRawType(), typeHandler); //表示引用對象引用的原生類型與數據庫字段類型映射被發現 mappedTypeFound = true; } catch (Throwable t) { // maybe users define the TypeReference with a different type and are not assignable, so just ignore it } } //若是以上都沒發現映射關係 if (!mappedTypeFound) { //註冊null類型與空數據庫字段類型與類型處理器對象到集合中 register((Class<T>) null, typeHandler); } }
XMLMapperBuilder也是BaseBuilder抽象類的子類之一,負責解析映射配置文件,在XMLConfigBuilder.mapperElement()方法中被實例化。好比有一份mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cloud.user.dao.BaseDao"> <insert id="insert" parameterType="com.cloud.model.base.BaseEntity"> INSERT INTO ${tableName} ( <if test="idNames != null and idNames.length > 0"> <foreach collection="idNames" index="index" item="idName" separator=","> ${idName} </foreach> </if> <if test="idNames != null and idNames.length > 0 and columnNames != null and columnNames.length > 0"> , </if> <if test="columnNames != null and columnNames.length > 0"> <foreach collection="columnNames" index="index" item="columnName" separator=","> ${columnName} </foreach> </if> ) VALUES ( <if test="idValues != null and idValues.length > 0"> <foreach collection="idValues" index="index" item="idValue" separator=","> #{idValue} </foreach> </if> <if test="idValues != null and idValues.length > 0 and columnValues != null and columnValues.length > 0"> , </if> <if test="columnValues != null and columnValues.length > 0"> <foreach collection="columnValues" index="index" item="columnValue" separator=","> #{columnValue} </foreach> </if> ) </insert> <update id="update" parameterType="com.cloud.model.base.BaseEntity"> UPDATE ${tableName} <set> <foreach collection="columnMap" index="columnKey" item="columnValue" separator=","> ${columnKey}=#{columnValue} </foreach> </set> <where> <foreach collection="idMap" index="idKey" item="idValue" separator="AND"> ${idKey}=#{idValue} </foreach> </where> </update> <delete id="delete" parameterType="com.cloud.model.base.BaseEntity"> DELETE FROM ${tableName} <where> <foreach collection="idMap" index="idKey" item="idValue" separator="AND"> ${idKey}=#{idValue} </foreach> </where> </delete> </mapper>
XMLMapperBuilder.parse()方法是解析映射文件的入口
private final MapperBuilderAssistant builderAssistant; //建造者助理對象
private final Map<String, XNode> sqlFragments; //sql片斷映射
public void parse() { //判斷是否已經加載過該映射文件 if (!configuration.isResourceLoaded(resource)) { //解析<mapper>節點 configurationElement(parser.evalNode("/mapper")); //將該映射文件放入全局配置的已載入集合中 configuration.addLoadedResource(resource); //註冊Mapper接口 bindMapperForNamespace(); } //處理configurationElement()方法中解析失敗的<resultMap>節點 parsePendingResultMaps(); //處理configurationElement()方法中解析失敗的<cache-ref>節點 parsePendingCacheRefs(); //處理configurationElement()方法中解析失敗的SQL語句節點 parsePendingStatements(); }
private void configurationElement(XNode context) { try { //獲取<mapper>節點的namespace屬性 String namespace = context.getStringAttribute("namespace"); //若是該namespace屬性爲空則拋出異常 if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } //將該namespace屬性放入助理對象的當前namespace屬性 builderAssistant.setCurrentNamespace(namespace); //解析<cache-ref>節點 cacheRefElement(context.evalNode("cache-ref")); //解析<cache>節點 cacheElement(context.evalNode("cache")); //解析<parameterMap>節點(以廢棄,不作代碼分析) parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析<resultMap>節點 resultMapElements(context.evalNodes("/mapper/resultMap")); //解析<sql>節點 sqlElement(context.evalNodes("/mapper/sql")); //解析<select>、<insert>、<update>、<delete>節點 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
private void cacheRefElement(XNode context) { if (context != null) { //若是<cache-ref>節點不爲null,將當前命名空間與節點取得的namespace屬性放入到命名空間緩存中 configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); //使用助理對象和節點的namespace屬性來構造一個CacheRefResolver對象 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) { //獲取<cache>節點的type屬性,默認值是PERPETUAL String type = context.getStringAttribute("type", "PERPETUAL"); //從類型別名映射中取出該type屬性所對應的緩存類實例 Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); //獲取<cache>節點的eviction屬性,默認值是LRU String eviction = context.getStringAttribute("eviction", "LRU"); //從類型別名映射中取出該eviction屬性對應的緩存類實例 Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); //獲取<cache>節點的flushInterval屬性 Long flushInterval = context.getLongAttribute("flushInterval"); //獲取<cache>節點的size屬性 Integer size = context.getIntAttribute("size"); //獲取<cache>節點的readOnly屬性,默認值false boolean readWrite = !context.getBooleanAttribute("readOnly", false); //獲取<cache>節點的blocking屬性,默認值false boolean blocking = context.getBooleanAttribute("blocking", false); //獲取<cache>下的全部子節點,並轉化爲一個Properties對象 Properties props = context.getChildrenAsProperties(); //經過以上屬性生成Cache實例對象,並添加到全局配置信息中 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } }
private void resultMapElements(List<XNode> list) throws Exception { //遍歷全部的<resultMap>節點(由於<resultMap>節點可能不止一個) 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 { //將節點的id值放入錯誤上下文的activity屬性 ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); //獲取<resultMap>節點的id屬性, 默認值會拼裝全部父節點的 id 或 value 或 property String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); //獲取<resultMap>節點的type屬性,默認值會被指定爲ofType屬性,resultType屬性,javaType屬性 String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); //獲取<resultMap>節點的extends屬性 String extend = resultMapNode.getStringAttribute("extends"); //獲取<resultMap>節點的autoMapping屬性 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); //經過類型別名映射獲取type屬性爲名稱的Class實例 Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; //定義一個ResultMapping列表(ResultMapping表明<resultMap>節點下的全部子節點,屬性定義見後面),該集合用於記錄解析的結果 List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); //將額外的resultMapping集合添加進來 resultMappings.addAll(additionalResultMappings); //獲取<resultMap>的子節點 List<XNode> resultChildren = resultMapNode.getChildren(); //遍歷全部子節點 for (XNode resultChild : resultChildren) { //若是子節點的名稱爲constructor if ("constructor".equals(resultChild.getName())) { //解析<constructor>子節點 processConstructorElement(resultChild, typeClass, resultMappings); //若是子節點的名稱爲discriminator } else if ("discriminator".equals(resultChild.getName())) { //解析<discriminator>子節點 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { //處理<id>、<result>、<association>、<collection>等節點 List<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); //若是是<id>節點,向flags集合中添加ID枚舉 } //建立ResultMapping對象,並添加到集合中 resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { //建立ResultMap對象,並添加到全局配置信息中,ResultMap對象的屬性信息見後面的代碼 return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } }
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { //獲取<constructor>的全部子節點 List<XNode> argChildren = resultChild.getChildren(); //遍歷<constructor>的子節點 for (XNode argChild : argChildren) { //產生一個結果標誌列表對象 List<ResultFlag> flags = new ArrayList<ResultFlag>(); //將結果標誌之一的CONSTRUCTOR標誌(ResultFlag是一個枚舉)加入結果標誌列表中 flags.add(ResultFlag.CONSTRUCTOR); //若是子節點的名稱爲idArg if ("idArg".equals(argChild.getName())) { //將ID標誌加入結果標誌列表中 flags.add(ResultFlag.ID); } //根據子節點的各個屬性構建ResultMapping對象,並添加到集合中 resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } }
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception { String property; //若是標誌列表中包含CONSTRUCTOR if (flags.contains(ResultFlag.CONSTRUCTOR)) { //獲取節點的name屬性 property = context.getStringAttribute("name"); } else { //不然獲取節點的property屬性 property = context.getStringAttribute("property"); } //獲取節點的column屬性 String column = context.getStringAttribute("column"); //獲取節點的javaType屬性 String javaType = context.getStringAttribute("javaType"); //獲取節點的jdbcType屬性 String jdbcType = context.getStringAttribute("jdbcType"); //獲取節點的select屬性 String nestedSelect = context.getStringAttribute("select"); //獲取節點的resultMap屬性,默認值爲嵌套的resultMap的Id值 String nestedResultMap = context.getStringAttribute("resultMap", processNestedResultMappings(context, Collections.<ResultMapping> emptyList())); //獲取節點的notNullColumn屬性 String notNullColumn = context.getStringAttribute("notNullColumn"); //獲取節點的columnPrefix屬性 String columnPrefix = context.getStringAttribute("columnPrefix"); //獲取節點的typeHandler屬性 String typeHandler = context.getStringAttribute("typeHandler"); //獲取節點的resultSet屬性 String resultSet = context.getStringAttribute("resultSet"); //獲取節點的foreignColumn屬性 String foreignColumn = context.getStringAttribute("foreignColumn"); //獲取節點的tetchType屬性 boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager")); //在類型別名映射中獲取javaType屬性對應的Java類實例 Class<?> javaTypeClass = resolveClass(javaType); //在類型別名映射中獲取typeHandler屬性對應的類型處理器類實例 @SuppressWarnings("unchecked") Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); //在類型別名映射中獲取jdbcType屬性對應的數據庫字段類型的枚舉 JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); //建立ResultMapping對象 return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy); }
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception { //若是該節點名稱爲assoclation或者collection或者case if ("association".equals(context.getName()) || "collection".equals(context.getName()) || "case".equals(context.getName())) { //若是該節點的select屬性爲null if (context.getStringAttribute("select") == null) { //解析該節點,獲取解析結果 ResultMap resultMap = resultMapElement(context, resultMappings); //返回該解析結果的Id return resultMap.getId(); } } return null; }
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { //獲取<discriminator>節點的column屬性 String column = context.getStringAttribute("column"); //獲取<discriminator>節點的javaType屬性 String javaType = context.getStringAttribute("javaType"); //獲取<discriminator>節點的jdbcType屬性 String jdbcType = context.getStringAttribute("jdbcType"); //獲取<discriminator>節點的typeHandler屬性 String typeHandler = context.getStringAttribute("typeHandler"); //從類型別名映射中獲取javaType屬性對應的java類型類實例 Class<?> javaTypeClass = resolveClass(javaType); //從類型別名映射中獲取typeHandler屬性對應的類型處理器類實例 @SuppressWarnings("unchecked") Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); //從類型別名映射中獲取數據庫字段類型的枚舉 JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); Map<String, String> discriminatorMap = new HashMap<String, String>(); //遍歷<discriminator>節點的全部子節點 for (XNode caseChild : context.getChildren()) { //獲取子節點的value屬性 String value = caseChild.getStringAttribute("value"); //獲取子節點的resultMap屬性,默認爲嵌套的ResultMap的Id值 String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings)); //將value屬性以及resultMap屬性添加到集合中 discriminatorMap.put(value, resultMap); } //建立鑑別器(Discriminator)對象 return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap); }
private void sqlElement(List<XNode> list) throws Exception { //若是從全局配置信息中獲取的數據庫Id不爲null if (configuration.getDatabaseId() != null) { sqlElement(list, configuration.getDatabaseId()); } sqlElement(list, null); }
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception { //遍歷全部的<sql>節點(由於<sql>節點可能不止1個) for (XNode context : list) { //獲取<sql>節點的databaseId屬性 String databaseId = context.getStringAttribute("databaseId"); //獲取<sql>節點的id屬性 String id = context.getStringAttribute("id"); //爲id添加命名空間 id = builderAssistant.applyCurrentNamespace(id, false); //檢測<sql>的數據庫id與全局配置信息中的數據庫id是否一致 if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { //若是一致將id與該<sql>節點添加到sql片斷映射中 sqlFragments.put(id, context); } } }
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { //若是全局配置信息中的數據庫id不爲null if (requiredDatabaseId != null) { //若是從<sql>標籤中獲取的數據庫id與全局配置信息中的數據庫id不一樣,返回false if (!requiredDatabaseId.equals(databaseId)) { return false; } } else { //若是全局配置信息中的數據庫id爲null //從<sql>標籤中獲取的數據庫id也爲null,返回false if (databaseId != null) { return false; } //若是當前sql片斷映射中包含id if (this.sqlFragments.containsKey(id)) { //獲取該id對應的<sql>節點 XNode context = this.sqlFragments.get(id); //若是該<sql>節點獲取的數據庫id屬性不爲null,返回false if (context.getStringAttribute("databaseId") != null) { return false; } } } return true; }
private void buildStatementFromContext(List<XNode> list) { //若是從全局配置信息中獲取的數據庫id不爲null if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); }
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { //遍歷全部的<select>、<insert>、<update>、<delete>節點 for (XNode context : list) { //建立XMLStatementBuilder對象 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { //使用該對象來解析<select>、<insert>、<update>、<delete>節點 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
private void bindMapperForNamespace() { //獲取當前的命名空間 String namespace = builderAssistant.getCurrentNamespace(); //若是當前命名空間不爲null if (namespace != null) { Class<?> boundType = null; try { //反射加載命名空間的類實例 boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } //若是該類實例不爲null 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); } } } }
private void parsePendingResultMaps() { //從全局配置信息中獲取解析錯誤的ResultMaps集合 Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps(); synchronized (incompleteResultMaps) { //同步該集合 //獲取該集合的迭代器 Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator(); while (iter.hasNext()) { try { //生成新的ResultMap,加入到全局配置信息中 iter.next().resolve(); //從解析錯誤的集合中移除 iter.remove(); } catch (IncompleteElementException e) { // ResultMap is still missing a resource... } } } }
private void parsePendingCacheRefs() { //從全局配置信息中獲取解析錯誤的CacheRefs集合 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() { //從全局配置信息中獲取解析失敗的SQL語句集合 Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements(); synchronized (incompleteStatements) { //同步該集合 //獲取該集合的迭代器 Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator(); while (iter.hasNext()) { try { //生成新的MappedStatement對象,並添加到全局配置信息中 iter.next().parseStatementNode(); //從解析失敗的集合中移除 iter.remove(); } catch (IncompleteElementException e) { // Statement is still missing a resource... } } } }
在Configuration中
protected final Map<String, String> cacheRefMap = new HashMap<String, String>(); //實際綁定到命名空間的緩存
public void addCacheRef(String namespace, String referencedNamespace) { cacheRefMap.put(namespace, referencedNamespace); }
在CacheRefResolver中
public Cache resolveCacheRef() { return assistant.useCacheRef(cacheRefNamespace); }
在MapperBuilderAssistant中
private boolean unresolvedCacheRef; //未解決的緩存方案
private Cache currentCache; //當前緩存
public Cache useCacheRef(String namespace) { //若是命名空間爲null,拋出異常 if (namespace == null) { throw new BuilderException("cache-ref element requires a namespace attribute."); } try { //設置當前緩存方案未解決 unresolvedCacheRef = true; //從全局配置信息中獲取命名空間的緩存 Cache cache = configuration.getCache(namespace); //若是獲取的緩存爲null,拋出異常 if (cache == null) { throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found."); } //設置當前緩存爲獲取到的緩存 currentCache = cache; //設置當前緩存方案已解決 unresolvedCacheRef = false; return cache; } catch (IllegalArgumentException e) { throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e); } }
public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { //經過<cache>節點配置的屬性來建立Cache實例 Cache cache = new CacheBuilder(currentNamespace) .implementation(valueOrDefault(typeClass, PerpetualCache.class)) .addDecorator(valueOrDefault(evictionClass, LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build(); //將該緩存對象添加到全局配置信息中 configuration.addCache(cache); currentCache = cache; return cache; }
public String applyCurrentNamespace(String base, boolean isReference) { //若是id自己爲null,返回null if (base == null) { return null; } //是否已經被標記包含了命名空間,若是包含則直接返回id if (isReference) { // is it qualified with any namespace yet? if (base.contains(".")) { return base; } } else { // is it qualified with this namespace yet? //若是id以命名空間開頭,直接返回 if (base.startsWith(currentNamespace + ".")) { return base; } //若是id不包含命名空間卻有.號,拋出異常 if (base.contains(".")) { throw new BuilderException("Dots are not allowed in element names, please remove it from " + base); } } //給id加上命名空間開頭 return currentNamespace + "." + base; }
在ResultMapping中
private Configuration configuration; //全局配置信息 private String property; //對應節點的property屬性,表示與column進行映射的屬性 private String column; //對應節點的column屬性,表示的是從數據庫中獲得的列名或是列名的別名 private Class<?> javaType; //對應節點的javaType屬性,表示的是一個Java類型的類實例 private JdbcType jdbcType; //對應節點的jdbcType屬性,表示的是進行映射的列的數據庫字段類型 private TypeHandler<?> typeHandler; //對應節點的typeHandler屬性,表示的是類型處理器 //對應節點的resultMap屬性,該屬性經過id引用了另外一個<resultMap>節點定義,它負責將結果集中的一部分列映射成其餘關聯的結果對象。這樣咱們能夠經過join方式進行 //關聯查詢,而後直接映射成多個對象,並同時設置這些對象之間的組合關係 private String nestedResultMapId; //對應節點的select屬性,該屬性經過id引用了另外一個<select>節點定義,它會把指定的列的值傳入select屬性指定的select語句中做爲參數進行查詢 private String nestedQueryId; private Set<String> notNullColumns; //對應節點的notNullColumn屬性拆分後的結果 private String columnPrefix; //對應節點的columnPrefix屬性 private List<ResultFlag> flags; //處理後的標誌,標誌共2個:id和constructor private List<ResultMapping> composites; //對應節點的column屬性拆分後生成的結果 private String resultSet; //對應節點的resultSet屬性 private String foreignColumn; //對應節點的foreignColumn屬性 private boolean lazy; //是否延遲加載,對應節點的fetchType屬性
它跟mapper文件的關係以下圖所示
圖片來源於網絡
在ResultMap中
private Configuration configuration; //全局配置信息 private String id; //<resultMap>節點的id屬性 private Class<?> type; //<resultMap>節點的type屬性 private List<ResultMapping> resultMappings; //記錄除了<discriminator>節點以外的其餘映射關係(即ResultMapping對象集合) private List<ResultMapping> idResultMappings; //記錄了映射關係中帶有ID標誌的映射關係,例如<id>節點和<constructor>節點的<idArg>子節點 private List<ResultMapping> constructorResultMappings; //記錄了映射關係中帶有Constructor標誌的映射關係,例如<constructor>全部子元素 private List<ResultMapping> propertyResultMappings; //記錄了映射關係中不帶有Constructor標誌的映射關係 private Set<String> mappedColumns; //記錄了全部映射關係中涉及的column屬性的集合 private Set<String> mappedProperties; //記錄了全部映射關係中涉及的properties屬性的集合 private Discriminator discriminator; //鑑別器,對應<discriminator>節點 private boolean hasNestedResultMaps; //是否含有嵌套的結果映射,若是某個映射關係中存在resultMap屬性,且不存在resultSet屬性,則爲true private boolean hasNestedQueries; //是否含有嵌套查詢,若是某個屬性映射存在select屬性,則爲true private Boolean autoMapping; //是否開啓自動映射
XMLStatementBuilder也是BaseBuilder抽象類的子類之一,負責解析映射配置文件中的<select>、<insert>、<update>、<delete>節點,在XMLMapperBuilder.buildStatementFromContext()方法中被實例化。
解析SQL節點的代碼以下
private final String requiredDatabaseId; //全局配置信息中獲取的數據庫id
public void parseStatementNode() { //獲取該節點的id屬性 String id = context.getStringAttribute("id"); //獲取該節點的databaseId屬性 String databaseId = context.getStringAttribute("databaseId"); //判斷全局配置信息中的數據庫id與該節點的數據庫id若是不一樣,結束 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } //獲取該節點的fetchSize屬性 Integer fetchSize = context.getIntAttribute("fetchSize"); //獲取該節點的timeout屬性 Integer timeout = context.getIntAttribute("timeout"); //獲取該節點的parameterMap屬性 String parameterMap = context.getStringAttribute("parameterMap"); //獲取該節點的parameterType屬性 String parameterType = context.getStringAttribute("parameterType"); //在類型別名映射中獲取parameterType屬性的Class實例 Class<?> parameterTypeClass = resolveClass(parameterType); //獲取該節點的resultMap屬性 String resultMap = context.getStringAttribute("resultMap"); //獲取該節點的resultType屬性 String resultType = context.getStringAttribute("resultType"); //獲取該節點的lang屬性 String lang = context.getStringAttribute("lang"); //從全局配置信息中獲取lang屬性Class實例對應的LanguageDriver對象 LanguageDriver langDriver = getLanguageDriver(lang); //從類型別名映射中獲取resultType屬性的類實例 Class<?> resultTypeClass = resolveClass(resultType); //獲取該節點的resultSetType屬性 String resultSetType = context.getStringAttribute("resultSetType"); //從該節點的statementType屬性(默認值爲PREPARED)獲得StatementType的枚舉 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); //從resultSetType屬性獲取ResultSetType的枚舉 ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); //獲取節點的節點名 String nodeName = context.getNode().getNodeName(); //獲取節點名的枚舉 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); //是否爲<select>節點 boolean isSelect = sqlCommandType == SqlCommandType.SELECT; //獲取該節點的flushCache屬性,默認值爲非isSelect boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); //獲取該節點的useCache屬性,默認值爲isSelect boolean useCache = context.getBooleanAttribute("useCache", isSelect); //獲取該節點的resultOrdered屬性,默認值爲false boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); //在解析SQL節點以前,先處理其中的<include>節點 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); //解析<selectKey>節點 processSelectKeyNodes(id, parameterTypeClass, langDriver); //獲取該節點的SQL語句對象(已經移除了<include><selectKey>節點的) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); //獲取該節點的resultSets屬性 String resultSets = context.getStringAttribute("resultSets"); //獲取該節點的keyProperty屬性 String keyProperty = context.getStringAttribute("keyProperty"); //獲取該節點的keyColumn屬性 String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; //獲取<selectKey>節點對應的SelectKeyGenerator的id String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); //若是全局配置信息中有該id if (configuration.hasKeyGenerator(keyStatementId)) { //獲取該id對應的主鍵對象 keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { //不然根據節點的useGeneratedKeys屬性的默認值來判斷有無主鍵 keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } //建立MappedStatement對象,並添加到全局配置信息中保存 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { //若是全局配置信息中的數據庫id不爲null if (requiredDatabaseId != null) { //若是SQL語句節點中的數據庫id與全局配置信息中的數據庫id不一樣,返回false if (!requiredDatabaseId.equals(databaseId)) { return false; } } else { //若是全局配置信息中的數據庫id爲null //且SQL語句節點的數據庫id也爲null,返回false if (databaseId != null) { return false; } //綁定SQL語句的id與命名空間 id = builderAssistant.applyCurrentNamespace(id, false); //若是全局配置信息中有該id的Statement if (this.configuration.hasStatement(id, false)) { //從全局配置信息中獲取該id對應的Statement MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2 //若是該Statement的數據庫id不爲null,返回false if (previous.getDatabaseId() != null) { return false; } } } return true; }
private LanguageDriver getLanguageDriver(String lang) { Class<?> langClass = null; if (lang != null) { //若是SQL語句節點的lang屬性不爲null,從類型別名映射中獲取lang屬性的Class實例 langClass = resolveClass(lang); } //從全局配置信息中返回該類實例對應的LanguageDriver對象 return builderAssistant.getLanguageDriver(langClass); }
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) { //獲取全部的<selectkey>節點 List<XNode> selectKeyNodes = context.evalNodes("selectKey"); //若是全局配置信息的數據庫Id不爲null if (configuration.getDatabaseId() != null) { parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId()); } parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null); removeSelectKeyNodes(selectKeyNodes); }
private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) { //遍歷全部的<selectkey>節點 for (XNode nodeToHandle : list) { //經過<select>節點id+!selectKey組成id String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX; //獲取該節點的databasedId屬性 String databaseId = nodeToHandle.getStringAttribute("databaseId"); //若是databasedId屬性與全局配置信息中的數據庫Id相同 if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) { //解析<selectKey>節點 parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId); } } }
private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) { //獲取節點的resultType屬性 String resultType = nodeToHandle.getStringAttribute("resultType"); //在類型別名映射中獲取resultType屬性對應的類實例 Class<?> resultTypeClass = resolveClass(resultType); //獲取節點的statementType的屬性(默認爲PREPARED)的枚舉 StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString())); //獲取節點的keyProperty屬性 String keyProperty = nodeToHandle.getStringAttribute("keyProperty"); //獲取節點的keyColumn屬性 String keyColumn = nodeToHandle.getStringAttribute("keyColumn"); //判斷節點的order屬性(默認爲AFTER)是否爲BEFORE boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER")); //defaults boolean useCache = false; boolean resultOrdered = false; KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE; Integer fetchSize = null; Integer timeout = null; boolean flushCache = false; String parameterMap = null; String resultMap = null; ResultSetType resultSetTypeEnum = null; //獲取該節點的SQL語句 SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass); //肯定Sql命令類型爲select SqlCommandType sqlCommandType = SqlCommandType.SELECT; //根據以上全部信息產生一個MappedStatement對象,並添加到全局信息配置中(MappedStatement是一個記錄了SQL語句節點全部信息的類) builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null); //將SQL語句節點的id與命名空間綁定 id = builderAssistant.applyCurrentNamespace(id, false); //從全局配置信息中獲取該id對應的MappedStatement對象 MappedStatement keyStatement = configuration.getMappedStatement(id, false); //建立<selectKey>節點對應的KeyGennerator,添加到全局配置信息的keyGenerators集合中保存 configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore)); }
在MapperBuilderAssistant中
public LanguageDriver getLanguageDriver(Class<?> langClass) { if (langClass != null) { //若是該langClass類實例不爲null,將該類實例註冊到全局配置信息中 configuration.getLanguageRegistry().register(langClass); } else { //若是爲null,從全局配置信息的註冊器中獲取默認的類實例 langClass = configuration.getLanguageRegistry().getDefaultDriverClass(); } //返回從全局配置信息中該類實例對應的LanguageDriver對象 return configuration.getLanguageRegistry().getDriver(langClass); }
在XMLIncludeTransformer中
這裏是針對<include>節點的,咱們假設有這樣一段XML配置
<!-- assist是查詢輔助工具類, ${req.require}表示列名,#{req.value}表示值它是防SQL注入的 --> <sql id="assist"> <where> <foreach collection="require" item="req" separator=" "> ${req.require} <if test="req.value != null"> #{req.value} </if> <if test="req.values != null"> <foreach collection="req.values" item="value" separator=","> #{value} </foreach> </if> <if test="req.suffix != null"> ${req.suffix}</if> </foreach> </where> </sql>
<select id="getBetdetailsRowCount" parameterType="com.cloud.model.common.Assist" resultType="java.lang.Long"> select count(*) from betdetails <if test="require!=null"> <include refid="assist" /> </if> </select>
public void applyIncludes(Node source) { Properties variablesContext = new Properties(); Properties configurationVariables = configuration.getVariables(); if (configurationVariables != null) { variablesContext.putAll(configurationVariables); } applyIncludes(source, variablesContext, false); }
private void applyIncludes(Node source, final Properties variablesContext, boolean included) { //若是節點名稱爲include if (source.getNodeName().equals("include")) { //查找refid屬性指向的<sql>節點,返回的是其深克隆的Node對象 Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext); //解析<include>節點下的<property>節點,並獲得一個新的Properties對象 Properties toIncludeContext = getVariablesContext(source, variablesContext); //遞歸處理<include>節點,在<sql>節點中可能會使用<include>引用了其餘SQL片斷 applyIncludes(toInclude, toIncludeContext, true); //若是<sql>節點跟<include>節點不是同一個XML裏的節點(頂層節點不一樣) if (toInclude.getOwnerDocument() != source.getOwnerDocument()) { //將<sql>節點導入到<include>所在的頂層節點(<mapper>節點),變成一個新節點 toInclude = source.getOwnerDocument().importNode(toInclude, true); } //將<include>節點替換成<sql>節點 source.getParentNode().replaceChild(toInclude, source); //若是<sql>節點有子節點 while (toInclude.hasChildNodes()) { //將子節點添加到<sql>節點前面 toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude); } //移除<sql>節點自己 toInclude.getParentNode().removeChild(toInclude); //若是該節點不是<include>節點,且是一個元素類型的節點(通常爲SQL語句的節點) } else if (source.getNodeType() == Node.ELEMENT_NODE) { //若是從全局配置信息中獲取的變量屬性不爲空,且<include>節點已經被替換成SQL語句節點 if (included && !variablesContext.isEmpty()) { //獲取該節點的全部屬性 NamedNodeMap attributes = source.getAttributes(); //遍歷該節點的全部屬性 for (int i = 0; i < attributes.getLength(); i++) { Node attr = attributes.item(i); //將該屬性值替換變量 attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext)); } } NodeList children = source.getChildNodes(); //遍歷該SQL語句的子節點 for (int i = 0; i < children.getLength(); i++) { //遞歸處理這些子節點 applyIncludes(children.item(i), variablesContext, included); } //若是<included>已經被替換成SQL語句節點且該節點類型爲文本 } else if (included && source.getNodeType() == Node.TEXT_NODE && !variablesContext.isEmpty()) { //使用以前解析獲得的Properties對象替換對應的佔位符(替換變量) source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext)); } }
private Node findSqlFragment(String refid, Properties variables) { //去除refid字符串中先後限定符中的\ refid = PropertyParser.parse(refid, variables); //將refid與命名空間綁定 refid = builderAssistant.applyCurrentNamespace(refid, true); try { //從refid獲取在全局配置信息中對應的<sql>節點 XNode nodeToInclude = configuration.getSqlFragments().get(refid); //返回獲取的<sql>節點的克隆 return nodeToInclude.getNode().cloneNode(true); } catch (IllegalArgumentException e) { throw new IncompleteElementException("Could not find SQL statement to include with refid '" + refid + "'", e); } }
private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) { Map<String, String> declaredProperties = null; NodeList children = node.getChildNodes(); //遍歷<include>節點的全部子節點 for (int i = 0; i < children.getLength(); i++) { Node n = children.item(i); //若是該子節點類型是一個元素 if (n.getNodeType() == Node.ELEMENT_NODE) { //獲取該子節點的name屬性 String name = getStringAttribute(n, "name"); //獲取該子節點的value屬性(替換了變量) String value = PropertyParser.parse(getStringAttribute(n, "value"), inheritedVariablesContext); if (declaredProperties == null) { declaredProperties = new HashMap<String, String>(); } //若是declaredProperties的key已經存在,拋出異常,即子節點的名稱不能重複 if (declaredProperties.put(name, value) != null) { throw new BuilderException("Variable " + name + " defined twice in the same include definition"); } } } //若是沒有子節點 if (declaredProperties == null) { //返回從全局配置信息中獲取的變量屬性 return inheritedVariablesContext; } else { //若是有子節點 //將全局配置信息中獲取的變量屬性與<include>子節點獲取的name,value屬性映射放入一個屬性對象中返回 Properties newProperties = new Properties(); newProperties.putAll(inheritedVariablesContext); newProperties.putAll(declaredProperties); return newProperties; } }
在PropertyParser中
public static String parse(String string, Properties variables) { VariableTokenHandler handler = new VariableTokenHandler(variables); GenericTokenParser parser = new GenericTokenParser("${", "}", handler); return parser.parse(string); }
在GenericTokenParser中
public String parse(String text) { if (text == null || text.isEmpty()) { return ""; } //獲取前置變量限定符${的位置 int start = text.indexOf(openToken, 0); if (start == -1) { return text; } //將text字符串轉成字節數組 char[] src = text.toCharArray(); //開始位 int offset = 0; final StringBuilder builder = new StringBuilder(); StringBuilder expression = null; while (start > -1) { //若是前置變量限定符${存在 //若是前置位置限定符${的前一位爲\ if (start > 0 && src[start - 1] == '\\') { // this open token is escaped. remove the backslash and continue. //鏈接\跟前置位置限定符${ builder.append(src, offset, start - offset - 1).append(openToken); //將開始位變爲前置位置限定符${後一位 offset = start + openToken.length(); } else { //若是前置位置限定符${的前一位不爲\ // found open token. let's search close token. if (expression == null) { expression = new StringBuilder(); } else { expression.setLength(0); } //獲取前置位置限定符${以前的全部字符 builder.append(src, offset, start - offset); //開始位變爲前置位置限定符${後一位 offset = start + openToken.length(); //從前置位置限定符${後一位開始獲取後置位置限定符}的位置 int end = text.indexOf(closeToken, offset); while (end > -1) { //後置位置限定符}存在 //若是後置位置限定符}的前一位爲\ if (end > offset && src[end - 1] == '\\') { // this close token is escaped. remove the backslash and continue. //鏈接中間段的字符和後置位置限定符} expression.append(src, offset, end - offset - 1).append(closeToken); //將開始位變爲後置位置限定符}的後一位 offset = end + closeToken.length(); //重新的開始位開始獲取後置位置限定符}的位置 end = text.indexOf(closeToken, offset); } else { //若是後置位置限定符}的前一位不爲\ //獲取中間字符 expression.append(src, offset, end - offset); //將開始位變爲後置位置限定符}的後一位 offset = end + closeToken.length(); break; } } if (end == -1) { //若是後置位置限定符不存在 // close token was not found. builder.append(src, start, src.length - start); offset = src.length; } else { builder.append(handler.handleToken(expression.toString())); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } if (offset < src.length) { builder.append(src, offset, src.length - offset); } return builder.toString(); }
在XMLLanguageDriver中
@Override public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); return builder.parseScriptNode(); }
在XMLScriptBuilder中
private final XNode context; //待解析的節點
//不一樣節點類型的節點處理器映射,對應的節點名稱在初始化中被加載,節點名稱有trim,where,when,set,foreach,if,choose,otherwise,bind private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<String, NodeHandler>();
public SqlSource parseScriptNode() { //獲取待解析的節點的混合子節點集合 MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource = null; //根據是不是動態SQL,建立相應的SqlSource對象,SqlSource會在後面重點講解 if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }
protected MixedSqlNode parseDynamicTags(XNode node) { //用於記錄生成的sqlNode集合 List<SqlNode> contents = new ArrayList<SqlNode>(); //獲取節點<selectkey>的全部子節點 NodeList children = node.getNode().getChildNodes(); //遍歷全部子節點 for (int i = 0; i < children.getLength(); i++) { //獲取每個子節點對象 XNode child = node.newXNode(children.item(i)); //若是該子節點的節點類型爲文本節點 if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { //建立一個""字符串 String data = child.getStringBody(""); //建立一個空字符串的文本節點對象 TextSqlNode textSqlNode = new TextSqlNode(data); //若是該文本節點是動態的(解析SQL語句,若是含有未解析的${}佔位符,則爲動態SQL) if (textSqlNode.isDynamic()) { //記錄該動態節點 contents.add(textSqlNode); isDynamic = true; } else { //若是不是動態節點,記錄爲靜態文本節點 contents.add(new StaticTextSqlNode(data)); } } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 //若是子節點是一個標籤,那麼必定是動態SQL,而且根據不一樣的動態標籤生成不一樣的節點處理器 String nodeName = child.getNode().getNodeName(); NodeHandler handler = nodeHandlerMap.get(nodeName); if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } //處理動態SQL,並將解析獲得的SQL節點對象放入記錄集合 handler.handleNode(child, contents); isDynamic = true; } } //返回記錄集合的封裝 return new MixedSqlNode(contents); }
@Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { //獲取處理節點的name屬性 final String name = nodeToHandle.getStringAttribute("name"); //獲取處理節點的value屬性 final String expression = nodeToHandle.getStringAttribute("value"); //生成<bind>節點類型的Sql節點對象 final VarDeclSqlNode node = new VarDeclSqlNode(name, expression); //將該節點對象添加到集合中 targetContents.add(node); }
SqlSource是一個接口,表示映射文件或註解中定義的SQL語句,但它表示的SQL語句可能含有動態SQL語句相關的節點或是佔位符等須要解析的元素。
public interface SqlSource { //根據映射文件或註解描述的SQL語句,以及傳入的參數,返回可執行的SQL BoundSql getBoundSql(Object parameterObject); }
SqlSourceBuilder也是BaseBuilder的子類之一,在通過SqlNode.apply()方法的解析以後,SQL語句會被傳遞到SqlSourceBuilder中進行進一步的解析。它的核心parse()方法以下
//參數originalSql是通過SqlNode.apply()方法處理以後的SQL語句 //參數parameterType是用戶傳入的實參類型 //參數additionalParameters是通過SqlNode.apply()方法處理後的DynamicContext.bindings集合 public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { //建立ParameterMappingTokenHandler對象,它是解析#{}佔位符中的參數屬性以及替換佔位符的核心 ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); //使用GenericTokenParser與ParameterMappingTokenHandler配合解析#{}佔位符 GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); String sql = parser.parse(originalSql); //建立StaticSqlSource,其中封裝了佔位符被替換成"?"的SQL語句以及參數對應的ParameterMapping集合 return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
ParameterMappingTokenHandler是其內部類,也繼承了SqlSourceBuilder,實現了TokenHandler接口,各字段含義以下
//用於記錄解析獲得的ParameterMapping集合 private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>(); //參數類型 private Class<?> parameterType; //DynamicContext.bindings集合對應的MeataObject對象 private MetaObject metaParameters;
它的核心方法爲handleToken()
@Override public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; }
private ParameterMapping buildParameterMapping(String content) { //解析content字符串,並將解析結果存入Map中。例如#{__frc_item_0, javaType=int, jdbcType=NUMERIC, typeHandler=MyTypeHandler}被解析成 //{"property" -> "__frch_item_0", "javaType" -> "int", "jdbcType" -> "NUMERIC", "typeHandler" -> "MyTypeHandler"} Map<String, String> propertiesMap = parseParameterMapping(content); //獲取參數名稱好比__frch_item_0 String property = propertiesMap.get("property"); //定義property的類實例 Class<?> propertyType; //肯定參數(__frch_item_0)的javaType屬性 //若是傳入參數的反射屬性中有property的getter方法 if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params //從metaObject反射工具中獲取property屬性對應的類實例 propertyType = metaParameters.getGetterType(property); //若是類型處理註冊器中有該參數類型的註冊 } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) { //將參數類型類實例直接賦給propertyType propertyType = parameterType; //若是從map中獲取jdbcType的value爲JdbcType枚舉中的CURSOR } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) { //propertyType爲ResultSet的類實例 propertyType = java.sql.ResultSet.class; //若是參數類型是一個Map類型的實現類或者參數名爲null } else if (property == null || Map.class.isAssignableFrom(parameterType)) { //propertyType爲Object的類實例 propertyType = Object.class; } else { //以上都不是,獲取參數類型的反射類MetaClass MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory()); //若是property爲該參數類型的一個getter屬性 if (metaClass.hasGetter(property)) { //獲取該propety的類實例 propertyType = metaClass.getGetterType(property); } else { //若是不是一個getter屬性,則把propertyType僅定爲一個Object的類實例 propertyType = Object.class; } } //建立ParameterMapping的構建器,並設置ParameterMapping的相關配置 ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType); Class<?> javaType = propertyType; String typeHandlerAlias = null; //遍歷propertiesMap的全部鍵值對映射 for (Map.Entry<String, String> entry : propertiesMap.entrySet()) { //獲取每個映射的key String name = entry.getKey(); //獲取每個映射的value String value = entry.getValue(); //若是key爲javaType if ("javaType".equals(name)) { //經過類型別名映射解析value的別名 javaType = resolveClass(value); //將解析後的別名放入ParameterMapping的javaType屬性中 builder.javaType(javaType); //若是key爲jdbcType } else if ("jdbcType".equals(name)) { //經過jdbc別名映射解析value的枚舉,放入ParameterMapping的jdbcType屬性中 builder.jdbcType(resolveJdbcType(value)); //若是key爲mode } else if ("mode".equals(name)) { //經過mode別名映射解析value的枚舉(此處爲進出參數),放入ParameterMapping的mode屬性中 builder.mode(resolveParameterMode(value)); //若是key爲numericScale } else if ("numericScale".equals(name)) { //將vlue轉化爲整形,放入ParameterMapping的numericScale屬性中 builder.numericScale(Integer.valueOf(value)); //若是key爲resultMap } else if ("resultMap".equals(name)) { //將value放入ParameterMapping的resultMapId屬性中 builder.resultMapId(value); //若是key爲typeHandler } else if ("typeHandler".equals(name)) { //獲取value賦值typeHandlerAlias typeHandlerAlias = value; //若是key爲jdbcTypeName } else if ("jdbcTypeName".equals(name)) { //將value放入ParameterMapping的jdbcTypeName屬性 builder.jdbcTypeName(value); } else if ("property".equals(name)) { // Do Nothing } else if ("expression".equals(name)) { throw new BuilderException("Expression based parameters are not supported yet"); } else { throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + parameterProperties); } } //若是typeHandlerAlias不爲null if (typeHandlerAlias != null) { //獲取TypeHandler對象並放入ParameterMapping的typeHandler屬性中 builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias)); } //建立ParameterMapping對象,若是沒有指定類型處理器,則會在build()方法中,根據java類型,數據庫字段類型從類型處理器註冊器中獲取對應的類型處理器對象 return builder.build(); }
private Map<String, String> parseParameterMapping(String content) { try { //解析content字符串,並存入ParameterExpression實例中 return new ParameterExpression(content); } catch (BuilderException ex) { throw ex; } catch (Exception ex) { throw new BuilderException("Parsing error was found in mapping #{" + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex); } }
ParameterMapping中記錄了#{}佔位符中的參數屬性,其各個字段的含義以下
private Configuration configuration; //全局配置信息 private String property; //傳入進來的參數name private ParameterMode mode; //輸入參數仍是輸出參數,枚舉類型 private Class<?> javaType = Object.class; //參數的Java類型 private JdbcType jdbcType; //參數的JDBC類型 private Integer numericScale; //浮點參數的精度 private TypeHandler<?> typeHandler; //參數對應的類型處理器對象 private String resultMapId; //參數對應的ResultMap的Id private String jdbcTypeName; //參數的jdbcTypeName屬性 private String expression;
public ParameterMapping build() { //解析parameterMapping參數類型處理器 resolveTypeHandler(); //對參數類型處理器檢查有效性 validate(); return parameterMapping; }
private void resolveTypeHandler() { //若是類型處理器對象爲null且Java類實例不爲null if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) { //獲取全局配置信息 Configuration configuration = parameterMapping.configuration; //獲取全局配置信息的類型處理器註冊器 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); //獲取java類型對應的數據庫字段類型的類型處理器對象 parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType); } }
private void validate() { //若是參數java類型爲ResultSet if (ResultSet.class.equals(parameterMapping.javaType)) { //若是參數對應的resultMapId爲null,拋出異常 if (parameterMapping.resultMapId == null) { throw new IllegalStateException("Missing resultmap in property '" + parameterMapping.property + "'. " + "Parameters of type java.sql.ResultSet require a resultmap."); } //若是參數java類型不爲ResultSet } else { //若是參數的類型處理器爲null,拋出異常 if (parameterMapping.typeHandler == null) { throw new IllegalStateException("Type handler was null on parameter mapping for property '" + parameterMapping.property + "'. It was either not specified and/or could not be found for the javaType (" + parameterMapping.javaType.getName() + ") : jdbcType (" + parameterMapping.jdbcType + ") combination."); } } }
在ParameterExpression中,ParameterExpression繼承於HashMap
public ParameterExpression(String expression) { //解析expression字符串 parse(expression); }
private void parse(String expression) { //獲取expression中大於空格字符的索引 int p = skipWS(expression, 0); //若是該索引的字符爲'(' if (expression.charAt(p) == '(') { //處理該字符串中的內容 expression(expression, p + 1); } else { //若是該索引的字符不爲'(' //以",:"爲爲邊界,將p到",:"索引之間的字符串爲value,"property"爲key存入當前ParameterExpression實例 //再處理後續jdbcTypeOpt()方法 property(expression, p); } }
private int skipWS(String expression, int p) { for (int i = p; i < expression.length(); i++) { //若是expression的每個字符爲大於空格以上的字符,返回該字符的索引 if (expression.charAt(i) > 0x20) { return i; } } //若是沒有一個大於空格以上的字符,返回該字符串的長度 return expression.length(); }
private void expression(String expression, int left) { int match = 1; int right = left + 1; //獲取字符串中(和)中的索引位置 while (match > 0) { //若是expression中'('的後兩位字符爲')',match遞減 if (expression.charAt(right) == ')') { match--; //若是expression中'('的後兩位字符爲'(',match遞增 } else if (expression.charAt(right) == '(') { match++; } //right遞增 right++; } //將expression和()中間的字符串以key,value存入當前ParameterExpression實例 put("expression", expression.substring(left, right - 1)); //將':'後面的字符串,以','爲分隔符爲value,將"jdbcType"爲key存入當前ParameterExpression實例 //再將'='兩邊的字符串,以','爲分隔符,以key,value形式存入當前ParameterExpression實例 jdbcTypeOpt(expression, right); }
private void jdbcTypeOpt(String expression, int p) { //獲取p索引以後的大於空格的字符索引 p = skipWS(expression, p); //若是expression之中存在該字符 if (p < expression.length()) { //且該索引的字符爲':' if (expression.charAt(p) == ':') { //將':'後面的字符串,以','爲分隔符爲value,將"jdbcType"爲key存入當前ParameterExpression實例 //再將'='兩邊的字符串,以','爲分隔符,以key,value形式存入當前ParameterExpression實例 jdbcType(expression, p + 1); //且該索引的字符爲',' } else if (expression.charAt(p) == ',') { //將'='兩邊的字符串,以','爲分隔符,以key,value形式存入當前ParameterExpression實例 option(expression, p + 1); } else { throw new BuilderException("Parsing error in {" + expression + "} in position " + p); } } }
private void jdbcType(String expression, int p) { //獲取p索引以後的大於空格的字符索引 int left = skipWS(expression, p); //獲取','在expression的位置 int right = skipUntil(expression, left, ","); if (right > left) { //將jdbcType以及去除了left,rignt兩邊空格的字符串以key,value存入當前ParameterExpression實例 put("jdbcType", trimmedStr(expression, left, right)); } else { throw new BuilderException("Parsing error in {" + expression + "} in position " + p); } //將'='兩邊的字符串,以','爲分隔符,以key,value形式存入當前ParameterExpression實例 option(expression, right + 1); }
private int skipUntil(String expression, int p, final String endChars) { //從p位置開始,遍歷後續的字符串 for (int i = p; i < expression.length(); i++) { //遍歷每個字符 char c = expression.charAt(i); //若是該字符在endChars內,返回該字符的索引位 if (endChars.indexOf(c) > -1) { return i; } } //不然返回expression的長度 return expression.length(); }
private String trimmedStr(String str, int start, int end) { while (str.charAt(start) <= 0x20) { start++; } while (str.charAt(end - 1) <= 0x20) { end--; } //去掉str兩邊的空格 return start >= end ? "" : str.substring(start, end); }
private void option(String expression, int p) { //獲取p索引以後的大於空格的字符索引 int left = skipWS(expression, p); //若是expression之中存在該字符 if (left < expression.length()) { //獲取'='在expression的位置(left以後) int right = skipUntil(expression, left, "="); //獲取去除了left,rignt兩邊空格的字符串 String name = trimmedStr(expression, left, right); left = right + 1; //獲取','在expression的位置(left以後) right = skipUntil(expression, left, ","); //獲取去除了left,rignt兩邊空格的字符串 String value = trimmedStr(expression, left, right); //將name,value存入當前ParameterExpression實例 put(name, value); //遞歸後面全部的字符串 option(expression, right + 1); } }
private void property(String expression, int left) { //若是該字符在expression中存在 if (left < expression.length()) { //獲取',:'在expression的位置(left以後) int right = skipUntil(expression, left, ",:"); //以"property"爲key;left,right中間去除兩邊空格的字符串爲value,存入當前ParameterExpression實例 put("property", trimmedStr(expression, left, right)); //將':'後面的字符串,以','爲分隔符爲value,將"jdbcType"爲key存入當前ParameterExpression實例 //再將'='兩邊的字符串,以','爲分隔符,以key,value形式存入當前ParameterExpression實例 jdbcTypeOpt(expression, right); } }
在TypeHandlerRegistry中
//記錄java類型向jdbcType轉換,須要使用到TypeHandler private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class
public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) { return getTypeHandler((Type) type, jdbcType); }
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) { if (ParamMap.class.equals(type)) { return null; } //獲取type對應的數據庫字段類型與類型處理器的映射 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type); TypeHandler<?> handler = null; //若是該映射不爲null if (jdbcHandlerMap != null) { //獲取該映射中數據庫字段類型對應的類型處理器對象 handler = jdbcHandlerMap.get(jdbcType); if (handler == null) { //若是該處理器對象爲null,直接獲取該映射中null對應的處理器對象 handler = jdbcHandlerMap.get(null); } if (handler == null) { //若是處理器對象依然爲null,從該映射中找到第一個不爲null的處理器對象 handler = pickSoleHandler(jdbcHandlerMap); } } // type drives generics here return (TypeHandler<T>) handler; }
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) { //從TYPE_HANDLER_MAP中獲取type接口對應的數據庫類型與類型處理器的映射 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type); if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) { return null; } //若是該映射爲null且type爲Class類型(Class類爲Type接口的實現類) if (jdbcHandlerMap == null && type instanceof Class) { //將type強制轉換成Class實例 Class<?> clazz = (Class<?>) type; //若是該類爲枚舉類型 if (clazz.isEnum()) { //以枚舉和以該枚舉來構造的類型處理器對象的映射 jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz); //若是該映射爲null if (jdbcHandlerMap == null) { //將clazz類實例以及以clazz(枚舉類型)爲構造參數構造的defaultEnumTypeHandler對象爲參數,註冊進TYPE_HANDLER_MAP中 register(clazz, getInstance(clazz, defaultEnumTypeHandler)); //返回在TYPE_HANDLER_MAP中由clazz獲取的jdbcType和TypeHandler的映射 return TYPE_HANDLER_MAP.get(clazz); } //若是該類不爲枚舉類型 } else { //追溯clazz的全部父類可能對應的數據庫字段類型與類型處理器的映射 jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz); } } //將查到的映射爲value,type爲key放入TYPE_HANDLER_MAP中 TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap); //返回查找到的映射 return jdbcHandlerMap; }
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) { //遍歷clazz類實例的全部接口 for (Class<?> iface : clazz.getInterfaces()) { //獲取每個接口對應的數據庫類型與類型處理器的映射 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface); //若是該映射爲null if (jdbcHandlerMap == null) { //遞歸遍歷該接口的全部父接口 jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz); } //若是該映射不爲null if (jdbcHandlerMap != null) { // Found a type handler regsiterd to a super interface HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>(); //遍歷該映射 for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) { //建立一個以枚舉類型爲構造參數的類型處理器實例對象的value,映射的key爲key,放入新的映射中 newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass())); } return newMap; } } return null; }
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) { //獲取clazz的父類 Class<?> superclass = clazz.getSuperclass(); //若是父類爲null,或者父類爲Object,返回null if (superclass == null || Object.class.equals(superclass)) { return null; } //從TYPE_HANDLER_MAP中查找父類對應的數據庫字段類型與類型處理器的映射 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass); if (jdbcHandlerMap != null) { //若是該映射不爲null,返回該映射 return jdbcHandlerMap; } else { //若是該映射爲null,遞歸查找該父類的父類對應的數據庫字段類型與類型處理器的映射 return getJdbcHandlerMapForSuperclass(superclass); } }
private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) { TypeHandler<?> soleHandler = null; //遍歷jdbcHandlerMap的全部value for (TypeHandler<?> handler : jdbcHandlerMap.values()) { if (soleHandler == null) { //若是soleHandler爲null,將每個value賦給soleHandler soleHandler = handler; //若是soleHandler不爲null且value與soleHandler不是同一個Class,返回null } else if (!handler.getClass().equals(soleHandler.getClass())) { // More than one type handlers registered. return null; } } //返回找到的第一個不爲null的value return soleHandler; }
通過SqlSourceBuilder解析後,SqlNode解析後的SQL語句的變化爲(示例)
select * from XXX where id in ( ? , ? ),其中(?,?)爲一個parameterMappings集合,第一個?表明一個ParameterMapping,其值相似爲{property='__frch_item_0',mode=IN,javaType=class java.lang.Integer},第二個?表明一個ParameterMapping,其值相似爲{property='__frch_item_1',mode=IN,javaType=class java.lang.Integer}
以後,SqlSourceBuilder會將上述SQL語句以及parameterMappings集合封裝成StaticSqlSource對象。StaticSqlSource是SqlSource接口的一個實現類,它的接口方法以下
@Override public BoundSql getBoundSql(Object parameterObject) { return new BoundSql(configuration, sql, parameterMappings, parameterObject); }
BoundSql中核心字段含義以下
private final String sql; //該字段中記錄了SQL語句,該SQL語句中可能含有"?"佔位符 private final List<ParameterMapping> parameterMappings; //SQL中的參數屬性集合,ParameterMapping的集合 private final Object parameterObject; //客戶端執行SQL時傳入的實際參數 //空的HashMap集合,以後會複製DynamicContext.bindings集合中的內容 private final Map<String, Object> additionalParameters; //additionalParameters集合對應的MetaObject對象 private final MetaObject metaParameters;