博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring的JNDI数据源连接池配置示例及Spring对JNDI实现分析
阅读量:7017 次
发布时间:2019-06-28

本文共 11464 字,大约阅读时间需要 38 分钟。

hot3.png

在使用 Tomcat服务器 + SpringFramework 进行JavaEE项目的开发部署的时候可以在Tomcat的配置文件中进行JDBC数据源的配置,具体步骤如下(这里省略了工程的建立步骤):
1) 添加如下代码到tomcat的conf目录下的server.xml中:
Xml代码  收藏代码
完成上述步骤数据源的连接池配置已经完成,但是为了提高项目的可移植性,最好将上述第二步的内容放入到工程的META-INF目录的context.xml中(这个文件需要自行建立):
Xml代码  收藏代码
2)在Spring的配置文件,如applicationContext.xml中配置配置如下内容:
Xml代码  收藏代码
  
java:comp/env/jdbc/demoDB
3)建立数据库基础操作类 BaseDAOImpl
    接口代码:
Java代码  收藏代码
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; }
   实现类代码:
Java代码  收藏代码
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; } }
这里存在一个疑问:
运行如下代码:
Java代码  收藏代码
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);  }
会报告如下的错误:
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代码  收藏代码
java:comp/env/jdbc/portalDataService
对org.springframework.jdbc.core.JdbcTemplate的dataSource属性的注入为何能够成功?
带着这样的疑问去iteye中提问,没有得到详细的解答,但是iteye的提示功能似乎很不错,在问题的下方给出了相关内容参考提示,进入到《从源代码解读spring之DataSource实现和FactoryBean模式》这个帖子中,看完以后大受启发。一下是从这篇帖子摘抄出来的内容:
再看源码后发现,JndiObjectFactoryBean实现了FactoryBean接口,下面是org.springframework.beans.factory.FactoryBean源代码里一段注释:
Java代码  收藏代码
/**   * 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.   *   * 

NB: A bean that implements this interface cannot be used * as a normal bean. 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的实现细节 :
Java代码  收藏代码
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() { 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中的数据源配置可以看到:
Xml代码  收藏代码
type="javax.sql.DataSource" 
这样一句。然后在去细看JndiObjectFactoryBean类中的afterPropertiesSet方法的具体代码所以一切都明了了。
综上所述,这里主要还是要对Spring的FactoryBean模式的理解最为重要。

转载于:https://my.oschina.net/zhangmaoyuan/blog/1606260

你可能感兴趣的文章
数据库原理整理笔记1
查看>>
如果选错云服务商,后果很严重……
查看>>
c#备份MySQL数据库 --转载
查看>>
HDU - 1247 Hat’s Words 字典树
查看>>
从client(content="<p></p>")中检測到有潜在危急的 Request.Form 值。
查看>>
《Effective C++》笔记:I
查看>>
C语言 指针和指针变量
查看>>
经典类与新式类的区别
查看>>
JavaMail收发邮件的步骤
查看>>
树莓派练习程序(寻迹模块)
查看>>
点击弹出居中带有透明遮罩层窗口
查看>>
C语言程序设计第四次作业——选择结构(2)
查看>>
安装Tomcat提示Failed to install Tomcat6 service...的解决办法
查看>>
17.08.07
查看>>
C#语法糖(Csharp Syntactic sugar)语法技巧
查看>>
Farey sequences
查看>>
Django错误集
查看>>
C# Microsoft.Office不存在空间名称Interop和Excel
查看>>
Material Design 设计规范总结(1)
查看>>
Nginx性能测试(未优化篇)
查看>>