从源代码解读spring之DataSource实现和FactoryBean模式

本文探讨了Spring框架如何整合Hibernate,重点介绍了两种DataSource配置方式:基于JDBC和JNDI的配置。并深入分析了这两种配置方式的具体实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家平日使用spring + hibernate做项目的时候大概都接触过下面的spring配置代码:

下面是使用普通的jdbc驱动获得DataSource的配置


<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>oracle.jdbc.OracleDriver</value></property>
<property name="url"><value>jdbc:oracle:thin:@caij-b815c8aab6:1521:cui</value></property>
<property name="username"><value>cuishen</value></property>
<property name="password"><value>cuishen</value></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources"><list>
<value>com/cuishen/testDao/pojo/Test.hbm.xml</value>
</list></property>
<property name="hibernateProperties"><props>
<prop key="dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="connection.autocommit">true</prop>
</props></property>
<property name="dataSource"><ref local="dataSource"/></property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="dao" class="com.conserv.dao.impl.HibernateDaoImpl" init-method="init" destroy-method="destroy">
<property name="transactionManager"><ref local="txManager"/></property>
<property name="dialect"><value>Oracle9</value></property>
</bean>


下面是通过JNDI获得的DataSource的配置,只要将上面的id为"dataSource"的bean换成下面的配置就行了


<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="cs" />
</bean>


配置很简单,使用也非常方便,spring毫不挑食,不管是jdbc版的DataSource也好,是JNDI版的也好,它都能接受,那这个兼容性是怎么做到的呢??现在从源代码入手来一探究竟:

1. 先看看jdbc版的DataSource - org.springframework.jdbc.datasource.DriverManagerDataSource


public class DriverManagerDataSource extends AbstractDataSource


再看看这个AbstractDataSource:


public abstract class AbstractDataSource implements javax.sql.DataSource


哈哈,原来DriverManagerDataSource是javax.sql.DataSource的实现类,那做为bean注入给sessionFactory真是无可厚非

我们再看看它内部的实现细节


return DriverManager.getConnection(url, props);


哈哈,这代码是不是再熟悉也不过啦?原来DriverManagerDataSource实现了javax.sql.DataSource接口,本质是对jdbc连接数据库的简单封装

2. 接下来看看JNDI版的DataSource - org.springframework.jndi.JndiObjectFactoryBean


public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean


追溯JndiObjectFactoryBean的父类和实现的接口以及父类的父类,都和javax.sql.DataSource接口八竿子打不着,没有一点点渊源,oh,my God! 这怎么可能!?完全不相干的对象怎么能够被注入?这完全有悖java的精神!但事实摆在眼前,测试是完全通过的!静下心来,我注意到了JndiObjectFactoryBean实现了FactoryBean接口,一直以来脑子里对FactoryBean模式感到有点模糊,不能完全领会其本质,难道真的是这里面有文章??好,借此机会,好好研究下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里面,所以并没有违背java的精神!

有兴趣也可以看看org.springframework.orm.hibernate3.LocalSessionFactoryBean,它也实现了FactoryBean接口,内部实现如出一辙,只不过它担负的重任不是造JNDI object,而是要造SessionFactory对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值