Spring的JNDI數據源鏈接池配置

在使用 Tomcat服務器 + SpringFramework 進行JavaEE項目的開發部署的時候能夠在Tomcat的配置文件中進行JDBC數據源的配置,具體步驟以下(這裏省略了工程的創建步驟):java

 

1) 添加以下代碼到tomcat的conf目錄下的server.xml中:mysql

Xml代碼  spring

<Context> 
	<Resource name="jdbc/demoDB" auth="Container" 
	type="javax.sql.DataSource"
	driverClassName="com.mysql.jdbc.Driver"
	url="jdbc:mysql://localhost:3306/demo"
	username="root"
	password="123"
	maxActive="50"
	maxIdle="30"
	maxWait="10000" />
</Context>

 完成上述步驟數據源的鏈接池配置已經完成,可是爲了提升項目的可移植性,最好將上述第二步的內容放入到工程的META-INF目錄的context.xml中(這個文件須要自行創建):sql

Xml代碼  數據庫

<?xml version="1.0" encoding="UTF-8"?>
<Context>
      <Resource name="jdbc/demoDB" auth="Container" 
      type="javax.sql.DataSource"
      driverClassName="com.mysql.jdbc.Driver"
      url="jdbc:mysql://localhost:3306/demo"
      username="root"
      password="123"
      maxActive="50"
      maxIdle="30"
      maxWait="10000" />
</Context>

 

2)在Spring的配置文件,如applicationContext.xml中配置配置以下內容:tomcat

Xml代碼  服務器

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName">
			<value>java:comp/env/jdbc/demoDB</value>
		</property>
	</bean>
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
	</bean>
	
	<!-- 這裏是自定義的數據庫基礎操做類 -->
	<bean id="sqlBaseDAO" class="demo.BaseDAOImpl">
		<property name="jdbcTemplate">
			<ref bean="jdbcTemplate" />
		</property>
	</bean>
</beans>

 

3)創建數據庫基礎操做類 BaseDAOImplapp

    接口代碼:less

Java代碼  xss

public interface BaseDAO {
	public List<Map<String, Object>> select(String sql);
	public void update(String how);
	public void insert(Object obj);
	public void insert(String sql);
	public void save(String sql);
	public void edit(String sql);
	public void execute(String sql, PreparedStatementCallback callback);
	
	public void delete(String sql);
	public void insertObjects(String[] sqls);
	public Connection getConnection() throws Exception;
}

 

   實現類代碼:

Java代碼  

public class BaseDAOImpl implements BaseDAO {
	private JdbcTemplate jdbcTemplate;
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
		this.jdbcTemplate = jdbcTemplate;
	}
	public void insert(Object obj) {
	}
	public void insert(String sql) {
		jdbcTemplate.execute(sql);
	}
	public void insertObjects(String[] sqls) {
		jdbcTemplate.batchUpdate(sqls);
	}
	public List<Map<String, Object>> select(String sql) {
		return jdbcTemplate.queryForList(sql);
	}
	public void update(String how) {
		jdbcTemplate.update(how);
	}
	public void delete(String sql) {
		if (sql == null) {
			return;
		}
		jdbcTemplate.execute(sql);
	}
	public void edit(String sql) {
		if (sql == null) {
			return;
		}
		jdbcTemplate.execute(sql);
	}
	public void execute(String sql, PreparedStatementCallback callback) {
		jdbcTemplate.execute(sql, callback);
	}
	
	public void save(String sql) {
		if (sql == null) {
			return;
		}
		jdbcTemplate.execute(sql);
	}
	public Connection getConnection() throws Exception {
		Connection conn = jdbcTemplate.getDataSource().getConnection();
		return conn;
	}
}

 

 

這裏存在一個疑問:

運行以下代碼:

Java代碼  

public static void main(String[] args) {
	org.springframework.jndi.JndiObjectFactoryBean jofb = new org.springframework.jndi.JndiObjectFactoryBean();
	javax.sql.DataSource ds = (javax.sql.DataSource)jofb;
	org.springframework.jdbc.core.JdbcTemplate jTemplate = new org.springframework.jdbc.core.JdbcTemplate();
	jTemplate.setDataSource(ds);
}

 

會報告以下的錯誤:

Out代碼  

Exception in thread "main" java.lang.ClassCastException: org.springframework.jndi.JndiObjectFactoryBean cannot be cast to javax.sql.DataSource

從JndiObjectFactoryBean的源碼中也能夠看到,JndiObjectFactoryBean的父類或所繼承的接口都沒有繼承javax.sql.DataSource接口,因此一下的配置中:

Xml代碼  

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
	<property name="jndiName">
		<value>java:comp/env/jdbc/portalDataService</value>
	</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<property name="dataSource">
		<ref bean="dataSource" />
	</property>
</bean>

 對org.springframework.jdbc.core.JdbcTemplate的dataSource屬性的注入爲什麼可以成功?

 

帶着這樣的疑問去iteye中提問,沒有獲得詳細的解答,可是iteye的提示功能彷佛很不錯,在問題的下方給出了相關內容參考提示,進入到《從源代碼解讀spring之DataSource實現和FactoryBean模式》這個帖子中,看完之後大受啓發。一下是從這篇帖子摘抄出來的內容:

 

 

再看源碼後發現,JndiObjectFactoryBean實現了FactoryBean接口,下面是org.springframework.beans.factory.FactoryBean源代碼裏一段註釋: 

 

Java代碼  

/** 
 * Interface to be implemented by objects used within a BeanFactory 
 * that are themselves factories. If a bean implements this interface, 
 * it is used as a factory, not directly as a bean. 
 * 
 * <p><b>NB: A bean that implements this interface cannot be used 
 * as a normal bean.</b> A FactoryBean is defined in a bean style, 
 * but the object exposed for bean references is always the object 
 * that it creates. 
 */

 

翻譯過來是說:全部實現FactoryBean接口的類都被看成工廠來使用,而不是簡單的直接看成bean來使用,FactoryBean實現類裏定義了要生產的對象,而且由FactoryBean實現類來造該對象的實例,看到這裏聰明的大概已經能猜出個八九不離十了吧,咱們回過頭來看看JndiObjectFactoryBean的實現細節 :

 

Java代碼  

private Object jndiObject;  
/** 
 * Look up the JNDI object and store it. 
 * 廣義上說是造對象的過程,就本例而言,是經過JNDI得到DataSource對象 
 */  
public void afterPropertiesSet() throws IllegalArgumentException, NamingException {  
    super.afterPropertiesSet();  
  
    if (this.proxyInterface != null) {  
        if (this.defaultObject != null) {  
            throw new IllegalArgumentException(  
                    "'defaultObject' is not supported in combination with 'proxyInterface'");  
        }  
        // We need a proxy and a JndiObjectTargetSource.  
        this.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);  
    }  
  
    else {  
        if (!this.lookupOnStartup || !this.cache) {  
            throw new IllegalArgumentException(  
                "Cannot deactivate 'lookupOnStartup' or 'cache' without specifying a 'proxyInterface'");  
        }  
        if (this.defaultObject != null && getExpectedType() != null &&  
                !getExpectedType().isInstance(this.defaultObject)) {  
            throw new IllegalArgumentException("Default object [" + this.defaultObject +  
                    "] of type [" + this.defaultObject.getClass().getName() +  
                    "] is not of expected type [" + getExpectedType().getName() + "]");  
        }  
        // Locate specified JNDI object.  
        this.jndiObject = lookupWithFallback();  
    }  
}  
/** 
 * Return the singleton JNDI object. 
 * 返回JNDI對象(DataSource對象) 
 */  
public Object getObject() {  
    return this.jndiObject;  
}  
  
public Class getObjectType() {  
    if (this.proxyInterface != null) {  
        return this.proxyInterface;  
    }  
    else if (this.jndiObject != null) {  
        return this.jndiObject.getClass();  
    }  
    else {  
        return getExpectedType();  
    }  
}

 

對於JndiObjectFactoryBean對象,spring IOC容器啓動時確實造了它的對象,只不過這時是工廠自己,spring會自動調用工廠裏的afterPropertiesSet()方法去造真正須要的bean,而後調用getObject()和getObjectType()方法返回已造好的對象和類型,再將其準確的注入依賴它的其餘bean裏面。

 

好吧,也許上面org.springframework.beans.factory.FactoryBean的註釋看起來像家長教育孩子該怎麼怎麼,那麼Spring究竟是怎麼實現這種思想的呢?參考《Spring技術內幕》中2.5.3節對FactoryBean的實現的講解,結合Spring的源碼能夠看到:

     常見的工廠Bean是怎樣實現的,這些FactoryBean爲應用生成須要的對象,這些對象每每是通過特殊處理的,好比像 ProxyFactoryBean 這樣的特殊 Bean。FactoryBean 的生產特性是在getBean中起做用的,咱們看到下面的調用:

再來看FactoryBean特性的實現:

 

Java代碼  

//該方法在org.springframework.beans.factory.support.AbstractBeanFactory類中
protected Object getObjectForBeanInstance(
		Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
	// Don't let calling code try to dereference the factory if the bean isn't a factory.
	// 若是這裏不是對FactoryBean的調用,那麼結束處理。
	if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
		throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
	}
	// Now we have the bean instance, which may be a normal bean or a FactoryBean.
	// If it's a FactoryBean, we use it to create a bean instance, unless the
	// caller actually wants a reference to the factory.
	if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
		return beanInstance;
	}
	Object object = null;
	if (mbd == null) {
		object = getCachedObjectForFactoryBean(beanName);
	}
	if (object == null) {
		// Return bean instance from factory.
		FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
		// Caches object obtained from FactoryBean if it is a singleton.
		if (mbd == null && containsBeanDefinition(beanName)) {
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		boolean synthetic = (mbd != null && mbd.isSynthetic());
		//這裏從FactoryBean中獲得bean。 
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}
//該方法在org.springframework.beans.factory.support.FactoryBeanRegistrySupport類中
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
	if (factory.isSingleton() && containsSingleton(beanName)) {
		synchronized (getSingletonMutex()) {
			Object object = this.factoryBeanObjectCache.get(beanName);
			if (object == null) {
				object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
				this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
			}
			return (object != NULL_OBJECT ? object : null);
		}
	}
	else {
		return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
	}
}
//該方法在org.springframework.beans.factory.support.FactoryBeanRegistrySupport類中
private Object doGetObjectFromFactoryBean(
		final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
		throws BeanCreationException {
	Object object;
	//這裏調用factory的getObject方法來從FactoryBean中獲得bean。
	try {
		if (System.getSecurityManager() != null) {
			AccessControlContext acc = getAccessControlContext();
			try {
				object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
					public Object run() throws Exception {
							return factory.getObject();
						}
					}, acc);
			}
			catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		}
		else {
			object = factory.getObject();
		}
	}
	catch (FactoryBeanNotInitializedException ex) {
		throw new BeanCurrentlyInCreationException(beanName, ex.toString());
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
	}
	
	// Do not accept a null value for a FactoryBean that's not fully
	// initialized yet: Many FactoryBeans just return null then.
	if (object == null && isSingletonCurrentlyInCreation(beanName)) {
		throw new BeanCurrentlyInCreationException(
				beanName, "FactoryBean which is currently in creation returned null from getObject");
	}
	if (object != null && shouldPostProcess) {
		try {
			object = postProcessObjectFromFactoryBean(object, beanName);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
		}
	}
	return object;
}

     這裏返回的已是做爲工廠的 FactoryBean 生產的產品,並非 FactoryBean 自己。這種FactoryBean的機制能夠爲咱們提供一個很好的封裝機制,好比封裝Proxy、RMI、JNDI等。通過對FactoryBean實現過程的原理分析,相信讀者會對getObject方法有很深入的印象。這個方法就是主要的FactoryBean 的接口,須要實現特定的工廠的生產過程,至於這個生產過程是怎樣和IoC容器整合的,就是咱們在上面分析的內容。

 

那麼返回的類型是怎麼肯定爲javax.sql.DataSource類型的呢?回頭再看在context.xml中的數據源配置能夠看到:

 

Xml代碼  

  

type="javax.sql.DataSource"

這樣一句。而後在去細看JndiObjectFactoryBean類中的afterPropertiesSet方法的具體代碼因此一切都明瞭了。

 

綜上所述,這裏主要仍是要對Spring的FactoryBean模式的理解最爲重要。

相關文章
相關標籤/搜索