今天在看代码时,发现这样一段配置:
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value>classpath:sqlmap.xml</value> </property> </bean>
打开org.springframework.orm.ibatis.SqlMapClientFactoryBean类,实现了FactoryBean, InitializingBean接口,它不是SqlMapClient的子类,为什么能够成为SqlMapClient的示例呢?带着这个疑问,开启了FactoryBean之旅。
一. Spring Bean
在Spring BeanFactory管理着两种Bean:
1. 普通的java bean。
2. 工厂Bean,即实现了FactoryBean接口的Bean。它负责生成目标bean,因此工厂Bean返回的对象不是自身类的实例,返回的是该工厂Bean的getObject()方法返回的对象,即目标Bean。
二. FactoryBean简介
org.springframework.beans.factory.FactoryBean定义了三个方法 ,如下:
public interface FactoryBean { /** * 返回该FactoryBean生产的对象实例,即目标Bean **/ Object getObject() throws Exception; /** * 返回目标Bean的类型 **/ Class getObjectType(); /** * 目标Bean是否单例 */ boolean isSingleton(); }当某些对象实例化的过程过于发杂,使用xml配置繁琐,例如SqlMapClient,就可以创建一个工程Bean,继承FactoryBean接口。
配置方法如下:
<bean id=" " class="…FactoryBean" />
使用该Bean注入的对象类型是getObjectType()返回的类型。
另外,如果想获得Factory本身,则可以通过在定义该bean的id前加&来实现。
三. 应用场景
1. 对象实例化过程过于复杂,xml配置繁琐,可以使用BeanFactory封装实例化的过程。
2. 在不同情况下需要不同的对象,比如链接数据库时,测试环境下采取直连方式,但是线上采用jndi获取。
四. 示例
本来想自己写一个的,但是SqlMapClientFactoryBean的实现真的很完美,这个类很典型,继承了FactoryBean和InitializingBean接口,并且使用了Resource和Properties功能,很有参考价值,直接贴在这里吧。。不要BS我啊
* Copyright 2002-2007 the original author or authors. package org.springframework.orm.ibatis; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Properties; import javax.sql.DataSource; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.client.SqlMapClientBuilder; import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; import com.ibatis.sqlmap.engine.transaction.TransactionConfig; import com.ibatis.sqlmap.engine.transaction.TransactionManager; import com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.Resource; import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.util.ClassUtils; /** * {@link org.springframework.beans.factory.FactoryBean} that creates an * iBATIS {@link com.ibatis.sqlmap.client.SqlMapClient}. This is the usual * way to set up a shared iBATIS SqlMapClient in a Spring application context; * the SqlMapClient can then be passed to iBATIS-based DAOs via dependency * injection. * * <p>Either {@link org.springframework.jdbc.datasource.DataSourceTransactionManager} * or {@link org.springframework.transaction.jta.JtaTransactionManager} can be * used for transaction demarcation in combination with a SqlMapClient, * with JTA only necessary for transactions which span multiple databases. * * <p>Allows for specifying a DataSource at the SqlMapClient level. This * is preferable to per-DAO DataSource references, as it allows for lazy * loading and avoids repeated DataSource references in every DAO. * * <p>Note: As of Spring 2.0.2, this class explicitly supports iBATIS 2.3. * Backwards compatibility with iBATIS 2.1 and 2.2 is preserved for the * time being, through corresponding reflective checks. * * @author Juergen Hoeller * @since 24.02.2004 * @see #setConfigLocation * @see #setDataSource * @see SqlMapClientTemplate#setSqlMapClient * @see SqlMapClientTemplate#setDataSource */ public class SqlMapClientFactoryBean implements FactoryBean, InitializingBean { // Determine whether the SqlMapClientBuilder.buildSqlMapClient(InputStream) // method is available, for use in the "buildSqlMapClient" template method. private final static boolean buildSqlMapClientWithInputStreamMethodAvailable = ClassUtils.hasMethod(SqlMapClientBuilder.class, "buildSqlMapClient", new Class[] {InputStream.class}); // Determine whether the SqlMapClientBuilder.buildSqlMapClient(InputStream, Properties) // method is available, for use in the "buildSqlMapClient" template method. private final static boolean buildSqlMapClientWithInputStreamAndPropertiesMethodAvailable = ClassUtils.hasMethod(SqlMapClientBuilder.class, "buildSqlMapClient", new Class[] {InputStream.class, Properties.class}); private static final ThreadLocal configTimeLobHandlerHolder = new ThreadLocal(); /** * Return the LobHandler for the currently configured iBATIS SqlMapClient, * to be used by TypeHandler implementations like ClobStringTypeHandler. * <p>This instance will be set before initialization of the corresponding * SqlMapClient, and reset immediately afterwards. It is thus only available * during configuration. * @see #setLobHandler * @see org.springframework.orm.ibatis.support.ClobStringTypeHandler * @see org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler * @see org.springframework.orm.ibatis.support.BlobSerializableTypeHandler */ public static LobHandler getConfigTimeLobHandler() { return (LobHandler) configTimeLobHandlerHolder.get(); } private Resource configLocation; private Properties sqlMapClientProperties; private DataSource dataSource; private boolean useTransactionAwareDataSource = true; private Class transactionConfigClass = ExternalTransactionConfig.class; private Properties transactionConfigProperties; private LobHandler lobHandler; private SqlMapClient sqlMapClient; public SqlMapClientFactoryBean() { this.transactionConfigProperties = new Properties(); this.transactionConfigProperties.setProperty("SetAutoCommitAllowed", "false"); } /** * Set the location of the iBATIS SqlMapClient config file. * A typical value is "WEB-INF/sql-map-config.xml". */ public void setConfigLocation(Resource configLocation) { this.configLocation = configLocation; } /** * Set optional properties to be passed into the SqlMapClientBuilder, as * alternative to a <code><properties></code> tag in the sql-map-config.xml * file. Will be used to resolve placeholders in the config file. * @see #setConfigLocation * @see com.ibatis.sqlmap.client.SqlMapClientBuilder#buildSqlMapClient(java.io.Reader, java.util.Properties) */ public void setSqlMapClientProperties(Properties sqlMapClientProperties) { this.sqlMapClientProperties = sqlMapClientProperties; } /** * Set the DataSource to be used by iBATIS SQL Maps. This will be passed to the * SqlMapClient as part of a TransactionConfig instance. * <p>If specified, this will override corresponding settings in the SqlMapClient * properties. Usually, you will specify DataSource and transaction configuration * <i>either</i> here <i>or</i> in SqlMapClient properties. * <p>Specifying a DataSource for the SqlMapClient rather than for each individual * DAO allows for lazy loading, for example when using PaginatedList results. * <p>With a DataSource passed in here, you don't need to specify one for each DAO. * Passing the SqlMapClient to the DAOs is enough, as it already carries a DataSource. * Thus, it's recommended to specify the DataSource at this central location only. * <p>Thanks to Brandon Goodin from the iBATIS team for the hint on how to make * this work with Spring's integration strategy! * @see #setTransactionConfigClass * @see #setTransactionConfigProperties * @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource * @see SqlMapClientTemplate#setDataSource * @see SqlMapClientTemplate#queryForPaginatedList */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * Set whether to use a transaction-aware DataSource for the SqlMapClient, * i.e. whether to automatically wrap the passed-in DataSource with Spring's * TransactionAwareDataSourceProxy. * <p>Default is "true": When the SqlMapClient performs direct database operations * outside of Spring's SqlMapClientTemplate (for example, lazy loading or direct * SqlMapClient access), it will still participate in active Spring-managed * transactions. * <p>As a further effect, using a transaction-aware DataSource will apply * remaining transaction timeouts to all created JDBC Statements. This means * that all operations performed by the SqlMapClient will automatically * participate in Spring-managed transaction timeouts. * <p>Turn this flag off to get raw DataSource handling, without Spring transaction * checks. Operations on Spring's SqlMapClientTemplate will still detect * Spring-managed transactions, but lazy loading or direct SqlMapClient access won't. * @see #setDataSource * @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy * @see org.springframework.jdbc.datasource.DataSourceTransactionManager * @see SqlMapClientTemplate * @see com.ibatis.sqlmap.client.SqlMapClient */ public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) { this.useTransactionAwareDataSource = useTransactionAwareDataSource; } /** * Set the iBATIS TransactionConfig class to use. Default is * <code>com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig</code>. * <p>Will only get applied when using a Spring-managed DataSource. * An instance of this class will get populated with the given DataSource * and initialized with the given properties. * <p>The default ExternalTransactionConfig is appropriate if there is * external transaction management that the SqlMapClient should participate * in: be it Spring transaction management, EJB CMT or plain JTA. This * should be the typical scenario. If there is no active transaction, * SqlMapClient operations will execute SQL statements non-transactionally. * <p>JdbcTransactionConfig or JtaTransactionConfig is only necessary * when using the iBATIS SqlMapTransactionManager API instead of external * transactions. If there is no explicit transaction, SqlMapClient operations * will automatically start a transaction for their own scope (in contrast * to the external transaction mode, see above). * <p><b>It is strongly recommended to use iBATIS SQL Maps with Spring * transaction management (or EJB CMT).</b> In this case, the default * ExternalTransactionConfig is fine. Lazy loading and SQL Maps operations * without explicit transaction demarcation will execute non-transactionally. * <p>Even with Spring transaction management, it might be desirable to * specify JdbcTransactionConfig: This will still participate in existing * Spring-managed transactions, but lazy loading and operations without * explicit transaction demaration will execute in their own auto-started * transactions. However, this is usually not necessary. * @see #setDataSource * @see #setTransactionConfigProperties * @see com.ibatis.sqlmap.engine.transaction.TransactionConfig * @see com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig * @see com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig * @see com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig * @see com.ibatis.sqlmap.client.SqlMapTransactionManager */ public void setTransactionConfigClass(Class transactionConfigClass) { if (transactionConfigClass == null || !TransactionConfig.class.isAssignableFrom(transactionConfigClass)) { throw new IllegalArgumentException("Invalid transactionConfigClass: does not implement " + "com.ibatis.sqlmap.engine.transaction.TransactionConfig"); } this.transactionConfigClass = transactionConfigClass; } /** * Set properties to be passed to the TransactionConfig instance used * by this SqlMapClient. Supported properties depend on the concrete * TransactionConfig implementation used: * <p><ul> * <li><b>ExternalTransactionConfig</b> supports "DefaultAutoCommit" * (default: false) and "SetAutoCommitAllowed" (default: true). * Note that Spring uses SetAutoCommitAllowed = false as default, * in contrast to the iBATIS default, to always keep the original * autoCommit value as provided by the connection pool. * <li><b>JdbcTransactionConfig</b> does not supported any properties. * <li><b>JtaTransactionConfig</b> supports "UserTransaction" * (no default), specifying the JNDI location of the JTA UserTransaction * (usually "java:comp/UserTransaction"). * </ul> * @see com.ibatis.sqlmap.engine.transaction.TransactionConfig#initialize * @see com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig * @see com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig * @see com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig */ public void setTransactionConfigProperties(Properties transactionConfigProperties) { this.transactionConfigProperties = transactionConfigProperties; } /** * Set the LobHandler to be used by the SqlMapClient. * Will be exposed at config time for TypeHandler implementations. * @see #getConfigTimeLobHandler * @see com.ibatis.sqlmap.engine.type.TypeHandler * @see org.springframework.orm.ibatis.support.ClobStringTypeHandler * @see org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler * @see org.springframework.orm.ibatis.support.BlobSerializableTypeHandler */ public void setLobHandler(LobHandler lobHandler) { this.lobHandler = lobHandler; } public void afterPropertiesSet() throws Exception { if (this.configLocation == null) { throw new IllegalArgumentException("configLocation is required"); } if (this.lobHandler != null) { // Make given LobHandler available for SqlMapClient configuration. // Do early because because mapping resource might refer to custom types. configTimeLobHandlerHolder.set(this.lobHandler); } try { this.sqlMapClient = buildSqlMapClient(this.configLocation, this.sqlMapClientProperties); // Tell the SqlMapClient to use the given DataSource, if any. if (this.dataSource != null) { TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance(); DataSource dataSourceToUse = this.dataSource; if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) { dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource); } transactionConfig.setDataSource(dataSourceToUse); transactionConfig.initialize(this.transactionConfigProperties); applyTransactionConfig(this.sqlMapClient, transactionConfig); } } finally { if (this.lobHandler != null) { // Reset LobHandler holder. configTimeLobHandlerHolder.set(null); } } } /** * Build a SqlMapClient instance based on the given standard configuration. * <p>The default implementation uses the standard iBATIS {@link SqlMapClientBuilder} * API to build a SqlMapClient instance based on an InputStream (if possible, * on iBATIS 2.3 and higher) or on a Reader (on iBATIS up to version 2.2). * @param configLocation the config file to load from * @param properties the SqlMapClient properties (if any) * @return the SqlMapClient instance (never <code>null</code>) * @throws IOException if loading the config file failed * @see com.ibatis.sqlmap.client.SqlMapClientBuilder#buildSqlMapClient */ protected SqlMapClient buildSqlMapClient(Resource configLocation, Properties properties) throws IOException { InputStream is = configLocation.getInputStream(); if (properties != null) { if (buildSqlMapClientWithInputStreamAndPropertiesMethodAvailable) { return SqlMapClientBuilder.buildSqlMapClient(is, properties); } else { return SqlMapClientBuilder.buildSqlMapClient(new InputStreamReader(is), properties); } } else { if (buildSqlMapClientWithInputStreamMethodAvailable) { return SqlMapClientBuilder.buildSqlMapClient(is); } else { return SqlMapClientBuilder.buildSqlMapClient(new InputStreamReader(is)); } } } /** * Apply the given iBATIS TransactionConfig to the SqlMapClient. * <p>The default implementation casts to ExtendedSqlMapClient, retrieves the maximum * number of concurrent transactions from the SqlMapExecutorDelegate, and sets * an iBATIS TransactionManager with the given TransactionConfig. * @param sqlMapClient the SqlMapClient to apply the TransactionConfig to * @param transactionConfig the iBATIS TransactionConfig to apply * @see com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient * @see com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate#getMaxTransactions * @see com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate#setTxManager */ protected void applyTransactionConfig(SqlMapClient sqlMapClient, TransactionConfig transactionConfig) { if (!(this.sqlMapClient instanceof ExtendedSqlMapClient)) { throw new IllegalArgumentException( "Cannot set TransactionConfig with DataSource for SqlMapClient if not of type " + "ExtendedSqlMapClient: " + this.sqlMapClient); } ExtendedSqlMapClient extendedClient = (ExtendedSqlMapClient) this.sqlMapClient; transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions()); extendedClient.getDelegate().setTxManager(new TransactionManager(transactionConfig)); } public Object getObject() { return this.sqlMapClient; } public Class getObjectType() { return (this.sqlMapClient != null ? this.sqlMapClient.getClass() : SqlMapClient.class); } public boolean isSingleton() { return true; } }
相关推荐
在Spring框架中,`FactoryBean`是一个非常重要的接口,它允许我们自定义对象的创建方式。这个接口使得我们可以实现自己的工厂逻辑,返回一个完全初始化、配置好的对象,甚至可以是带有额外增强功能的对象,比如代理...
在Spring框架中,`FactoryBean`是一个非常关键的接口,它允许我们自定义对象的创建方式,这在处理复杂依赖关系或需要特殊初始化逻辑时尤为有用。本篇将通过一个测试代码示例来深入解析`FactoryBean`的工作原理及其在...
Spring 中 FactoryBean 中的 getObject() 方法实例解析 Spring 框架中 FactoryBean 是一个非常重要的概念,它提供了一种创建和管理 Bean 的机制。在 Spring 中,FactoryBean 是一个特殊的 Bean,它可以创建其他 ...
首先,FactoryBean接口是Spring框架的核心组件之一,位于`org.springframework.beans.factory`包下。该接口定义了两个主要方法: 1. `Object getObject() throws Exception;`:返回由FactoryBean创建的对象。Spring...
在Spring框架中,`FactoryBean`是一个非常重要的接口,它为Spring容器提供了更高级别的抽象,使得我们可以自定义Bean的创建过程。当一个Bean的初始化需要处理复杂的逻辑、依赖其他多个Bean,或者需要进行特殊处理时...
简单了解Spring中BeanFactory与FactoryBean的区别 Spring框架中有两个非常关键的接口:BeanFactory和FactoryBean。虽然它们的名字相似,但它们的作用和实现机制却有很大的不同。 BeanFactory是Spring IoC容器的...
Spring中的FactoryBean代码示例 在Spring框架中,FactoryBean是一种特殊的Bean,它可以生成其他Bean的实例。今天我们来了解一下FactoryBean的实现和使用。 首先,让我们从SessionFactory说起。在使用SSH集成开发时...
在Spring框架中,`FactoryBean`是一个非常重要的概念,它为Spring容器提供了自定义对象创建的能力。本篇我们将深入探讨`FactoryBean`的最简实现及其在Spring配置文件中的使用,帮助你理解其实例化过程。 `...
BeanFactory是FactoryBean的基础,FactoryBean是建立在BeanFactory之上的。 三、FactoryBean的应用 FactoryBean的应用非常广泛,在复杂的业务系统中,FactoryBean可以用来创建和管理对象实例。例如,在一个电子...
通过这个示例,我们可以看到 FactoryBean 的强大之处。它可以将复杂的实例化逻辑封装起来,使得 Bean 的配置更加灵活和灵活。同时,它还可以帮助我们实现单例模式,使得我们的应用程序更加高效和稳定。
相比之下,FactoryBean是一个特殊的bean,它可以返回bean的实例的工厂bean,通过实现该接口可以对bean进行一些额外的操作,例如根据不同的配置类型返回不同类型的bean,简化xml配置等。在使用上也有些特殊,...
在Spring框架中,BeanFactory和FactoryBean是两个重要的接口,它们各自扮演着不同的角色,但都与Spring容器的管理和创建对象密切相关。 **BeanFactory接口**是Spring容器的基础,它是整个Spring IoC(Inversion of ...
`FactoryBean`的强大之处在于它可以处理复杂的对象创建逻辑,比如需要依赖其他服务、数据库连接、线程安全等。此外,`FactoryBean`还可以用于创建单例或多例对象,只需要在`FactoryBean`的实现中控制即可。 在...
Spring 提供了多种方式来实例化和管理 Bean,包括通过构造方法、简单工厂模式、FactoryBean 等。 Spring 还提供了多种范围的 Bean,包括 singleton、prototype 等。 Spring 的 IoC 容器是框架的核心组件,它负责...
4. **MapperFactoryBean**:它是 Spring 的 FactoryBean,用于实例化 Mapper 接口并注入到 Spring 容器。这样,我们可以在 Spring 环境中通过 DAO 接口直接调用 SQL 方法,而无需关心实现细节。 5. **Transaction ...
BeanFactory 是 Spring 框架中的核心组件之一,负责管理 Bean 的生命周期,包括实例化、配置和注入对象之间的关系。它是 Spring IOC(控制反转)容器的核心组件,提供了许多强大的功能,包括自动注入、依赖关系管理...
在Spring框架中,`FactoryBean`是一个至关重要的接口,它为Spring容器提供了创建对象的自定义逻辑。`FactoryBean`的使用使得我们可以控制对象的创建过程,甚至可以在对象被Spring管理的同时,添加额外的初始化或者...
它扩展了 Spring 的 FactoryBean 接口,可以直接在 Spring 配置文件中声明,通过配置数据源、MyBatis 的配置文件等参数,轻松创建 SqlSessionFactory。 2. SqlSessionTemplate:这是一个线程安全的 SqlSession 实现...
- MapperFactoryBean:实现了Spring的FactoryBean接口,为每个Mapper接口创建一个代理对象,调用Mapper接口的方法时,会自动执行对应的SQL。 4. **MyBatis-Spring的使用** - 配置:在Spring配置文件中添加...
本篇文章将深入探讨Spring AOP的内部实现,以及如何通过源代码理解其DataSource实现和FactoryBean模式。 首先,让我们了解AOP的基本概念。AOP的核心思想是“切面”,它封装了特定的关注点,如日志记录、事务管理、...