Spring的JNDI数据源连接池配置示例及Spring对JNDI实现分析
http://kingxss.iteye.com/blog/1481872
http://blog.csdn.net/samjustin1/article/details/52265796
http://blog.csdn.net/dyllove98/article/details/7706218
Spring jdbcTemplate JNDI数据源 数据源连接池 FactoryBean模式 源码分析
个人学习参考所用,勿喷!
在使用 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中配置配置如下内容:
>
< beans >
< bean id ="dataSource" class ="org.springframework.jndi.JndiObjectFactoryBean" >
< property name ="jndiName" >
< value > java:comp/env/jdbc/demoDBvalue >
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> 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> 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/portalDataServicevalue >
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源代码里一段注释:
翻译过来是说:所有实现FactoryBean接口的类都被当作工厂来使用,而不是简单的直接当作bean来使用,FactoryBean实现类里定义了要生产的对象,并且由FactoryBean实现类来造该对象的实例,看到这里聪明的大概已经能猜出个八九不离十了吧,我们回过头来看看JndiObjectFactoryBean的实现细节 :
private Object jndiObject;
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'" );
}
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() + "]" );
}
this .jndiObject = lookupWithFallback();
}
}
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特性的实现:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null ;
if (mbd == null ) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null ) {
FactoryBean> factory = (FactoryBean>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
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);
}
}
private Object doGetObjectFromFactoryBean(
final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null ) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged(new PrivilegedExceptionAction() {
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);
}
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模式的理解最为重要。