`
kingxss
  • 浏览: 972967 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多

 

个人学习参考所用,勿喷!

 

在使用 Tomcat服务器 + SpringFramework 进行JavaEE项目的开发部署的时候可以在Tomcat的配置文件中进行JDBC数据源的配置,具体步骤如下(这里省略了工程的建立步骤):

 

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

<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中(这个文件需要自行建立):

<?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中配置配置如下内容:

<!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)建立数据库基础操作类 BaseDAOImpl

    接口代码:

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;

}

 

   实现类代码:

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;
	}

}

 

 

这里存在一个疑问:

运行如下代码:

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);
}

 

会报告如下的错误:

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

从JndiObjectFactoryBean的源码中也可以看到,JndiObjectFactoryBean的父类或所继承的接口都没有继承javax.sql.DataSource接口,所以一下的配置中:

<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源代码里一段注释: 

 

/** 
 * 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的实现细节 :

 

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特性的实现:

 

//该方法在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中的数据源配置可以看到:

 

type="javax.sql.DataSource"

这样一句。然后在去细看JndiObjectFactoryBean类中的afterPropertiesSet方法的具体代码所以一切都明了了。

 

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

 

分享到:
评论
2 楼 kingxss 2014-01-26  
peixiaofu 写道
凯子不错啊 文章保存的不错嘛

朋友,谢谢你的谬赞!
1 楼 peixiaofu 2014-01-17  
凯子不错啊 文章保存的不错嘛

相关推荐

    如何在spring中等价配置得到原本由jndi配置实现的数据源

    然而,有时我们可能希望在非Web环境下或不使用JNDI的情况下配置数据源,这时我们需要在Spring的配置文件中等价地实现JNDI的数据源配置。以下是如何在Spring中进行这种转换的详细步骤和知识点: 1. **理解JNDI配置**...

    Tomcat配置数据源连接池示例

    本示例主要关注如何在Apache Tomcat服务器中配置数据源连接池,以提高数据库访问的效率和稳定性。Tomcat作为一个流行的Java Servlet容器,支持多种数据源连接池实现,如Apache Commons DBCP、C3P0、HikariCP等。在这...

    jboss配置MySql的JNDI

    通过在JBoss中配置JNDI数据源,可以实现应用程序与数据库之间的解耦,提高系统的可维护性和扩展性。 ### 配置步骤详解 #### 步骤一:准备MySQL数据源XML文件 首先,需将包含MySQL数据源配置的`mysql-ds.xml`文件...

    Spring的数据源配置 DBCP、C3P0、JNDI.txt

    **在Spring中配置JNDI Lookup**:使用`JndiObjectFactoryBean`来查找JNDI数据源,并将其注入到Spring Bean中。 #### 五、总结 本文详细介绍了在Spring框架中配置DBCP、C3P0以及通过JNDI获取数据源的方法。对于...

    Spring配置数据源的三种方式 (dbcp,c3p0,jndi)..docx

    本文将详细介绍Spring配置数据源的三种常见方式:`DriverManagerDataSource`、`Apache Commons DBCP`(BasicDataSource)以及通过`JNDI`查找数据源。 1. **DriverManagerDataSource** `DriverManagerDataSource`是...

    Spring 数据源的灵活配置巧应用

    本文将详细介绍如何在Spring中进行数据源的灵活配置,以及如何利用这些配置实现高效的数据访问。 #### 二、数据源的作用与重要性 数据源(DataSource)是JDBC API中的一个重要组成部分,它主要负责管理与数据库的...

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    Spring提供了对多种数据源的支持,包括JDBC DataSources和Transaction Managers。通过Spring的配置,我们可以轻松地管理多个数据源,并根据业务逻辑进行动态切换。这通常涉及到使用`@Qualifier`注解来指定特定的...

    tomcat6+jndi+c3p0配置数据库连接池

    4. **在应用中引用JNDI数据源**:在Java代码中,你可以通过JNDI查找来获取数据源。例如,在Servlet或JSP中: ```java InitialContext ic = new InitialContext(); DataSource ds = (DataSource) ic.lookup("java:...

    JNDI与Tomcat连接池的例子

    本示例将详细介绍如何利用JNDI(Java Naming and Directory Interface)来配置和使用Tomcat的连接池。 首先,我们要理解JNDI的作用。JNDI是一个Java API,用于查找和绑定资源,如数据源、邮件服务器等。在Web应用中...

    tomcat6.0 配置jndi

    **二、Tomcat6.0配置JNDI数据源步骤** 1. **创建数据源配置文件**:在Tomcat的`conf/server.xml`文件中,找到`&lt;GlobalNamingResources&gt;`标签,在其中添加数据源的配置。例如,创建一个名为`jdbc/myDataSource`的...

    JAVA数据库连接池(C3P0,Druid,JNDI ,DBCP,Proxool,BoneCP)

    本文将详细介绍几个常见的Java数据库连接池实现:C3P0、Druid、JNDI、DBCP、Proxool以及BoneCP,并结合Spring+MyBatis+maven环境下的应用示例进行解析。 1. C3P0: C3P0是一个开源的JDBC连接池,它实现了数据源和...

    Spring数据源配置

    C3P0 类包位于 `&lt;spring_home&gt;/lib/c3p0-0.9.x.jar`,C3P0 是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。下面是如何使用 C3P0 配置数据源的示例: xml 代码 Xml ...

    spring + c3p0 连接池

    本示例将深入探讨如何在Spring项目中配置并使用C3P0连接池,以实现高效、稳定的数据库连接管理。 **一、Spring框架简介** Spring是一个开源的Java平台,它简化了企业级应用的开发。Spring的核心特性包括依赖注入...

    SpringLDAP和JNDI的增删改查

    导入该项目后,你可以编译并运行,观察Spring LDAP和JNDI如何实现对LDAP目录的CRUD操作。 ### 结语 理解并熟练使用Spring LDAP和JNDI对于进行企业级的目录服务开发至关重要。通过这个入门级的例子,你应该能够了解...

    Tomcat JDNI数据源配置详解

    ### Tomcat JNDI数据源配置详解 #### 一、引言 在现代Web应用开发中,数据库连接管理是一项至关重要的任务。为了提高应用程序的性能和可维护性,通常会采用连接池技术来管理和复用数据库连接。Apache Tomcat作为一...

    spring proxool配置资料

    Spring Proxool是Spring框架中一个非常重要的组件,它提供了对proxool数据库连接池的集成。Proxool是一个轻量级、高效的数据库连接池,它允许应用程序在处理大量并发请求时有效地管理数据库连接,从而提高系统的性能...

    java数据源配置

    同时,Spring框架提供了对数据源的高级管理,可以方便地在XML或Java配置中声明数据源,并自动注入到bean中使用。 总的来说,Java数据源配置是一项基础但重要的工作,合理的配置能有效提升系统的性能和稳定性。在...

    连接池操作Java类.rar

    在Java编程中,连接池是一种优化数据库访问性能的技术。它通过复用已建立的数据库连接,避免了频繁创建和关闭...理解连接池的工作原理,选择合适的连接池实现,并对其进行适当配置和优化,是每个Java开发者的必备技能。

    spring_c3p0_配置

    C3P0是一个开放源代码的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0能够通过提供细粒度的控制来提高性能和可靠性,例如可以配置最小和最大连接数、空闲时间等参数。 #### 三、...

    hibernate 数据源配置文件

    通过上述配置,可以在应用中通过JNDI查找获取到相应的数据源对象,从而实现数据库连接的管理。 #### 四、总结 在实际开发过程中,根据项目需求的不同,可以选择不同的数据源配置方案。使用Properties文件的方式...

Global site tag (gtag.js) - Google Analytics