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

http://cuishen.iteye.com/blog/430735

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

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

Xml代码 复制代码  收藏代码
  1. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
  2.     <property name="driverClassName"><value>oracle.jdbc.OracleDriver</value></property>  
  3.     <property name="url"><value>jdbc:oracle:thin:@caij-b815c8aab6:1521:cui</value></property>  
  4.     <property name="username"><value>cuishen</value></property>  
  5.     <property name="password"><value>cuishen</value></property>  
  6. </bean>  
  7. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  8.     <property name="mappingResources"><list>  
  9.          <value>com/cuishen/testDao/pojo/Test.hbm.xml</value>  
  10.     </list></property>  
  11.     <property name="hibernateProperties"><props>  
  12.         <prop key="dialect">org.hibernate.dialect.Oracle9Dialect</prop>  
  13.         <prop key="connection.autocommit">true</prop>  
  14.     </props></property>  
  15.     <property name="dataSource"><ref local="dataSource"/></property>  
  16. </bean>  
  17. <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  18.     <property name="sessionFactory"><ref local="sessionFactory"/></property>  
  19. </bean>  
  20. <bean id="dao" class="com.conserv.dao.impl.HibernateDaoImpl" init-method="init" destroy-method="destroy">  
  21.     <property name="transactionManager"><ref local="txManager"/></property>  
  22.     <property name="dialect"><value>Oracle9</value></property>  
  23. </bean>  


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

Xml代码 复制代码  收藏代码
  1. <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">      
  2. ;property name="jndiName" value="cs" />      
  3. </bean>  


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

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

Java代码 复制代码  收藏代码
  1. public class DriverManagerDataSource extends AbstractDataSource  
public class DriverManagerDataSource extends AbstractDataSource


再看看这个AbstractDataSource:

Java代码 复制代码  收藏代码
  1. public abstract class AbstractDataSource implements javax.sql.DataSource  
public abstract class AbstractDataSource implements javax.sql.DataSource


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

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

Java代码 复制代码  收藏代码
  1. return DriverManager.getConnection(url, props);  
return DriverManager.getConnection(url, props);


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

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

Java代码 复制代码  收藏代码
  1. public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean  
public class JndiObjectFactoryBean extends JndiObjectLocator implements FactoryBean


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

Java代码 复制代码  收藏代码
  1. /**  
  2.  * Interface to be implemented by objects used within a BeanFactory  
  3.  * that are themselves factories. If a bean implements this interface,  
  4.  * it is used as a factory, not directly as a bean.  
  5.  *  
  6.  * <p><b>NB: A bean that implements this interface cannot be used  
  7.  * as a normal bean.</b> A FactoryBean is defined in a bean style,  
  8.  * but the object exposed for bean references is always the object  
  9.  * that it creates.  
  10.  */  
/**
 * 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的实现细节

Java代码 复制代码  收藏代码
  1. private Object jndiObject;   
  2. /**  
  3.  * Look up the JNDI object and store it.  
  4.  * 广义上说是造对象的过程,就本例而言,是通过JNDI获得DataSource对象  
  5.  */  
  6. public void afterPropertiesSet() throws IllegalArgumentException, NamingException {   
  7.     super.afterPropertiesSet();   
  8.   
  9.     if (this.proxyInterface != null) {   
  10.         if (this.defaultObject != null) {   
  11.             throw new IllegalArgumentException(   
  12.                     "'defaultObject' is not supported in combination with 'proxyInterface'");   
  13.         }   
  14.         // We need a proxy and a JndiObjectTargetSource.   
  15.         this.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);   
  16.     }   
  17.   
  18.     else {   
  19.         if (!this.lookupOnStartup || !this.cache) {   
  20.             throw new IllegalArgumentException(   
  21.                 "Cannot deactivate 'lookupOnStartup' or 'cache' without specifying a 'proxyInterface'");   
  22.         }   
  23.         if (this.defaultObject != null && getExpectedType() != null &&   
  24.                 !getExpectedType().isInstance(this.defaultObject)) {   
  25.             throw new IllegalArgumentException("Default object [" + this.defaultObject +   
  26.                     "] of type [" + this.defaultObject.getClass().getName() +   
  27.                     "] is not of expected type [" + getExpectedType().getName() + "]");   
  28.         }   
  29.         // Locate specified JNDI object.   
  30.         this.jndiObject = lookupWithFallback();   
  31.     }   
  32. }   
  33. /**  
  34.  * Return the singleton JNDI object.  
  35.  * 返回JNDI对象(DataSource对象)  
  36.  */  
  37. public Object getObject() {   
  38.     return this.jndiObject;   
  39. }   
  40.   
  41. public Class getObjectType() {   
  42.     if (this.proxyInterface != null) {   
  43.         return this.proxyInterface;   
  44.     }   
  45.     else if (this.jndiObject != null) {   
  46.         return this.jndiObject.getClass();   
  47.     }   
  48.     else {   
  49.         return getExpectedType();   
  50.     }   
  51. }  
	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对象

 

 

 

 

 

 

 

=========http://blog.csdn.net/turkeyzhou/article/details/3139258

在此之前,我已经接触到了DataSource和JTA事务了,我们都应用到了JNDI技术;在Spring中提供了JNDI技术的整合支持,JNDI如何使用,我就不累赘了,具体我们来看一下整合的源代码:

代码类结构:



我们从上而下进行分析;
首先是:

JndiTemplate:

jndiTemplate提供了对JNDI服务器的vjndi对象的绑定,查询,撤销绑定和重新绑定,从一定意义上来说他跟JdbcTemplate等是同意概念的类,直接与资源管理器进行交互,并且同样的是采用的回调机制,我们可以看见其依赖了JndiCallbake接口:

其对绑定,差性能,撤销,重新绑定的实现的源代码如下:

  1. public Object execute(JndiCallback contextCallback) throws NamingException {
  2.         Context ctx = createInitialContext();
  3.         try {
  4.             return contextCallback.doInContext(ctx);
  5.         }
  6.         finally {
  7.             try {
  8.                 ctx.close();
  9.             }
  10.             catch (NamingException ex) {
  11.                 logger.debug("Could not close JNDI InitialContext", ex);
  12.             }
  13.         }
  14.     }
  1.     protected Context createInitialContext() throws NamingException {
  2.         return new InitialContext(getEnvironment());
  3.     }



这段代码生成了上下文,进而把上下文传递给了JndiCallBack,在这个接口的回调方法,我们直接使用context对资源管理器进行curd交互;

绑定对象:

  1. public Object lookup(final String name) throws NamingException {
  2.         if (logger.isDebugEnabled()) {
  3.             logger.debug("Looking up JNDI object with name [" + name + "]");
  4.         }
  5.         return execute(new JndiCallback() {
  6.             public Object doInContext(Context ctx) throws NamingException {
  7.                 Object located = ctx.lookup(name);
  8.                 if (located == null) {
  9.                     throw new NameNotFoundException(
  10.                             "JNDI object with [" + name + "] not found: JNDI implementation 
  11. returned null");
  12.                 }
  13.                 return located;
  14.             }
  15.         });
  16.     }
  1.     public void bind(final String name, final Object object) throws NamingException {
  2.         if (logger.isDebugEnabled()) {
  3.             logger.debug("Binding JNDI object with name [" + name + "]");
  4.         }
  5.         execute(new JndiCallback() {
  6.             public Object doInContext(Context ctx) throws NamingException {
  7.                 ctx.bind(name, object);
  8.                 return null;
  9.             }
  10.         });
  11.     }

重新绑定:

  1. public void rebind(final String name, final Object object) throws NamingException {
  2.         if (logger.isDebugEnabled()) {
  3.             logger.debug("Rebinding JNDI object with name [" + name + "]");
  4.         }
  5.         execute(new JndiCallback() {
  6.             public Object doInContext(Context ctx) throws NamingException {
  7.                 ctx.rebind(name, object);
  8.                 return null;
  9.             }
  10.         });
  11.     }


撤销绑定:

  1. public void unbind(final String name) throws NamingException {
  2.         if (logger.isDebugEnabled()) {
  3.             logger.debug("Unbinding JNDI object with name [" + name + "]");
  4.         }
  5.         execute(new JndiCallback() {
  6.             public Object doInContext(Context ctx) throws NamingException {
  7.                 ctx.unbind(name);
  8.                 return null;
  9.             }
  10.         });
  11.     }
  12.     
  13. }

通过JndiTemplate,我们实现了底层访问代码;形成了具体实现层;与上层开来;

JndiAccessor:只是单纯的对JndiTemplate进行了包装,隔离了底层的实现细节;

JndiLocatorSupport的主要扩展的功能是,如果我们在配置JndiObjectFactoryBean的时候,配置了参数:resourceRef为true的话,那么就会如果我们查找的资源中间不包含:或者java:等前缀,我们会自动的会该路径加上java:comp/env,主要是匹配在j2ee容器里面的jndi资源的查找;

  1.     protected String convertJndiName(String jndiName) {
  2.         // Prepend container prefix if not already specified and no other scheme given.
  3.         if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) {
  4.             jndiName = CONTAINER_PREFIX + jndiName;
  5.         }
  6.         return jndiName;
  7.     }

最后,我们再来看一下:

在JndiObjectFactoryBean我们实际上得到的是:

   jndiObject :
初始化生成如下:

  1. public void afterPropertiesSet() throws IllegalArgumentException, NamingException {
  2.         super.afterPropertiesSet();
  3.         if (this.proxyInterface != null) {
  4.             if (this.defaultObject != null) {
  5.                 throw new IllegalArgumentException(
  6.                         "'defaultObject' is not supported in combination with 'proxyInterface'");
  7.             }
  8.             // We need a proxy and a JndiObjectTargetSource.
  9.             this.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);
  10.         }
  11.         else {
  12.             if (!this.lookupOnStartup || !this.cache) {
  13.                 throw new IllegalArgumentException(
  14.                     "Cannot deactivate 'lookupOnStartup' or 'cache' without specifying a 'proxyInterface'");
  15.             }
  16.             if (this.defaultObject != null && getExpectedType() != null &&
  17.                     !getExpectedType().isInstance(this.defaultObject)) {
  18.                 throw new IllegalArgumentException("Default object [" + this.defaultObject +
  19.                         "] of type [" + this.defaultObject.getClass().getName() +
  20.                         "] is not of expected type [" + getExpectedType().getName() + "]");
  21.             }
  22.             // Locate specified JNDI object.
  23.             this.jndiObject = lookupWithFallback();
  24.         }
  25.     }


当proxyTnterface不为空的时候
this
.jndiObject = JndiObjectProxyFactory.createJndiObjectProxy(this);
生成了一个从Jndi目录中取得了的对象的代理类:

  1. private static class JndiObjectProxyFactory {
  2.         private static Object createJndiObjectProxy(JndiObjectFactoryBean jof) throws NamingException {
  3.             // Create a JndiObjectTargetSource that mirrors the JndiObjectFactoryBean's configuration.
  4.             JndiObjectTargetSource targetSource = new JndiObjectTargetSource();
  5.             targetSource.setJndiTemplate(jof.getJndiTemplate());
  6.             targetSource.setJndiName(jof.getJndiName());
  7.             targetSource.setExpectedType(jof.getExpectedType());
  8.             targetSource.setResourceRef(jof.isResourceRef());
  9.             targetSource.setLookupOnStartup(jof.lookupOnStartup);
  10.             targetSource.setCache(jof.cache);
  11.             targetSource.afterPropertiesSet();
  12.             // Create a proxy with JndiObjectFactoryBean's proxy interface and the JndiObjectTargetSource.
  13.             ProxyFactory proxyFactory = new ProxyFactory();
  14.             proxyFactory.addInterface(jof.proxyInterface);
  15.             proxyFactory.setTargetSource(targetSource);
  16.             return proxyFactory.getProxy();
  17.         }
  18.     }

否则直接将从目录服务器中得到的对象暴露出来;如果查找出现异常,将会把DefaultObject暴露出来;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值