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代码  收藏代码

1public 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代码  收藏代码

1public 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代码  收藏代码

1public 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代码  收藏代码

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代码  收藏代码

1private Object jndiObject; 2/** 3 * Look up the JNDI object and store it. 4 * 广义上说是造对象的过程,就本例而言,是通过JNDI获得DataSource对象 5 */ 6public 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 */ 37public Object getObject() { 38 return this.jndiObject; 39} 40 41public 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特性的实现:

1//该方法在org.springframework.beans.factory.support.AbstractBeanFactory类中 2protected 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类中 37protected 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类中 54private 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代码  收藏代码
type="javax.sql.DataSource" 
这样一句。然后在去细看JndiObjectFactoryBean类中的afterPropertiesSet方法的具体代码所以一切都明了了。

综上所述,这里主要还是要对Spring的FactoryBean模式的理解最为重要。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Spring的JNDI数据源连接池配置示例及Spring对JNDI实现分析 - HelloWorld