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

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

    博客分类:
  • j2ee
阅读更多
大家平日使用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对象
分享到:
评论
5 楼 firefly1314 2010-01-16  
提出问题->分析问题->解决问题
lz步步深入,分析的很透彻。向lz学习,不浮于问题表面。

最近对spring的注入一头雾水,还需深入,继续学习ing~~~~
4 楼 haokong 2010-01-16  
有点意思,学习了,lz再接再厉啊。
看来什么都要追根问底啊。
3 楼 shanfeng1 2009-11-11  
看来咱层次还没上来
2 楼 xiaogu2008 2009-10-09  
看了我不困
1 楼 xiaogu2008 2009-10-09  
分析的相当精彩。

相关推荐

    初探spring aop内部实现 java

    本篇文章将深入探讨Spring AOP的内部实现,以及如何通过源代码理解其DataSource实现和FactoryBean模式。 首先,让我们了解AOP的基本概念。AOP的核心思想是“切面”,它封装了特定的关注点,如日志记录、事务管理、...

    Spring源代码解析(八):Spring驱动Hibernate的实现.doc

    总的来说,Spring通过`LocalSessionFactoryBean`实现对Hibernate的驱动,它将数据源、事务管理和配置信息集成在一起,创建出适应Spring管理的`SessionFactory`。这种集成方式使得开发者无需过多关注底层细节,可以...

    SpringBoot整合mybatis-plus实现多数据源的动态切换且支持分页查询.pdf

    在SpringBoot项目中,整合Mybatis-Plus并实现多数据源的动态切换,同时支持分页查询是一项常见的需求。以下将详细阐述这个过程中的关键步骤和技术要点。 首先,我们需要引入必要的Maven依赖。这里提到了四个关键...

    spring boot 整合mybtis druid 多数据源 源代码。

    在IT行业中,Spring Boot、MyBatis和Druid都是非常重要的技术组件,它们分别在微服务开发、数据库操作...通过这个源代码,你可以学习到如何在Spring Boot中灵活地管理和切换数据源,为不同的业务场景提供数据访问支持。

    springboot+mybatis多数据源配置下载

    5. **使用多数据源**:在业务代码中,通过`@Autowired`和`@Qualifier`注解来指定使用哪个数据源。例如: ```java @Service public class UserService { @Autowired @Qualifier("primary") private ...

    SpringBoot + mybatis-plus + druid 实现mySql与Orcl双数据源

    总结起来,通过SpringBoot、Mybatis-Plus和Druid,我们可以方便地实现双数据源配置,使得应用能同时处理MySQL和Oracle数据库的数据。在实际开发中,要根据业务场景灵活切换数据源,确保数据操作的正确性和效率。同时...

    springboot mybatis 集成多数据源 两种实现方式

    在Spring Boot应用中集成MyBatis并实现多数据源,是一种常见的需求,特别是在大型系统中,可能需要连接多个数据库以实现数据隔离或者读写分离。本文将详细介绍两种不同的实现方式,分别为静态添加和动态添加数据源。...

    Spring Boot 关系型数据库多数据源

    3. **配置数据源**:使用`@Configuration`和`@EnableJpaRepositories`注解来定义两个不同的`DataSource`,分别对应主库和从库。同时,使用`@Primary`标记主数据源。 ```java @Configuration public class ...

    Springboot+mybatis+druid多数据源配置(oracle+mybatis)

    本篇将详细讲解如何在Spring Boot项目中结合Mybatis和Druid实现多数据源配置,以支持Oracle和MySQL两种数据库,并进行数据库的分离调试。 首先,我们要理解Spring Boot的自动配置机制。Spring Boot通过扫描`@...

    springboot_mybatis 双数据源配置代码

    本示例项目"springboot_mybatis 双数据源配置代码"提供了一个完整的解决方案,展示了如何在Spring Boot和MyBatis框架下配置和管理两个不同的数据源。下面将详细介绍这个项目的实现细节和相关的技术知识点。 首先,...

    spring boot配置多数据源

    为了在业务代码中切换数据源,我们需要创建一个`AbstractRoutingDataSource`的实现类,它可以根据特定的条件选择要使用的数据源。 ```java @Configuration public class DynamicDataSource extends ...

    ibatis-spring学习

    本文将详细介绍如何在项目中整合iBatis与Spring,并通过一个学习程序的源代码分析其实现原理。 首先,理解iBatis的核心概念。iBatis将SQL语句与Java代码分离,通过XML或注解定义SQL映射文件,实现了SQL与代码的解耦...

    Springboot实现mybatis多数据源配置

    本篇文章将深入探讨如何在Spring Boot项目中实现MyBatis的多数据源配置。 首先,我们要明白什么是多数据源。多数据源是指在一个应用程序中,可以同时连接并操作多个不同的数据库。这种需求通常出现在大型系统中,...

    start-spring-boot.rar

    本篇文章将详细讲解如何在Spring Boot项目中整合Mybatis和Druid,实现多数据源的配置和管理。 首先,我们来看“start-spring-boot”项目的核心概念。Spring Boot简化了Spring的初始化和配置,使得开发者能够快速...

    Spring Boot中整合MyBatis

    Spring Boot以其自动化配置和简洁的编程模型而受到欢迎,而MyBatis则是一款优秀的持久层框架,它允许开发者直接编写SQL,实现了SQL与Java代码的深度融合。下面将详细介绍在Spring Boot中配置MyBatis以及实现多数据源...

    Spring Boot多数据源配置

    接下来,我们需要在`@ConfigurationProperties`的帮助下,将数据源配置的属性值从`application.properties`或`application.yml`文件中读取,以提高代码的可维护性。例如: ```properties # application.properties ...

    springboot +mybatis+oracle 配置多个数据源,配置两个数据库信息

    以上就是Spring Boot中结合MyBatis和Oracle配置双数据源的基本步骤。在实际开发中,还需要考虑事务管理、读写分离、路由策略等问题,但这里主要介绍了基础配置。通过这种方式,我们可以灵活地管理和操作多个数据库,...

    Springboot mybatis多数据源配置项目实例

    这个项目实例 `spring-boot-mybatis-mulidatasource` 可能包含了完整的代码结构,包括配置文件、数据源配置、MyBatis 配置、Service 和 DAO 层的实现。通过学习和分析这个实例,你可以深入理解如何在 Spring Boot ...

    spring-boot-starter-mybatis-spring-boot-2.1.1.zip

    总结起来,Spring Boot 2.1.1与MyBatis的集成涉及了依赖引入、数据源配置、MyBatis配置、Mapper接口和XML映射文件的编写等多个环节。通过这种方式,我们可以轻松地利用Spring Boot的便利性和MyBatis的灵活性,实现...

    spring-boot-starter-mybatis-spring-boot-2.1.3.tar.gz

    当Spring Boot与MyBatis结合时,可以极大地提升开发效率,同时保持代码的简洁性。本文将深入探讨Spring Boot 2.1.3版本中集成MyBatis的相关知识点。 一、Spring Boot Starter MyBatis简介 Spring Boot Starter ...

Global site tag (gtag.js) - Google Analytics