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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

在使用 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模式的理解最为重要。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
28 0
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
46 4
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
27天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
19天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
30 1
|
1月前
|
Java BI 调度
Java Spring的定时任务的配置和使用
遵循上述步骤,你就可以在Spring应用中轻松地配置和使用定时任务,满足各种定时处理需求。
131 1
|
1月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
47 0
|
Java 关系型数据库 数据库
|
Java 数据库连接 Spring
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。