《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不同外,其他都一样。如果我们要定义50bean,可以想象下。这时我们可以使用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 beanabstract属性设置为true,则spring不会对其实例化。Kenny and david bean则继承baseSaxophonist bean
 
请注意:parent bean并不一定要定义成abstract
 
 
Overriding inherited properties
child bean还可以覆盖继承于parent beanproperties,如:
例子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
这个功能通常用在不同classbean使用同一个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
1Method replacement—Enables existing methods (abstract or concrete) to be replaced at runtime with new implementations 这种方式是for “返回值不是在spring里有bean定义类型”的方法.
 
2Getter 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的典型。那么我们要如何使用呢:
 
假设有一个实现了InstrumentGuitar 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 classspring定义了一个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 classidpianist联系在一起,当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,该属性的typejava.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 loadBeanFactory(你也可以通过代码来调用registerCustomEditor()方法来实现它)
 
请注意:spring自带的custom editors(例如URLEditor and LocaleEditordefault已经被registerspring container啦,不需要用CustomEditorConfigurer.register它们。
 
举个例子bean A有一个url propertyjava.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 creationbean factory的生命周期里进行postprocessing bean configuration
2) external property filesLoad configuration information
3) property filesLoad resource messages(如locale message
4) 可以对由其他beansspring container发出event进行监听(listen)和响应(response)
5Be aware of their identity within the Spring container
 
下面逐个逐个介绍这些特殊的Beans
 
Postprocessing beans
Spring提供了2opportunities使开发者能够cut into a bean’s lifecyclereview or alter its configuration。这就叫做postprocessingpostprocessing操作是在一个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接口BeanafterPropertiesSet()方法执行之前
 
postProcessAfterInitialization()方法是在初始化之后被调用即在bean定义中设置的“init-method”方法执行之后或实现InitializingBean接口BeanafterPropertiesSet()方法执行之后
 
 
 
PostProcessor2种:
1) Bean PostProcessor
2) Bean Factory PostProcessor
 
bean postprocessor (注意:BeanPostProcessorfor all beans的,not for one bean)
举个例子,你希望你的APP所有BeanString propertybean创建时都要在后面加上一个“!”,那么就可以通过实现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有多少个beanloadedspring 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 fileload 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 fileload来自外部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 filepath
 
例如,上面指定的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,那么就要使用PropertyPlaceholderConfigurerlocations属性
<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
 
假设有2property 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,而只需要调用ApplicationContextgetMessage()方法即可!
 
例如:
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里为每个使用ApplicationContextbean都设置context
 
Spring提供了一种Aware功能,实现了某个AwareXXX接口的Bean,则可以自动获取一些spring相关的资源(),而不需要为bean通过在bean config xml里显式设置来获取
 
Spring提供了一些Aware接口,如BeanFactoryAwareApplicationContextAwareResourceLoaderAwareServletContextAware等等。比如,实现了BeanFactoryAware接口的beanSpring容器会自动把BeanFactory对象注入该bean(当然,前提是该bean有一个BeanFactory变量),而实现了ApplicationContextAware接口的bean,会自动把ApplicationContext对象注入该bean(当然,前提是该bean有一个ApplicationContext变量)
 
因此下面的代码是通过Awareinject 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”);
    }
}
 
上面的classbean定义不需要的applicationContext设置,会自动注入!
 
使用Aware相关接口的缺点是:你的bean classspring发生了严重的依赖关系。
 
使用Aware接口和不使用Aware接口的bean本身没什么区别(除了是否实现Aware接口),但在创建了bean实例后,如何把context传递给bean的途径就有区别了:
 * 前者是在创建bean实例时就会自动(不知不觉的:))context传递给该bean
 * 后者则是在创建bean实例后,自己手动写代码 bean.setContext(context)来传递context
 
 
Decoupling with application events
DIspring采用的使对象之间松耦合的最主要方式,但不是唯一方式。另外一个使对象之间松耦合,但又能够有互动的方式是: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 publishevent之外,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 eventbean
请注意:因为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来说,你的代码如果改动之后,需要recompileclass后才有效。有些user希望对于一些小的改动,直接修改code,不需要recompile即可使用。有什么解决方案呢?方案就是JAVAScript Language(如RubyGroovyBeansShell)相结合Spring支持这种结合!Spring可以把RubyGroovyBeansShell scripts定义成Bean
 
 
下面通过一个例子来讲解如何把script bean注入到一个java bean
 
假设有一个CoconutBean,它会被注入一个Lime interfacebean
 
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 scriptbean定义为
<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 scriptbean定义为:
<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 scriptbean定义为:
<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
 
我们把上面的例子反过来:LimeJava class来实现,Coconutscript来实现
 
首先创建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 filerefresh-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>
 
原创粉丝点击