1. SqlMapClientFactoryBean:

Spring中通过SqlMapClientTemplate提供对Ibatis的支持,与Spring对Hibernate的支持类似,Spring中SqlMapClientFactoryBean就是管理Ibatis的IoC容器,我们首先分析SqlMapClientFactoryBean的源码:

[java]  view plain copy
  1. //Spring管理Ibatis的IoC容器  
  2. public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean {  
  3.     //当前线程绑定Ibatis blob/clob等大字段数据处理器资源  
  4.     private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>();  
  5.     public static LobHandler getConfigTimeLobHandler() {  
  6.         return configTimeLobHandlerHolder.get();  
  7.     }  
  8.     //Ibatis配置文件路径  
  9.     private Resource[] configLocations;  
  10.     //Ibatis映射文件路径  
  11.     private Resource[] mappingLocations;  
  12.     //Ibatis sqlMapClient属性  
  13.     private Properties sqlMapClientProperties;  
  14.     //数据源  
  15.     private DataSource dataSource;  
  16.     //使用Spring事务包装数据源  
  17.     private boolean useTransactionAwareDataSource = true;  
  18.     //事务配置类  
  19.     private Class transactionConfigClass = ExternalTransactionConfig.class;  
  20.     //事务配置属性  
  21.     private Properties transactionConfigProperties;  
  22.     //blob/clob等lob类型处理器  
  23.     private LobHandler lobHandler;  
  24.     //Ibatis sqlMapClient  
  25.     private SqlMapClient sqlMapClient;  
  26.     public SqlMapClientFactoryBean() {  
  27.         this.transactionConfigProperties = new Properties();  
  28.     //不允许事务自动提交  
  29.     this.transactionConfigProperties.setProperty("SetAutoCommitAllowed""false");  
  30.     }  
  31.     //指定Ibatis sqlMapClient配置文件路径  
  32.     public void setConfigLocation(Resource configLocation) {  
  33.         this.configLocations = (configLocation != null ? new Resource[] {configLocation} : null);  
  34.     }  
  35.     //指定多个sqlMapClient配置文件路径,这些配置文件在运行时合并  
  36.     public void setConfigLocations(Resource[] configLocations) {  
  37.         this.configLocations = configLocations;  
  38.     }  
  39.     //指定Ibatis映射文件路径,这些映射文件在运行时被合并到SqlMapClient的配置中  
  40.     public void setMappingLocations(Resource[] mappingLocations) {  
  41.         this.mappingLocations = mappingLocations;  
  42.     }  
  43.     //指定Ibatis SqlMapClient可选的属性,即在SqlMapClient配置文件中通过属性  
  44. //文件设置的属性  
  45.     public void setSqlMapClientProperties(Properties sqlMapClientProperties) {  
  46.         this.sqlMapClientProperties = sqlMapClientProperties;  
  47.     }  
  48.     //设置Ibatis使用的数据源  
  49.     public void setDataSource(DataSource dataSource) {  
  50.         this.dataSource = dataSource;  
  51.     }  
  52.     //设置数据源是否使用事务包装  
  53.     public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) {  
  54.         this.useTransactionAwareDataSource = useTransactionAwareDataSource;  
  55.     }  
  56.     //设置Ibatis使用的事务配置类,默认是//com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig  
  57.     public void setTransactionConfigClass(Class transactionConfigClass) {  
  58.         if (transactionConfigClass == null || !TransactionConfig.class.isAssignableFrom(transactionConfigClass)) {  
  59.             throw new IllegalArgumentException("Invalid transactionConfigClass: does not implement " +  
  60.                     "com.ibatis.sqlmap.engine.transaction.TransactionConfig");  
  61.         }  
  62.         this.transactionConfigClass = transactionConfigClass;  
  63.     }  
  64.     //设置Ibatis事务配置类属性  
  65.     public void setTransactionConfigProperties(Properties transactionConfigProperties) {  
  66.         this.transactionConfigProperties = transactionConfigProperties;  
  67.     }  
  68.     //设置Ibatis使用的处理clob/blob等大字段的处理器  
  69.     public void setLobHandler(LobHandler lobHandler) {  
  70.         this.lobHandler = lobHandler;  
  71.     }  
  72.     //IoC容器初始化完成之后的回调方法,是InitializingBean接口的实现方法  
  73.     public void afterPropertiesSet() throws Exception {  
  74.         //配置lob处理器  
  75.         if (this.lobHandler != null) {  
  76.             configTimeLobHandlerHolder.set(this.lobHandler);  
  77.         }  
  78.         //创建Ibatis的SqlMapClient  
  79.         try {  
  80.             this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);  
  81.             //为创建的SqlMapClient设置数据源  
  82.             if (this.dataSource != null) {  
  83.                 //创建事务配置实例  
  84.                 TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();  
  85.                 //获取数据源  
  86.                 DataSource dataSourceToUse = this.dataSource;  
  87.                 //如果Ibatis配置指定使用事务包装的数据源,并且当前获取到的数据源  
  88.                 //不是事务包装数据源代理类型  
  89.                 if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {  
  90.                     //为指定数据源创建事务包装代理  
  91.                     dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);  
  92.                 }  
  93.                 //事务配置对象设置数据源  
  94.                 transactionConfig.setDataSource(dataSourceToUse);  
  95.             //初始化事务配置对象 transactionConfig.initialize(this.transactionConfigProperties);  
  96.                 applyTransactionConfig(this.sqlMapClient, transactionConfig);  
  97.             }  
  98.         }  
  99.         //创建SqlMapClient成功后,清除当前线程绑定的Lob处理器资源  
  100.         finally {  
  101.             if (this.lobHandler != null) {  
  102.                 configTimeLobHandlerHolder.remove();  
  103.             }  
  104.         }  
  105.     }  
  106.     //具体创建SqlMapClient的方法,根据给定的Ibatis配置文件、Ibatis映射文件  
  107.     //和Ibatis配置中的属性文件创建SqlMapClient  
  108.     protected SqlMapClient buildSqlMapClient(  
  109.             Resource[] configLocations, Resource[] mappingLocations, Properties properties)  
  110.             throws IOException {  
  111.         //如果给定Ibatis配置文件路径为空  
  112.         if (ObjectUtils.isEmpty(configLocations)) {  
  113.             throw new IllegalArgumentException("At least 1 'configLocation' entry is required");  
  114.         }  
  115.         SqlMapClient client = null;  
  116.         //创建Ibatis配置文件解析器  
  117.         SqlMapConfigParser configParser = new SqlMapConfigParser();  
  118.         //遍历所有的Ibatis配置文件  
  119.         for (Resource configLocation : configLocations) {  
  120.             //获取Ibatis配置文件输入流  
  121.             InputStream is = configLocation.getInputStream();  
  122.             try {  
  123.                 //创建Ibatis SqlMapClient  
  124.                 client = configParser.parse(is, properties);  
  125.             }  
  126.             catch (RuntimeException ex) {  
  127.                 throw new NestedIOException("Failed to parse config resource: " + configLocation, ex.getCause());  
  128.             }  
  129.         }  
  130.         //如果Ibatis映射文件不为null  
  131.         if (mappingLocations != null) {  
  132.             //根据Ibatis配置文件解析器创建Ibatis映射文件解析器  
  133.             SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser);  
  134.             //遍历所给定的Ibatis映射文件  
  135.             for (Resource mappingLocation : mappingLocations) {  
  136.                 try {  
  137.                     //解析Ibatis映射文件  
  138.                     mapParser.parse(mappingLocation.getInputStream());  
  139.                 }  
  140.                 catch (NodeletException ex) {  
  141.                     throw new NestedIOException("Failed to parse mapping resource: " + mappingLocation, ex);  
  142.                 }  
  143.             }  
  144.         }  
  145.         //返回创建的SqlMapClient对象  
  146.         return client;  
  147.     }  
  148.     //将Ibatis配置中指定的事务配置应用到SqlMapClient上  
  149.     protected void applyTransactionConfig(SqlMapClient sqlMapClient, TransactionConfig transactionConfig) {  
  150. //如果SqlMapClient不是ExtendedSqlMapClient类型,则无法将Ibatis配置//中指定的事务配置应用到SqlMapClient对象  
  151.         if (!(sqlMapClient instanceof ExtendedSqlMapClient)) {  
  152.             throw new IllegalArgumentException(  
  153.                     "Cannot set TransactionConfig with DataSource for SqlMapClient if not of type " +  
  154.                     "ExtendedSqlMapClient: " + sqlMapClient);  
  155.         }  
  156.         ExtendedSqlMapClient extendedClient = (ExtendedSqlMapClient) sqlMapClient;  
  157.     //设置最大并发Ibatis事务数量  transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions());  
  158.         //为SqlMapClient设置事务处理器  
  159.         extendedClient.getDelegate().setTxManager(new TransactionManager(transactionConfig));  
  160.     }  
  161. //Spring IoC容器中对应用提供的一个获取被管理对象的方法,应该通过此方法获  
  162. //取被Spring IoC容器管理的Ibatis SqlMapClient对象  
  163.     public SqlMapClient getObject() {  
  164.         return this.sqlMapClient;  
  165.     }  
  166.     //获取SqlMapClient的类型  
  167.     public Class<? extends SqlMapClient> getObjectType() {  
  168.         return (this.sqlMapClient != null ? this.sqlMapClient.getClass() : SqlMapClient.class);  
  169.     }  
  170.     //默认Spring IoC容器中管理的对象是单态模式的  
  171.     public boolean isSingleton() {  
  172.         return true;  
  173.     }  
  174. //Ibatis映射解析器工厂,内部类  
  175.     private static class SqlMapParserFactory {  
  176.         //创建Ibatis映射解析器  
  177.         public static SqlMapParser createSqlMapParser(SqlMapConfigParser configParser) {  
  178.             XmlParserState state = null;  
  179.             try {  
  180.                 //使用JDK反射机制获取SqlMapConfigParser类中的state字段  
  181.                 Field stateField = SqlMapConfigParser.class.getDeclaredField("state");  
  182. //使用JDK反射机制使state字段可以被访问,主要解决private、//protect和默认访问权限没有提供get方法的情况  
  183.                 stateField.setAccessible(true);  
  184.                 //使用Ibatis配置解析器获取指定字段的值  
  185.                 state = (XmlParserState) stateField.get(configParser);  
  186.             }  
  187.             catch (Exception ex) {  
  188.                 throw new IllegalStateException("iBATIS 2.3.2 'state' field not found in SqlMapConfigParser class - " +  
  189.                         "please upgrade to IBATIS 2.3.2 or higher in order to use the new 'mappingLocations' feature. " + ex);  
  190.             }  
  191.             //为指定字段值创建Ibatis映射解析器  
  192.             return new SqlMapParser(state);  
  193.         }  
  194.     }  
  195. }  

SqlMapClientFactoryBean实现了SpringFactoryBean接口,是Spring中管理IbatisIoC容器,在IoC容器初始化过程中主要完成定位Ibatis配置文件和Ibatis映射文件等工作。同时SqlMapClientFactoryBean实现了InitializingBean接口,实现了afterPropertiesSet方法,该方法是在IoC容器初始化完成之后由IoC容器进行回调的,在该方法中主要是根据定义的Ibatis配置和映射文件创建IbatisSqlMapClient对象的过程。

2.SqlMapClientTemplate

Spring通过SqlMapClientTemplateIbatis一些通用操作做统一的封装处理,同时也对IbatisAPI做了一些封装,方便开发者使用,下面我们继续分析SqlMapClientTemplateIbatis封装的实现。

(1).execute方法的实现:

JdbcTemplateHibernateTemplate一样,SpringSqlMapClientTemplate中也是通过execute方法封装Ibatis增删改查前的通用操作,同时在execute方法中调用相应的回调对象的回调方法来真正完成Ibatis的处理操作,execute方法源码如下:

[java]  view plain copy
  1. public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException {  
  2.         Assert.notNull(action, "Callback object must not be null");  
  3.         Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");  
  4.         //通过SqlMapClient对象打开一个Ibatis SqlMapSession  
  5.         SqlMapSession session = this.sqlMapClient.openSession();  
  6.         if (logger.isDebugEnabled()) {  
  7.             logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");  
  8.         }  
  9.         Connection ibatisCon = null;  
  10.         try {  
  11.             Connection springCon = null;  
  12.             //获取数据源  
  13.             DataSource dataSource = getDataSource();  
  14.             //根据数据源是否是事务包装数据源代理类型,判断数据源是否需要事务包装  
  15.             boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);  
  16.             try {  
  17.                 //获取连接  
  18.                 ibatisCon = session.getCurrentConnection();  
  19.                 //如果当前Ibatis SqlMapSession还没有创建过连接  
  20.                 if (ibatisCon == null) {  
  21. //如果Ibatis数据源已经在Spring事务管理之下,则直接使用数据源//创建连接,否则,使用DataSourceUtils创建连接,并且创建的连//接置于Spring事务管理之中  
  22.                     springCon = (transactionAware ?  
  23.                             dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));  
  24.                     session.setUserConnection(springCon);  
  25.                     if (logger.isDebugEnabled()) {  
  26.                         logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");  
  27.                     }  
  28.                 }  
  29.                 //如果当前Ibatis SqlMapSession已经创建过连接,则直接使用  
  30.                 else {  
  31.                     if (logger.isDebugEnabled()) {  
  32.                         logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");  
  33.                     }  
  34.                 }  
  35.             }  
  36.             catch (SQLException ex) {  
  37.                 throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);  
  38.             }  
  39.             //调用具体增删改查操作回调对象的方法  
  40.             try {  
  41.                 return action.doInSqlMapClient(session);  
  42.             }  
  43.             catch (SQLException ex) {  
  44.                 throw getExceptionTranslator().translate("SqlMapClient operation"null, ex);  
  45.             }  
  46.             finally {  
  47.                 try {  
  48.                     //释放连接  
  49.                     if (springCon != null) {  
  50.                         if (transactionAware) {  
  51.                             springCon.close();  
  52.                         }  
  53.                         else {  
  54.             DataSourceUtils.doReleaseConnection(springCon, dataSource);  
  55.                         }  
  56.                     }  
  57.                 }  
  58.                 catch (Throwable ex) {  
  59.                     logger.debug("Could not close JDBC Connection", ex);  
  60.                 }  
  61.             }  
  62.         }  
  63.         //关闭Ibatis的SqlMapSession  
  64.         finally {  
  65.             if (ibatisCon == null) {  
  66.                 session.close();  
  67.             }  
  68.         }  
  69.     }  

(2).Spring封装Ibatis API的方法:

我们以Spring的queryForObject方法为例,分析Spring封装Ibatis API的实现,源码如下:

[java]  view plain copy
  1. //查询对象  
  2. public Object queryForObject(final String statementName, final Object parameterObject)  
  3.             throws DataAccessException {  
  4.         //调用execute方法,参数是实现了SqlMapClientCallback接口的匿名内部类,  
  5.         //execute方法中回调该对象的doInSqlMapClient方法  
  6.         return execute(new SqlMapClientCallback<Object>() {  
  7.             //真正调用Ibatis API做具体操作处理的方法  
  8.             public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {  
  9.                 //调用Ibatis SqlMapSession对象的queryForObejct方法  
  10.                 return executor.queryForObject(statementName, parameterObject);  
  11.             }  
  12.         });  
  13.     }   
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐