午后星期午

          Spring的JNDI數據源連接池配置示例及Spring對JNDI實現分析

          個人學習參考所用,勿噴!

           

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

           

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

          Xml代碼  收藏代碼
          1. <Context>   
          2.     <Resource name="jdbc/demoDB" auth="Container"   
          3.     type="javax.sql.DataSource"  
          4.     driverClassName="com.mysql.jdbc.Driver"  
          5.     url="jdbc:mysql://localhost:3306/demo"  
          6.     username="root"  
          7.     password="123"  
          8.     maxActive="50"  
          9.     maxIdle="30"  
          10.     maxWait="10000" />  
          11. </Context>  

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

          Xml代碼  收藏代碼
          1. <?xml version="1.0" encoding="UTF-8"?>  
          2. <Context>  
          3.       <Resource name="jdbc/demoDB" auth="Container"   
          4.       type="javax.sql.DataSource"  
          5.       driverClassName="com.mysql.jdbc.Driver"  
          6.       url="jdbc:mysql://localhost:3306/demo"  
          7.       username="root"  
          8.       password="123"  
          9.       maxActive="50"  
          10.       maxIdle="30"  
          11.       maxWait="10000" />  
          12. </Context>  

           

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

          Xml代碼  收藏代碼
          1. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
          2. <beans>  
          3.     <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">  
          4.         <property name="jndiName">  
          5.             <value>java:comp/env/jdbc/demoDB</value>  
          6.         </property>  
          7.     </bean>  
          8.     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
          9.         <property name="dataSource">  
          10.             <ref bean="dataSource" />  
          11.         </property>  
          12.     </bean>  
          13.       
          14.     <!-- 這里是自定義的數據庫基礎操作類 -->  
          15.     <bean id="sqlBaseDAO" class="demo.BaseDAOImpl">  
          16.         <property name="jdbcTemplate">  
          17.             <ref bean="jdbcTemplate" />  
          18.         </property>  
          19.     </bean>  
          20. </beans>  

           

          3)建立數據庫基礎操作類 BaseDAOImpl

              接口代碼:

          Java代碼  收藏代碼
          1. public interface BaseDAO {  
          2.   
          3.     public List<Map<String, Object>> select(String sql);  
          4.   
          5.     public void update(String how);  
          6.   
          7.     public void insert(Object obj);  
          8.   
          9.     public void insert(String sql);  
          10.   
          11.     public void save(String sql);  
          12.   
          13.     public void edit(String sql);  
          14.   
          15.     public void execute(String sql, PreparedStatementCallback callback);  
          16.       
          17.     public void delete(String sql);  
          18.   
          19.     public void insertObjects(String[] sqls);  
          20.   
          21.     public Connection getConnection() throws Exception;  
          22.   
          23. }  

           

             實現類代碼:

          Java代碼  收藏代碼
          1. public class BaseDAOImpl implements BaseDAO {  
          2.     private JdbcTemplate jdbcTemplate;  
          3.   
          4.     public void setJdbcTemplate(JdbcTemplate jdbcTemplate){  
          5.         this.jdbcTemplate = jdbcTemplate;  
          6.     }  
          7.   
          8.     public void insert(Object obj) {  
          9.   
          10.     }  
          11.   
          12.     public void insert(String sql) {  
          13.         jdbcTemplate.execute(sql);  
          14.     }  
          15.   
          16.     public void insertObjects(String[] sqls) {  
          17.         jdbcTemplate.batchUpdate(sqls);  
          18.     }  
          19.   
          20.     public List<Map<String, Object>> select(String sql) {  
          21.         return jdbcTemplate.queryForList(sql);  
          22.     }  
          23.   
          24.     public void update(String how) {  
          25.         jdbcTemplate.update(how);  
          26.   
          27.     }  
          28.   
          29.     public void delete(String sql) {  
          30.         if (sql == null) {  
          31.             return;  
          32.         }  
          33.         jdbcTemplate.execute(sql);  
          34.     }  
          35.   
          36.     public void edit(String sql) {  
          37.         if (sql == null) {  
          38.             return;  
          39.         }  
          40.         jdbcTemplate.execute(sql);  
          41.     }  
          42.   
          43.     public void execute(String sql, PreparedStatementCallback callback) {  
          44.         jdbcTemplate.execute(sql, callback);  
          45.     }  
          46.       
          47.     public void save(String sql) {  
          48.         if (sql == null) {  
          49.             return;  
          50.         }  
          51.         jdbcTemplate.execute(sql);  
          52.     }  
          53.   
          54.     public Connection getConnection() throws Exception {  
          55.         Connection conn = jdbcTemplate.getDataSource().getConnection();  
          56.         return conn;  
          57.     }  
          58.   
          59. }  

           

           

          這里存在一個疑問:

          運行如下代碼:

          Java代碼  收藏代碼
          1. public static void main(String[] args) {  
          2.     org.springframework.jndi.JndiObjectFactoryBean jofb = new org.springframework.jndi.JndiObjectFactoryBean();  
          3.     javax.sql.DataSource ds = (javax.sql.DataSource)jofb;  
          4.     org.springframework.jdbc.core.JdbcTemplate jTemplate = new org.springframework.jdbc.core.JdbcTemplate();  
          5.     jTemplate.setDataSource(ds);  
          6. }  

           

          會報告如下的錯誤:

          Out代碼  收藏代碼
          1. Exception in thread "main" java.lang.ClassCastException: org.springframework.jndi.JndiObjectFactoryBean cannot be cast to javax.sql.DataSource  

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

          Xml代碼  收藏代碼
          1. <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">  
          2.     <property name="jndiName">  
          3.         <value>java:comp/env/jdbc/portalDataService</value>  
          4.     </property>  
          5. </bean>  
          6. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
          7.     <property name="dataSource">  
          8.         <ref bean="dataSource" />  
          9.     </property>  
          10. </bean>  

           對org.springframework.jdbc.core.JdbcTemplate的dataSource屬性的注入為何能夠成功?

           

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

           

           

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

           

          Java代碼  收藏代碼
          1. /**  
          2.  * Interface to be implemented by objects used within a BeanFactory  
          3.  * that are themselves factories. If a bean implements this interface,  
          4.  * it is used as a factory, not directly as a bean.  
          5.  *  
          6.  * <p><b>NB: A bean that implements this interface cannot be used  
          7.  * as a normal bean.</b> A FactoryBean is defined in a bean style,  
          8.  * but the object exposed for bean references is always the object  
          9.  * that it creates.  
          10.  */   

           

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

           

          Java代碼  收藏代碼
          1. private Object jndiObject;    
          2. /**  
          3.  * Look up the JNDI object and store it.  
          4.  * 廣義上說是造對象的過程,就本例而言,是通過JNDI獲得DataSource對象  
          5.  */    
          6. public void afterPropertiesSet() throws IllegalArgumentException, NamingException {    
          7.     super.afterPropertiesSet();    
          8.     
          9.     if (this.proxyInterface != null) {    
          10.         if (this.defaultObject != null) {    
          11.             throw new IllegalArgumentException(    
          12.                     "'defaultObject' is not supported in combination with 'proxyInterface'");    
          13.         }    
          14.         // We need a proxy and a JndiObjectTargetSource.    
          15.         this.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);    
          16.     }    
          17.     
          18.     else {    
          19.         if (!this.lookupOnStartup || !this.cache) {    
          20.             throw new IllegalArgumentException(    
          21.                 "Cannot deactivate 'lookupOnStartup' or 'cache' without specifying a 'proxyInterface'");    
          22.         }    
          23.         if (this.defaultObject != null && getExpectedType() != null &&    
          24.                 !getExpectedType().isInstance(this.defaultObject)) {    
          25.             throw new IllegalArgumentException("Default object [" + this.defaultObject +    
          26.                     "] of type [" + this.defaultObject.getClass().getName() +    
          27.                     "] is not of expected type [" + getExpectedType().getName() + "]");    
          28.         }    
          29.         // Locate specified JNDI object.    
          30.         this.jndiObject = lookupWithFallback();    
          31.     }    
          32. }    
          33. /**  
          34.  * Return the singleton JNDI object.  
          35.  * 返回JNDI對象(DataSource對象)  
          36.  */    
          37. public Object getObject() {    
          38.     return this.jndiObject;    
          39. }    
          40.     
          41. public Class getObjectType() {    
          42.     if (this.proxyInterface != null) {    
          43.         return this.proxyInterface;    
          44.     }    
          45.     else if (this.jndiObject != null) {    
          46.         return this.jndiObject.getClass();    
          47.     }    
          48.     else {    
          49.         return getExpectedType();    
          50.     }    
          51. }    

           

          對于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代碼  收藏代碼
          1. //該方法在org.springframework.beans.factory.support.AbstractBeanFactory類中  
          2. protected Object getObjectForBeanInstance(  
          3.         Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {  
          4.   
          5.     // Don't let calling code try to dereference the factory if the bean isn't a factory.  
          6.     // 如果這里不是對FactoryBean的調用,那么結束處理。  
          7.     if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {  
          8.         throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());  
          9.     }  
          10.   
          11.     // Now we have the bean instance, which may be a normal bean or a FactoryBean.  
          12.     // If it's a FactoryBean, we use it to create a bean instance, unless the  
          13.     // caller actually wants a reference to the factory.  
          14.     if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {  
          15.         return beanInstance;  
          16.     }  
          17.   
          18.     Object object = null;  
          19.     if (mbd == null) {  
          20.         object = getCachedObjectForFactoryBean(beanName);  
          21.     }  
          22.     if (object == null) {  
          23.         // Return bean instance from factory.  
          24.         FactoryBean<?> factory = (FactoryBean<?>) beanInstance;  
          25.         // Caches object obtained from FactoryBean if it is a singleton.  
          26.         if (mbd == null && containsBeanDefinition(beanName)) {  
          27.             mbd = getMergedLocalBeanDefinition(beanName);  
          28.         }  
          29.         boolean synthetic = (mbd != null && mbd.isSynthetic());  
          30.         //這里從FactoryBean中得到bean。   
          31.         object = getObjectFromFactoryBean(factory, beanName, !synthetic);  
          32.     }  
          33.     return object;  
          34. }  
          35.   
          36. //該方法在org.springframework.beans.factory.support.FactoryBeanRegistrySupport類中  
          37. protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {  
          38.     if (factory.isSingleton() && containsSingleton(beanName)) {  
          39.         synchronized (getSingletonMutex()) {  
          40.             Object object = this.factoryBeanObjectCache.get(beanName);  
          41.             if (object == null) {  
          42.                 object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
          43.                 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));  
          44.             }  
          45.             return (object != NULL_OBJECT ? object : null);  
          46.         }  
          47.     }  
          48.     else {  
          49.         return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
          50.     }  
          51. }  
          52.   
          53. //該方法在org.springframework.beans.factory.support.FactoryBeanRegistrySupport類中  
          54. private Object doGetObjectFromFactoryBean(  
          55.         final FactoryBean factory, final String beanName, final boolean shouldPostProcess)  
          56.         throws BeanCreationException {  
          57.   
          58.     Object object;  
          59.     //這里調用factory的getObject方法來從FactoryBean中得到bean。  
          60.     try {  
          61.         if (System.getSecurityManager() != null) {  
          62.             AccessControlContext acc = getAccessControlContext();  
          63.             try {  
          64.                 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
          65.                     public Object run() throws Exception {  
          66.                             return factory.getObject();  
          67.                         }  
          68.                     }, acc);  
          69.             }  
          70.             catch (PrivilegedActionException pae) {  
          71.                 throw pae.getException();  
          72.             }  
          73.         }  
          74.         else {  
          75.             object = factory.getObject();  
          76.         }  
          77.     }  
          78.     catch (FactoryBeanNotInitializedException ex) {  
          79.         throw new BeanCurrentlyInCreationException(beanName, ex.toString());  
          80.     }  
          81.     catch (Throwable ex) {  
          82.         throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);  
          83.     }  
          84.       
          85.     // Do not accept a null value for a FactoryBean that's not fully  
          86.     // initialized yet: Many FactoryBeans just return null then.  
          87.     if (object == null && isSingletonCurrentlyInCreation(beanName)) {  
          88.         throw new BeanCurrentlyInCreationException(  
          89.                 beanName, "FactoryBean which is currently in creation returned null from getObject");  
          90.     }  
          91.   
          92.     if (object != null && shouldPostProcess) {  
          93.         try {  
          94.             object = postProcessObjectFromFactoryBean(object, beanName);  
          95.         }  
          96.         catch (Throwable ex) {  
          97.             throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);  
          98.         }  
          99.     }  
          100.   
          101.     return object;  
          102. }  

               這里返回的已經是作為工廠的 FactoryBean 生產的產品,并不是 FactoryBean 本身。這種FactoryBean的機制可以為我們提供一個很好的封裝機制,比如封裝Proxy、RMI、JNDI等。經過對FactoryBean實現過程的原理分析,相信讀者會對getObject方法有很深刻的印象。這個方法就是主要的FactoryBean 的接口,需要實現特定的工廠的生產過程,至于這個生產過程是怎樣和IoC容器整合的,就是我們在上面分析的內容。

           

          那么返回的類型是怎么確定為javax.sql.DataSource類型的呢?回頭再看在context.xml中的數據源配置可以看到:

           

          Xml代碼  收藏代碼
          1. type="javax.sql.DataSource"  

          這樣一句。然后在去細看JndiObjectFactoryBean類中的afterPropertiesSet方法的具體代碼所以一切都明了了。

           

          綜上所述,這里主要還是要對Spring的FactoryBean模式的理解最為重要。

          posted on 2013-12-31 14:28 午后星期午 閱讀(370) 評論(0)  編輯  收藏 所屬分類: JavaEE

          主站蜘蛛池模板: 苗栗市| 钟山县| 太和县| 鹿泉市| 通州市| 文山县| 西城区| 柯坪县| 宁波市| 长治市| 许昌市| 鹿邑县| 台北市| 沁水县| 久治县| 太仆寺旗| 科技| 莫力| 乌什县| 淅川县| 威宁| 阳江市| 固阳县| 凤台县| 吉隆县| 商水县| 梁河县| 岱山县| 江孜县| 白银市| 固安县| 昂仁县| 巩义市| 若尔盖县| 如东县| 阳春市| 嵩明县| 镇宁| 广灵县| 双鸭山市| 江阴市|