《spring 2.0 in action》点滴 (Chapter 03)
来源:互联网 发布:软件水平考试 编辑:程序博客网 时间:2024/05/15 05:18
Declaring parent and child beans
对于同一个class,如果你需要定义多个bean,那么你可以尝试使用parent and child bean。
对于parent and child bean,spring为<bean> element提供了2个属性:
Parent— 指定the id of a <bean> which is 作为parent bean。
Abstract— 如果设置为true,表示该<bean>的定义是abstract的,spring将不会实例化该bean。
例子1:
旧的代码:
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
<bean id="david" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument" ref="saxophone" />
</bean>
上面的代码可以看到对于Instrumentalist class定义了两个bean,这两个bean除了id不同外,其他都一样。如果我们要定义50个bean,可以想象下。这时我们可以使用parent bean:
<bean id="baseSaxophonist"
class="com.springinaction.springidol.Instrumentalist"
abstract="true">
<property name="instrument" ref="saxophone" />
<property name="song" value="Jingle Bells" />
</bean>
<bean id="kenny" parent="baseSaxophonist" />
<bean id="david" parent="baseSaxophonist" />
上面的代码把baseSaxophonist bean的abstract属性设置为true,则spring不会对其实例化。Kenny and david bean则继承baseSaxophonist bean。
请注意:parent bean并不一定要定义成abstract!
Overriding inherited properties
child bean还可以覆盖继承于parent bean的properties,如:
例子2:
<bean id="baseSaxophonist"
class="com.springinaction.springidol.Instrumentalist"
abstract="true">
<property name="instrument" ref="saxophone" />
<property name="song" value="Jingle Bells" />
</bean>
<bean id="frank" parent="baseSaxophonist">
<property name="song" value="Mary had a little lamb" />
</bean>
Abstracting common properties
这个功能通常用在不同class的bean使用同一个property的情况下,这样就可以对这个property专门定义一个bean
例子3:
旧的代码:
<bean id="taylor"
class="com.springinaction.springidol.Vocalist">
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
<bean id="stevie"
class="com.springinaction.springidol.Instrumentalist">
<property name="instrument" ref="guitar" />
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
上面的代码定义了不同的class的两个bean,但它们都使用了同一个property: song。那么我们就可以把共同的property抽出来作为一个bean定义(注意:这个bean并不对应任何的class,即没有class属性):
<bean id="basePerformer" abstract="true">
<property name="song" value="Somewhere Over the Rainbow" />
</bean>
<bean id="taylor"
class="com.springinaction.springidol.Vocalist"
parent="basePerformer" />
<bean id="stevie"
class="com.springinaction.springidol.Instrumentalist"
parent="basePerformer">
<property name="instrument" ref="guitar" />
</bean>
Applying method injection
Java并没有提供下列功能:add new methods to any class at runtime, without changing the class’s definition。但Ruby则提供了这个功能。举个例子,你想在ruby string class里添加一个new method,那么你只需要象下面这样定义(而不需要修改本身的string class):
class String
def print_length
puts "This string is #{self.length} characters long"
end
end
定义好后,你就可以使用了,如:
message = "Hello"
message.print_length
但Java里没有这功能。Spring提供了method injection功能,使你能够在runtime期间把method注入到class里
Spring提供了2种方式的method injection:
1.Method replacement—Enables existing methods (abstract or concrete) to be replaced at runtime with new implementations (这种方式是for “返回值不是在spring里有bean定义类型”的方法).
2.Getter injection—Enables existing methods (abstract or concrete) to be replaced at runtime with a new implementation that returns a specific bean from the Spring context. (这种方式是for “使用get开头的,而且返回值是在spring里有bean定义类型”的方法)
method replacement
旧的代码
public class MagicBoxImpl implements MagicBox {
public MagicBoxImpl() {}
public String getContents() {
return "A beautiful assistant";
}
}
<bean id="magicBox" class="com.springinaction.springidol.MagicBoxImpl" />
如果你想在runtime期间修改getContents方法的话,就可以使用spring method replacement:
<bean id="magicBox"
class="com.springinaction.springidol.MagicBoxImpl">
<replaced-method name="getContents" replacer="tigerReplacer" />
</bean>
<bean id="tigerReplacer" class="com.springinaction.springidol.TigerReplacer" />
import org.springframework.beans.factory.support.MethodReplacer;
public class TigerReplacer implements MethodReplacer {
public Object reimplement(Object target, Method method,
Object[] args) throws Throwable {
return "A ferocious tiger";
}
}
注意:被替代的method也可以是一个抽象方法!
Getter injection
public abstract class Instrumentalist implements Performer {
public void perform() throws PerformanceException {
getInstrument().play();
}
public abstract Instrument getInstrument();
}
上面的getInstrument方法就是一个符合getter injection的典型。那么我们要如何使用呢:
假设有一个实现了Instrument的Guitar class,你希望在runtime期间上面的getInstrument方法返回的是一个Guitar对象,那么你就可以使用下列配置:
<bean id="stevie"
class="com.springinaction.springidol.Instrumentalist">
<lookup-method name="getInstrument" bean="guitar" />
</bean>
当然还需要有guitar bean的定义:
<bean id="guitar" class="com.springinaction.springidol.Guitar"
scope="prototype" />
注意我们把scope设置为prototype是为了使每次调用getInstrument方法时都返回不同的Guitar对象
Injecting non-Spring beans
我们知道spring对configure到spring container的所有bean进行创建、管理。但是,对于在spring里有定义bean,但又不是通过spring来创建的class,又应该如何使用spring呢?这种情况是可能的:例如,一个由ORM创建的object则不是spring创建的。
例子:
Instrumentalist class
public abstract class Instrumentalist implements Performer {
public Instrumentalist() {}
public void perform() throws PerformanceException {
instrument.play();
}
private Instrumentinstrument;
public void setInstrument(Instrumentinstrument) {
this. instrument = instrument;
}
}
假设Instrumentalist class在spring里定义了一个bean,但我们create对象outside of spring:
Instrumentalist pianist = new Instrumentalist();
pianist.perform();
在执行perform方法时,会抛出NullPointerException,因为instrument变量为null。
当然,我们可以用代码为对象注入property:
Piano piano = new Piano();
pianist.setInstrument(piano);
pianist.perform();
这是可行的,但有些情况下,并不允许你通过代码对其进行注入。例如:一个ORM创建了一个Instrumentalist对象,那么我们可能根本没有机会来为其configure its properties.
对于“在spring里有定义bean,但又不是通过spring来创建的class”这种情况,我们应该把这些在spring container外创建的对象纳入到spring里来完成DI呢?Spring 2.0提供了解决方案!
第一步:把bean定义成abstract:
<bean id="pianist"
class="com.springinaction.springidol.Instrumentalist"
abstract="true">
<property name="instrument">
<bean class="com.springinaction.springidol.Piano" />
</property>
</bean>
上面的bean定义为abstract=true,并不是为了被其他bean继承,而纯粹是为了告诉spring不要被spring实例化!
第二步:如果把上面的定义(注意上面设置的id)和class联系起来
@Configurable("pianist")
public class Instrumentalist implements Performer {
…
}
@Configurable的作用有2个:
1> 它表明Instrumentalist的实例会被纳入和配置到spring container里,即使它是在outside of spring创建。
2> 它把Instrumentalist class与id为pianist联系在一起,当spring在配置一个实例时,它会寻找pianist bean作为模板来进行配置(包括DI)
第三步:在spring configure file里添加下列代码
<aop:spring-configured />
它表示有一些在外部创建的beans,需要被配置和纳入进spring container。
请注意:<aop:spring-configured/>是用到aspectJ,这意味着你的APP必须在一个AspectJ-enabled JVM里运行。
The best way to AspectJ-enable a Java 5 JVM is to start it with the following JVM argument:
-javaagent:/path/to/aspectjweaver.jar
经过这三步,当你执行下列代码时就不会抛出异常,因为spring container会纳入对象,并进行配置(包括注入)
Instrumentalist pianist = new Instrumentalist();
pianist.perform();
Registering custom property editors
有时候我们看到有些bean的定义只需要通过赋予一个string value就可以设置一个complex property。例如,在spring 2.0 in action第九章,你会看到如何使用JaxRpcPortProxyFactoryBean定义一个web service,对它你需要设置“wsdlDocumentUrl” property,该属性的type是java.net.URL,但你对它进行设置时赋予的是string:
<property name="wsdlDocumentUrl"
value="http://www.xmethods.net/sd/BabelFishService.wsdl" />
spring会自动帮你把string value转换成URL object,它是怎么做到的呢?就是使用JavaBeans API。其中的java.beans.PropertyEditor提供了把string value转化成non string type的接口。java.beans.PropertyEditorSupport实现了该接口,它提供了2个方法:
getAsText() returns the String representation of a property’s value.
setAsText(String value) sets a bean property value from the String value passed in.
spring基于PropertyEditorSupport上,提供了一些更为具体的custom editor,见下表:
下面讲解如何扩展PropertyEditorSupport class来自定义一个你自己的custom property editor:假设你有一个Contact bean,它包含了一个PhoneNumber type的property
public Contact {
private PhoneNumber phoneNumber;
public void setPhoneNumber(PhoneNumber phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
public PhoneNumber {
private String areaCode;
private String prefix;
private String number;
public PhoneNumber() { }
public PhoneNumber(String areaCode, String prefix,
String number) {
this.areaCode = areaCode;
this.prefix = prefix;
this.number = number;
}
…
}
如果按普通的bean定义,应该是:
<bean id="infoPhone"
class="com.springinaction.chapter03.propeditor.PhoneNumber">
<constructor-arg value="888" />
<constructor-arg value="555" />
<constructor-arg value="1212" />
</bean>
<bean id="contact"
class="com.springinaction.chapter03.propeditor.Contact">
<property name="phoneNumber" ref="infoPhone" />
</bean>
上面的定义中,你必须要定义phonenumber bean才能够定义contact bean,否则无法contact bean无法得到phonenumber property注入。
如果我们使用自定义的custom PhoneEditor的话,那么就不用定义phonenumber bean啦!
public class PhoneEditor
extends java.beans.PropertyEditorSupport {
//text value example “111-111-111”
public void setAsText(String textValue) {
String areaCode = textValue.substring(0,3);
String prefix = textValue.substring(4,7);
String number = textValue.substring(8);
PhoneNumber phone = new PhoneNumber(areaCode, prefix, number);
setValue(phone);
}
}
那么在configure file里你应该这样定义:
<bean
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.springinaction.chapter03.propeditor.PhoneNumber">
<bean id="phoneEditor"
class="com.springinaction.chapter03.propeditor.PhoneEditor">
</bean>
</entry>
</map>
</property>
</bean>
<bean id="contact"
class="com.springinaction.chapter03.propeditor.Contact">
<property name="phoneNumber" value="888-555-1212" />
</bean>
这样就会使得contact bean会自动把"888-555-1212"转化成phoneNumber type property。
CustomEditorConfigurer实质上是一个BeanFactoryPostProcessor,因此它会在bean factory初始化之后调用registerCustomEditor()方法来把custom editors load进BeanFactory里。(你也可以通过代码来调用registerCustomEditor()方法来实现它)
请注意:spring自带的custom editors(例如URLEditor and LocaleEditor)default已经被register进spring container啦,不需要用CustomEditorConfigurer.来register它们。
举个例子,bean A有一个url property是java.net.URL type的,如果你的设置为:
<property name="url" value="http://www.xxx.com" />
那么spring会自动帮你把string value转化为url type!
Working with Spring’s special beans
Spring自身提供了一些特殊的beans,通过这些beans,有下列作用:
1) 可以在bean’s creation和bean factory的生命周期里进行postprocessing bean configuration
2) 从external property files来Load configuration information
3) 从property files来Load resource messages(如locale message)
4) 可以对由其他beans或spring container发出的event进行监听(listen)和响应(response)
5)Be aware of their identity within the Spring container
下面逐个逐个介绍这些特殊的Beans
Postprocessing beans
Spring提供了2个opportunities使开发者能够cut into a bean’s lifecycle来review or alter its configuration。这就叫做postprocessing。postprocessing操作是在一个bean进行instantiation
and configuration event发生时执行的。
例如BeanPostProcessor接口给开发者提供了two opportunities to alter a bean after it has been created and wired:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(
Object bean, String name) throws BeansException;
Object postProcessAfterInitialization(
Object bean, String name) throws BeansException;
}
postProcessBeforeInitialization()方法是在bean初始化之前被调用(即在bean定义中设置的“init-method”方法执行之前或实现InitializingBean接口的Bean的afterPropertiesSet()方法执行之前)
postProcessAfterInitialization()方法是在初始化之后被调用(即在bean定义中设置的“init-method”方法执行之后或实现InitializingBean接口的Bean的afterPropertiesSet()方法执行之后)
PostProcessor有2种:
1) Bean PostProcessor
2) Bean Factory PostProcessor
bean postprocessor (注意:BeanPostProcessor是for all beans的,not for one bean)
举个例子,你希望你的APP的所有Bean的String property在bean创建时都要在后面加上一个“!”,那么就可以通过实现BeanPostProcessor接口来实现这个功能。
public class MyProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(
Object bean, String name) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try {
for(int i=0; i < fields.length; i++) {
//check property是否是string type
if(fields[i].getType().equals(
java.lang.String.class)) {
fields[i].setAccessible(true);
String original = (String) fields[i].get(bean);
fields[i].set(bean, original + “!”);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return bean;
}
public Object postProcessBeforeInitialization(
Object bean, String name) throws BeansException {
//Do nothing before initialation
return bean;
}
}
上面的postProcessAfterInitialization方法会for all beans’ property,它会all beans的每一个string property都加一个“!”符号
创建了上面的Bean PostProcessor之后,我们需要把它注册到Spring里,分2种情况:
第一种情况,如果是使用BeanFactory的话,那么就必须通过代码来注册:
BeanPostProcessor processor = new MyProcessor();
factory.addBeanPostProcessor(processor);
第二种情况,也是最最常用到的情况,如果你使用Application context的话,你只需要在configure file里定义该Processor即可:
<bean
class="com.springinaction.chapter03.postprocessor.MyProcessor"/>
bean factory postprocessor
BeanFactoryPostProcessor performs postprocessing on the entire Spring container
BeanFactoryPostProcessor接口的定义如下:
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory)
throws BeansException;
}
postProcessBeanFactory()方法会在all beans loaded之后,任何bean(包括BeanPostProcessor beans)被初始化(instantiated)之前,被spring container所调用。
例如,下面是一个实现了BeanFactoryPostProcessor接口的类:
public class BeanCounter implements BeanFactoryPostProcessor {
private Logger LOGGER = Logger.getLogger(BeanCounter.class);
public void postProcessBeanFactory(
ConfigurableListableBeanFactory factory)
throws BeansException {
LOGGER.debug("BEAN COUNT: " +
factory.getBeanDefinitionCount());
}
}
上面的BeanCounter class是用来log有多少个bean被loaded进spring container。
如果你使用的是application context,那么直接通过下面的定义来把BeanCounter BeanFactoryPostProcessor注册到spring container:
<bean id="beanCounter"
class="com.springinaction.chapter03.postprocessor.BeanCounter"/>
如果你是直接使用Bean Factory,则无法使用BeanFactoryPostProcessor,因为它无法象BeanPostProcessor那样通过addBeanPostProcessor方法来注册
上面的BeanCounter是自定义的BeanFactoryPostProcessor,实际上Spring本身就自带了几个不需要你显式注册的BeanFactoryPostProcessors。例如上一节提到的CustomerEditorConfigurer,它就实现了BeanFactoryPostProcessor接口。另一个有用的BeanFactoryPostProcessor实现类是PropertyPlaceholderConfigurer。它是用来从external property file里load property然后赋值给configure xml file里的变量,下一节将详细讲解。
Externalizing configuration properties
有时候你喜欢把一些property的设置从spring configure xml file里分离出来,例如通常会把data source的设置从spring configure xml file中分离出来,用一个property file来保存。Spring support this function:它通过PropertyPlaceholderConfigurer来告诉spring and configure xml file去load来自外部property file里的设置。因此,你的bean configure xml file里需要定义:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="jdbc.properties" />
</bean>
其中的“location”属性是指定property file的path。
例如,上面指定的jdbc.properties file’s content is:
database.url=jdbc:hsqldb:Training
database.driver=org.hsqldb.jdbcDriver
database.user=appUser
database.password=password
上面的property file定义了data source的配置,那么在spring bean configure file里如何使用它呢?见下列代码:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${database.url}" />
<property name="driverClassName" value="${database.driver}" />
<property name="username" value="${database.user}" />
<property name="password" value="${database.password}" />
</bean>
如果你的bean configure xml file要使用多个external property file,那么就要使用PropertyPlaceholderConfigurer的locations属性:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config. PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>jdbc.properties</value>
<value>security.properties</value>
<value>application.properties</value>
</list>
</property>
</bean>
Resolving resource messages
Property file除了用来作为external configure file,还可以用来存储resource message(如Locale resource message)
假设有2个property file
myresource.properties—Default resource message
myresource_en_US.properties—resource message for en US
Spring提供了一个实现了MessageSource接口的ResourceBundleMessageSource,你需要通过下列定义来把message resource绑定到spring context上:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>trainingtext</value>
</property>
</bean>
请注意:上面的id属性值必须为“messageSource”,这样的话你就不需要把messageSource bean inject到需要它的bean,而只需要调用ApplicationContext的getMessage()方法即可!
例如:
Locale locale = … ; //determine locale
String text = context.getMessage("computer", locale);
而如果是在JSP里使用message resource,则使用tag:
<spring:message code="computer"/>
在你的bean里,是通过ApplicationContext来获得message resource的,你的Bean如何获取ApplicationContext呢??答案当然是把ApplicationContext inject到每一个使用它的bean里。问题来了:这样的话不是要在bean config xml里为每个使用ApplicationContext的bean都设置context?
Spring提供了一种Aware功能,实现了某个AwareXXX接口的Bean,则可以自动获取一些spring相关的资源(),而不需要为bean通过在bean config xml里显式设置来获取。
Spring提供了一些Aware接口,如BeanFactoryAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等。比如,实现了BeanFactoryAware接口的bean,Spring容器会自动把BeanFactory对象注入该bean(当然,前提是该bean有一个BeanFactory变量),而实现了ApplicationContextAware接口的bean,会自动把ApplicationContext对象注入该bean(当然,前提是该bean有一个ApplicationContext变量)
因此下面的代码是通过Aware来inject applicationContext,从而调用getMessage:
public class AwareBean implements ApplicationContextAware{
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
}
public String sayHello () {
return applicationContext.getMessage(“hello”);
}
}
上面的class的bean定义不需要的applicationContext设置,会自动注入!
使用Aware相关接口的缺点是:你的bean class与spring发生了严重的依赖关系。
使用Aware接口和不使用Aware接口的bean本身没什么区别(除了是否实现Aware接口),但在创建了bean实例后,如何把context传递给bean的途径就有区别了:
* 前者是在创建bean实例时就会自动(不知不觉的:))把context传递给该bean
* 后者则是在创建bean实例后,自己手动写代码 bean.setContext(context)来传递context
Decoupling with application events
DI是spring采用的使对象之间松耦合的最主要方式,但不是唯一方式。另外一个使对象之间松耦合,但又能够有互动的方式是:publish and listen for application event。
在spring里,一个bean可以用来做一个event publisher,也可以用来做一个event listener,甚至可以做both。
下面通过一个例子来讲解publish and listen event机制
第一步:定义一个application event,它扩展了ApplicationEvent抽象类
public class CourseFullEvent extends ApplicationEvent {
private Course course;
public CourseFullEvent(Object source, Course course) {
super(source);
this.course = course;
}
public Course getCourse() {
return course;
}
}
第二步:定义一个Application listener,它实现org.springframework.context.ApplicationListener接口。
public class RefreshListener implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
…
}
}
你需要注意的是:除了由你自定义的Bean publish的event之外,spring自身还自带了一些扩展了ApplicationEvent抽象类的event,包括下列几个:
* ContextClosedEvent—Published when the application context is closed
* ContextRefreshedEvent—Published when the application context is initialized or refreshed
* RequestHandledEvent—Published within a web application context when a request is handled
虽然你的大多数Beans都不关心这些自带的event,但你定义的Application listener则不会区分哪些是这些自带的event,哪些是你自定义的、需要的event,只要有event发生就调用onApplicationEvent()方法,因此你的onApplicationEvent()方法的代码必须用来对event进行判断。
第三步:在spring里注册该listener。
很简单,只需要当作普通bean定义即可
<bean id="refreshListener"
class="com.springinaction.foo.RefreshListener"/>
第四步: 用来publish event的bean。
请注意:因为publish event需要用到applicationcontext,因此bean里要DI applicationContext
public class BeanA implements ApplicationContextAware{
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
}
public void testPublishEvent () {
applicationContext.publishEvent(new CourseFullEvent(this, new Course()));
}
}
Scripting beans
对于JAVA来说,你的代码如果改动之后,需要recompile成class后才有效。有些user希望对于一些小的改动,直接修改code,不需要recompile即可使用。有什么解决方案呢?方案就是JAVA和Script Language(如Ruby、Groovy、BeansShell)相结合。Spring支持这种结合!Spring可以把Ruby、Groovy、BeansShell scripts定义成Bean:
下面通过一个例子来讲解如何把script bean注入到一个java bean里
假设有一个Coconut的Bean,它会被注入一个Lime interface的bean:
public class Coconut {
public Coconut() {}
public void drinkLime() {
lime.drink();
}
private Lime lime;
public void setLime(Lime lime) {
this.lime = lime;
}
}
Lime interface:
public interface Lime {
void drink();
}
在spring bean xml file里,Coconut bean的定义是:
<bean id="coconut" class="com.springinaction.scripting.Coconut">
<property name="lime" ref="lime" />
</bean>
请注意:上面我们只定义了coconut bean,它需要引用一个id=”lime”的bean,但我们并没有实现Lime接口的class,因此没有这个bean。下面我们打算分别用ruby, groovy, beansShell scripts来创建这个Bean。
但在这之前,你首先要确保你的spring bean configure xml file的<beans>的声明为:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
…
</beans>
Scripting the Lime in Ruby
下面的ruby scripts实现了Lime接口和drink方法
class Lime
def drink
puts "Called the doctor woke him up!"
end
end
Lime.new
最后一句不能省!没有它,就没有Lime实例对象被创建并注入到Coconut bean里。
重点来了:在spring xml file里对Ruby lime script的bean定义为:
<lang:jruby id="lime"
script-source="classpath:com/springinaction/scripting/Lime.rb"
script-interfaces="com.springinaction.scripting.Lime" />
Scripting a Groovy Lime
相对于Ruby来说,Groovy更适合JAVA开发者上手。
下面的groovy scripts实现了Lime接口和drink方法
class Lime implements com.springinaction.scripting.Lime {
void drink() {
print "Called the doctor woke him up!"
}
}
在spring xml file里对groovy lime script的bean定义为:
<lang:groovy id="lime"
script-source="classpath:com/springinaction/scripting/Lime.groovy" />
Scripting a BeanShell Lime
Ruby and Groovy都有自己的语法规则,但BeanShell就是使用Java的语法。这是最吸引JAVA开发者的地方。
下面的bean scripts实现了Lime接口的drink方法
void drink() {
System.out.println("Called the doctor woke him up!");
}
从上面代码你可以看到它并不是一个class definition,而只定义了drink方法。对于BeanShell来说,你只需要定义接口所需要的方法,而不需要定义class。
在spring xml file里对beanshell lime script的bean定义为:
<lang:bsh id="lime"
script-source="classpath:com/springinaction/scripting/Lime.bsh"
script-interfaces="com.springinaction.scripting.Lime" />
上面学习了如何把script bean注入到java class bean,下面讲解如何把java class bean注入到script bean(只讲解groovy)
我们把上面的例子反过来:Lime用Java class来实现,Coconut用script来实现。
首先创建Lime接口及实现类
public interface Lime {
void drink();
}
public class LimeImpl implements Lime {
public LimeImpl() {}
public void drink() {
System.out.println("Called the doctor woke him up!");
}
}
<bean id="lime" class="com.springinaction.scripting.LimeImpl" />
Groovy Coconut Scripts:
class Coconut implements com.springinaction.scripting.ICoconut {
public void drinkLime() {
lime.drink()
}
com.springinaction.scripting.Lime lime;
}
Groovy coconut bean definition:
<lang:groovy id="coconut"
script-source="classpath:com/springinaction/scripting/Coconut.groovy">
<lang:property name="lime" ref="lime" />
</lang:groovy>
Refreshing scripted beans
使用script language的优势在于不需要recompile,但不是说直接覆盖原来的文件spring就会自动detect到你的script file发生变化,而是需要你在bean configure xml file里通过refresh-check-delay属性来设置spring隔多久来check你的script是否发生变化,即refreshing.
<lang:jruby id="lime"
script-source="classpath:com/springinaction/scripting/Lime.rb"
script-interfaces="com.springinaction.scripting.Lime"
refresh-check-delay="5000"/>
上面的代码设置隔5秒就check一次该bean所对应的script file。refresh-check-delay属性值缺省为-1,即disable refreshing。
Writing scripted beans inline
通常情况下,你都是把scripts bean定义在一个script file里,然后在configure xml file里设置“script-source”属性。但有时如果是想写一些很简单的script,你可以利用<lang:inline-script> element直接把script写在configure xml file里。
例:
<lang:bsh id="lime"
script-interfaces="com.springinaction.scripting.Lime">
<lang:inline-script><![CDATA[
void drink() {
System.out.println("Called the doctor woke him up!");
}
]]>
</lang:inline-script>
</lang:bsh>
注意:强烈建议你的script写在<![CDATA[...]]>里
<lang:jruby>, <lang:groovy> and <lang:bsh>都support <lang:inline-script>
- 《spring 2.0 in action》点滴 (Chapter 03)
- 《spring 2.0 in action》点滴 (Chapter 02)
- xml形式装配bean——spring in action chapter 2
- 《ActiveMQ In Action》Chapter 3 The ActiveMQ in Action examples
- Netty in action thrid chapter 心得总结
- netty in action 4chapter 总结
- netty in action fifth chapter Summary
- netty in action sixth chapter summarize
- "Machine Learning in Action" Chapter 2
- Machine Learning in Action Chapter 2
- Spring in action 读书笔记
- Spring in action
- Spring in Action
- Spring in Action
- Spring in action笔记
- 读书笔记:spring in action
- Spring in Action问题
- Spring in Action(摘抄)
- 今天终于决定换CSDN的BLOG了.
- 软件开发生命周期模型
- c++string单双字符转换。。。。。。
- 开张!
- C#数据访问类
- 《spring 2.0 in action》点滴 (Chapter 03)
- MSDN将字节数组转换为十六进制值字符串时 Byte 的用法
- DELL OEM XP 操作系统集成SATA驱动资源及方法
- http://java.sun.com/developer/onlineTraining/new2java/supplements/quizzes/index.html
- 关系数据库的模式设计(转贴)
- 我们可使用多大的内存?
- TPI有没有听说过?
- 如何优化SQL Server数据库查询(转载)
- 关系模式的分解(转贴)