5.2.2设置器注入(Setter Injection)
现在我们来详细讲解设置器注入的方法,来改造上面的例子。
改造后的WeatherService.java源代码:
package ch02.sample2;
import java.util.Date;
/**
*
http://www.javastar.org
*/
public interface WeatherService {
Double getHistoricalHigh(Date date);
}
改造后的WeatherServiceImpl.java源代码:
package ch02.sample2;
import java.util.Date;
import ch02.sample2.WeatherDao;
import ch02.sample2.WeatherData;
/**
*
http://www.javastar.org
*/
public class WeatherServiceImpl implements WeatherService {
private WeatherDao weatherDao;
public void setWeatherDao(WeatherDao weatherDao) {
this.weatherDao = weatherDao;
}
public Double getHistoricalHigh(Date date) {
WeatherData wd = weatherDao.find(date);
if (wd != null)
return new Double(wd.getHigh());
return null;
}
}
注意这几行代码,
private WeatherDao weatherDao;
public void setWeatherDao(WeatherDao weatherDao) {
this.weatherDao = weatherDao;
}
Spring容器就用setWeatherDao(WeatherDao weatherDao)来注入weatherDao属性的实例。
下面我们使用一个Spring应用程序上下文,即ClasspathXmlApplicationContext,来管理天气服务的实例,并且确保给出了一个天气DAO的实例来合作。首先,以XML的格式定义一个配置文件applicationContext.xml。
"http://www.springframework.org/dtd/spring-beans.dtd">
简 单解释一下,beans元素中定义要有容器管理的每一个Bean实例的特征。Bean元素,定义要容器管理的一个Bean的相关属性;其中id是创建的 bean实例的名称,在其他位置通过该名称来得到该bean的实例;class指明创建该实例的类,允许存在同一个类的多个实例,以不同的名字来命名。 property指明要注入的属性,name就是该属性的名称。Ref来指定要注入的类实例,local表示在当前的xml中查找并校验所引用的实 例,local可以替换为bean或者parent,范围不同,以后我们会详细讲解。这里的
其实就是指定将id为weatherDao的实例赋于weatherService的属性 weatherDao。
下面我们改造一下测试的类就可以了。
测试类WeatherServiceTest.java的源代码:
package ch02.sample2;
import java.util.GregorianCalendar;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ch02.sample2.WeatherService;
/**
*
http://www.javastar.org
*/
public class WeatherServiceTest extends TestCase {
public void testSample2() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ch02/sample2/applicationContext.xml");
WeatherService ws = (WeatherService) ctx.getBean("weatherService");
Double high = ws.getHistoricalHigh(new GregorianCalendar(2004, 0, 1).getTime());
// ... do more validation of returned value here, this test is not realistic
System.out.println("High was: " + high);
}
}
这里就像上面说的,我们使用ClassPathXmlApplicationContext来管理天气预报类的实例。
其他的类和我们在5.2.1种的代码相同。运行结果如下:
2007-2-27 0:42:27 org.springframework.core.CollectionFactory
信息: JDK 1.4+ collections available
2007-2-27 0:42:27 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [ch02/sample2/applicationContext.xml]
2007-2-27 0:42:27 org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory
信 息: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy
2007-2-27 0:42:27 org.springframework.context.support.AbstractApplicationContext refresh
信 息: 2 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]
2007-2-27 0:42:27 org.springframework.context.support.AbstractApplicationContext initMessageSource
信 息: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@176c74b]
2007-2-27 0:42:27 org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster
信 息: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@95fd19]
2007-2-27 0:42:27 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信 息: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy]
High was: 15.0
有兴趣的可以把StaticDataWeatherDaoImpl.java中的WeatherData wd = new WeatherData();改造一下,改造成设置器注入的方式。
5.2.3构造器注入
上面我们介绍了最常用的注入方法,设置器注入,下面我们开始讲解构造器注入的方法,对象的依赖为通过对象自己的构造函数来提供。
我们继续改造前面的例子。
WeatherService.java保持不变:
package ch02.sample3;
import java.util.Date;
/**
*
http://www.javastar.org
*/
public interface WeatherService {
Double getHistoricalHigh(Date date);
}
改造后的WeatherServiceImpl.java的源代码:
package ch02.sample3;
import java.util.Date;
import ch02.sample3.WeatherDao;
import ch02.sample3.WeatherData;
/**
*/
public class WeatherServiceImpl implements WeatherService {
WeatherDao weatherDao;
public WeatherServiceImpl(WeatherDao weatherDao) {
this.weatherDao = weatherDao;
}
public Double getHistoricalHigh(Date date) {
WeatherData wd = weatherDao.find(date);
if (wd != null)
return new Double(wd.getHigh());
return null;
}
}
注意这里构造器加了参数WeatherDAO,而去掉了设置器。下面我们需要修改上下文配置文件。修改后的内容如下:
"http://www.springframework.org/dtd/spring-beans.dtd">
注意这里的配置修改成了
而用设置器的时候是
配置文件的其它元素就不做重复的解释了。
我们修改了这么多,而对于调用该服务的测试类毫无影响,所以不需要修改。运行测试类,同样得到如下的结果:
2007-3-1 18:02:00 org.springframework.core.CollectionFactory
信息: JDK 1.4+ collections available
2007-3-1 18:02:00 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [ch02/sample3/applicationContext.xml]
2007-3-1 18:02:00 org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory
信 息: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy
2007-3-1 18:02:00 org.springframework.context.support.AbstractApplicationContext refresh
信 息: 2 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]
2007-3-1 18:02:00 org.springframework.context.support.AbstractApplicationContext initMessageSource
信 息: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@116471f]
2007-3-1 18:02:00 org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster
信 息: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@11b9fb1]
2007-3-1 18:02:00 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信 息: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy]
High was: 15.0
5.2.4方法注入
现在我们讲解注入的最后一种形式,也是最少使用的一种形式:方法注入,在spring中也叫查询方法注入(Lookup Method Injection)。
为了方便大家理解,先不讲理论了,我们先来看代码,这里我们需要改造的类文件还是WeatherServiceImpl.java,源代码如下:
package ch02.sample4;
import java.util.Date;
import ch02.sample4.WeatherDao;
import ch02.sample4.WeatherData;
/**
*/
public abstract class WeatherServiceImpl implements WeatherService {
protected abstract WeatherDao getWeatherDao();
public Double getHistoricalHigh(Date date) {
WeatherData wd = getWeatherDao().find(date);
if (wd != null)
return new Double(wd.getHigh());
return null;
}
}
注 意这一句protected abstract WeatherDao getWeatherDao();是个抽象方法,返回一个WeatherDao对象。还有这一句WeatherData wd = getWeatherDao().find(date);使用该抽象方法取得对象。有人会问,抽象方法没有实现怎么运行,后面我们会做解释,先不用着急。
配置文件当然也需要修改一下了,其他的文件就不需要了。修改后的配置文件内容如下:
"http://www.springframework.org/dtd/spring-beans.dtd">
class="ch02.sample4.StatefulDataWeatherDaoImpl">
注意了,这对weatherDao的声明没有太大变化,就是加了一句singleton="false",表示每次请求实例的时候返回一个新的对象实例,而不使用缓存。对于方法注入,我们主要看的是这里
和以前的property和constructor-age是不同的,而是用了lookup-method,所以我们又称方法注入为查询方法注入。
解 释一下其运行原理,我们在前面定义的抽象方法会由容器来实现方法,然后返回由容器查询得到的对象,这里就是 weatherDao,bean="weatherDao"就是指定了返回的对象。其主要用途就是处理单态、无状态对象需要使用非单态、有状态或者非线程 安全对象的时候。
这种注入方式很少使用,就不多讲了,大家有时间了可以深入研究一下。测试类修改如下:
package ch02.sample4;
import java.util.GregorianCalendar;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ch02.sample4.WeatherService;
/**
*/
public class WeatherServiceTest extends TestCase {
public void testSample4() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ch02/sample4/applicationContext.xml");
WeatherService ws = (WeatherService) ctx.getBean("weatherService");
Double high = ws.getHistoricalHigh(new GregorianCalendar(2004, 0, 1).getTime());
// ... do more validation of returned value here, this test is not realistic
System.out.println("High was: " + high);
}
public void testToShowHowAbstractClassCanBeSubclassedJustForTest() {
WeatherService ws = new WeatherServiceImpl() {
protected WeatherDao getWeatherDao() {
return null;
}
};
}
}
测试结果如下:
2007-3-1 18:57:14 org.springframework.core.CollectionFactory
信息: JDK 1.4+ collections available
2007-3-1 18:57:14 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [ch02/sample4/applicationContext.xml]
2007-3-1 18:57:15 org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory
信 息: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy
2007-3-1 18:57:15 org.springframework.context.support.AbstractApplicationContext refresh
信 息: 2 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=15006066]
2007-3-1 18:57:15 org.springframework.context.support.AbstractApplicationContext initMessageSource
信 息: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@1a626f]
2007-3-1 18:57:15 org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster
信 息: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@1ee3914]
2007-3-1 18:57:15 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信 息: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [weatherService,weatherDao]; root of BeanFactory hierarchy]
High was: 15.0
注意这里需要用到cglib库,在Spring自带的lib中有cglib- nodep-2.1_3.jar。cglib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。很多框 架中都用到了该类库,比如hibernate。不多介绍了,有兴趣的自己去查一下相关的资料了。
最后说一下注入方法的选择,其实没有什么太多的原则,基本上用什么注入方法,在类设计的时 候就已经确定了。当然,提倡使用设置器注入,因为诸如太多的话,使用构造器会使代码比较冗长,比较难操作。强制不提供set方法的当然只能通过构造器,所 谓强制,举个例子,比如一个私有属性,不希望在运行的时候被修改,肯定不会提供set方法了,否则,就容易运行期间被无意间修改掉,所以只能通过构造器在 创建对象的时候设定。不多讲了,这个和类的目标设定有很大关系了。
关于测试程序的运行,我们只需要在eclipse中新建一个java project,然后引入相关的源代码即可。只是需要在工程的libraries中加入commons-logging.jar,junit- 4.2.jar,cglib-nodep-2.1_3.jar,当然还要有spring.jar。
然后选中test类文件选择运行为junit test的项目就行了。还有问题的话就发贴问吧,不要都通过QQ问了,人太多,回答不过来。
源代码见附件了。
本文讨论和源代码下载: http://www.javastar.org/thread-117-1-1.html
更多最新内容更新: http://www.javastar.org/thread-50-1-1.html