35. 动态语言支持

35.1 简介

Spring 2.0引入了对使用Spring和动态语言(如JRuby)定义的类和对象的全面支持。这种支持允许您以受支持的动态语言编写任意数量的类,并让Spring容器透明地实例化,配置和依赖注入生成的对象。

目前支持的动态语言包括:

  • JRuby 1.5

  • Groovy 1.8

  • BeanShell 2.0


Why only these languages? 选择支持的语言是因为a)语言在Java企业社区中具有很大的吸引力,b)在添加此支持时没有为其他语言提出请求,以及c)Spring开发人员最熟悉它们。


Section 35.4, “Scenarios” 中描述了这种动态语言支持可以立即有用的完整工作示例。

35.2 第一个例子

本章的大部分内容涉及详细描述动态语言支持。在深入研究动态语言支持的所有细节之前,让我们看一下用动态语言定义的bean的快速示例。第一个bean的动态语言是Groovy(这个例子的基础来自Spring测试套件,所以如果你想看到任何其他支持语言的等效例子,请看一下源代码)。

在下面找到Groovy bean将要实现的 Messenger 接口,并注意此接口是在普通Java中定义的。注入了 Messenger 引用的依赖对象将不知道底层实现是Groovy脚本。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

以下是依赖于 Messenger 接口的类的定义。

package org.springframework.scripting;

public class DefaultBookingService implements BookingService {

    private Messenger messenger;

    public void setMessenger(Messenger messenger) {
        this.messenger = messenger;
    }

    public void processBooking() {
        // use the injected Messenger object...
    }

}

这是Groovy中 Messenger 接口的实现。

// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;

// import the Messenger interface (written in Java) that is to be implemented
import org.springframework.scripting.Messenger

// define the implementation in Groovy
class GroovyMessenger implements Messenger {

    String message

}

最后,这里是bean定义,它将影响将Groovy定义的 Messenger 实现注入 DefaultBookingService 类的实例。

要使用自定义动态语言标记来定义动态语言支持的bean,您需要在Spring XML配置文件的顶部使用XML Schema前导码。您还需要使用Spring ApplicationContext实现作为IoC容器。支持使用带有简单BeanFactory实现的动态语言支持的bean,但是您必须管理Spring内部的管道才能这样做。有关基于模式的配置的更多信息,请参见第41章基于XML模式的配置。

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">

    <!-- this is the bean definition for the Groovy-backed Messenger implementation -->
    <lang:groovy id="messenger" script-source="classpath:Messenger.groovy">
        <lang:property name="message" value="I Can Do The Frug"/>
    </lang:groovy>

    <!-- an otherwise normal bean that will be injected by the Groovy-backed Messenger -->
    <bean id="bookingService" class="x.y.DefaultBookingService">
        <property name="messenger" ref="messenger"/>
    </bean>

</beans>

bookingService bean( DefaultBookingService )现在可以正常使用其私有 messenger 成员变量,因为注入其中的 Messenger 实例是 Messenger 实例。这里没有什么特别的东西,只是简单的Java和普通的Groovy。

希望上面的XML片段是不言自明的,但如果不是,请不要过分担心。请继续阅读有关上述配置的原因和原因的详细信息。

35.3 定义由动态语言支持的bean

本节将准确描述如何在任何支持的动态语言中定义Spring托管bean。

请注意,本章不会尝试解释支持的动态语言的语法和习语。例如,如果您想使用Groovy编写应用程序中的某些类,那么假设您已经知道了Groovy。如果您需要有关动态语言本身的更多详细信息,请参阅本章末尾的 Section 35.6, “Further resources”

35.3.1 常见概念

使用动态语言支持的bean涉及的步骤如下:

  • 编写动态语言源代码的测试(自然)

  • 然后编写动态语言源代码本身:)

  • 使用XML配置中相应的 <lang:language/> 元素定义动态语言支持的bean(当然,您可以使用Spring API以编程方式定义此类bean - 尽管您必须查阅源代码以获取有关如何执行此操作的指示本章未介绍高级配置的类型。请注意,这是一个迭代步骤。每个动态语言源文件至少需要一个bean定义(尽管同一个动态语言源文件当然可以由多个bean定义引用)。

前两个步骤(测试和编写动态语言源文件)超出了本章的范围。请参阅所选动态语言的语言规范和/或参考手册,并着手开发动态语言源文件。首先,您将首先阅读本章的其余部分,因为Spring的动态语言支持确实对动态语言源文件的内容做了一些(小的)假设。

<lang:language />元素

最后一步涉及定义动态语言支持的bean定义,每个bean对应一个要配置的bean(这与普通的JavaBean配置没有区别)。但是,不是指定要由容器实例化和配置的类的完全限定类名,而是使用 <lang:language/> 元素来定义动态语言支持的bean。

每种受支持的语言都有相应的 <lang:language/> 元素:

  • <lang:jruby/> (JRuby)

  • <lang:groovy/> (Groovy)

  • <lang:bsh/> (BeanShell)

可用的确切属性和子元素配置取决于bean定义的确切语言(下面的语言特定部分提供了完整的低位)。

可刷新的 beans 子

Spring中动态语言支持的一个(如果不是)最引人注目的 Value 增加是“可刷新的bean”功能。

可刷新bean是一个动态语言支持的bean,只需少量配置,动态语言支持的bean就可以监视其底层源文件资源的变化,然后在动态语言源文件发生变化时重新加载(对于例如,开发人员编辑并保存对文件系统上文件的更改)。

这允许开发人员将任意数量的动态语言源文件部署为应用程序的一部分,将Spring容器配置为创建由动态语言源文件支持的bean(使用本章中描述的机制),然后随着需求的变化或其他一些外部因素发挥作用,只需编辑动态语言源文件,并在由更改的动态语言源文件支持的bean中进行任何更改。无需关闭正在运行的应用程序(或在Web应用程序的情况下重新部署)。如此修改的动态语言支持的bean将从更改的动态语言源文件中获取新的状态和逻辑。

请注意,默认情况下此功能处于关闭状态。

让我们看一个例子来看看开始使用可刷新的bean是多么容易。要打开可刷新的bean功能,您只需在bean定义的 <lang:language/> 元素上指定一个额外的属性。因此,如果我们坚持本章前面的 the example ,那么我们将在Spring XML配置中更改以实现可刷新的bean:

<beans>

    <!-- this bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
    <lang:groovy id="messenger"
            refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
            script-source="classpath:Messenger.groovy">
        <lang:property name="message" value="I Can Do The Frug"/>
    </lang:groovy>

    <bean id="bookingService" class="x.y.DefaultBookingService">
        <property name="messenger" ref="messenger"/>
    </bean>

</beans>

这就是你所要做的一切。 'messenger' bean定义上定义的 'refresh-check-delay' 属性是在对基础动态语言源文件所做的任何更改之后刷新bean之前的毫秒数。您可以通过为 'refresh-check-delay' 属性指定负值来关闭刷新行为。请记住,默认情况下,禁用刷新行为。如果您不想要刷新行为,则只需不要定义属性。

如果我们运行以下应用程序,我们可以运行可刷新的功能;请在下一段代码中请原谅'跳过箍到暂停执行'的诡计。 System.in.read() 调用仅在那里,以便在我(作者)关闭并编辑底层动态语言源文件时暂停程序的执行,以便在程序恢复执行时刷新将在动态语言支持的bean上触发。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Messenger messenger = (Messenger) ctx.getBean("messenger");
        System.out.println(messenger.getMessage());
        // pause execution while I go off and make changes to the source file...
        System.in.read();
        System.out.println(messenger.getMessage());
    }
}

那么,为了本示例的目的,我们假设必须更改对 Messenger 实现的 getMessage() 方法的所有调用,以使消息被引号括起来。以下是我(作者)暂停执行程序时对 Messenger.groovy 源文件所做的更改。

package org.springframework.scripting

class GroovyMessenger implements Messenger {

    private String message = "Bingo"

    public String getMessage() {
        // change the implementation to surround the message in quotes
        return "'" + this.message + "'"
    }

    public void setMessage(String message) {
        this.message = message
    }
}

程序执行时,输入暂停前的输出将是I Can Do The Frug。在对源文件进行更改并保存并且程序恢复执行之后,在动态语言支持的 Messenger 实现上调用 getMessage() 方法的结果将是“I Can Do The Frug”(注意包含附加内容)引号)。

重要的是要理解,如果在 'refresh-check-delay' 值的窗口内发生更改,脚本的更改将不会触发刷新。同样重要的是要理解,在动态语言支持的bean上调用方法之前,实际上不会“拾取”脚本的更改。只有在动态语言支持的bean上调用方法时,它才会检查其底层脚本源是否已更改。与刷新脚本有关的任何异常(例如遇到编译错误或发现脚本文件已被删除)都会导致致命异常传播到调用代码。

上面描述的可刷新bean行为不适用于使用 <lang:inline-script/> 元素表示法定义的动态语言源文件(请参阅 the section called “Inline dynamic language source files” )。此外,它仅适用于实际可以检测到对基础源文件的更改的bean;例如,通过检查文件系统上存在的动态语言源文件的上次修改日期的代码。

内联动态语言源文件

动态语言支持还可以满足直接嵌入Spring bean定义的动态语言源文件。更具体地说, <lang:inline-script/> 元素允许您在Spring配置文件中立即定义动态语言源。一个例子可能会使内联脚本功能清晰明了:

<lang:groovy id="messenger">
    <lang:inline-script>

package org.springframework.scripting.groovy;

import org.springframework.scripting.Messenger

class GroovyMessenger implements Messenger {
    String message
}

    </lang:inline-script>
    <lang:property name="message" value="I Can Do The Frug"/>
</lang:groovy>

如果我们将一个问题放在一边,围绕在内部定义动态语言源是否是一个好习惯一个Spring配置文件, <lang:inline-script/> 元素在某些情况下很有用。例如,我们可能希望快速将Spring Validator 实现添加到Spring MVC Controller 。这只是使用内联源的片刻工作。 (有关此类示例,请参阅 Section 35.4.2, “Scripted Validators” 。)

下面是使用 inline: 表示法直接在Spring XML配置文件中定义基于JRuby的bean的源代码的示例。 (注意使用<字符表示 '<' 字符。在这种情况下,围绕 <![CDATA[]]> 区域中的内联源可能会更好。)

<lang:jruby id="messenger" script-interfaces="org.springframework.scripting.Messenger">
    <lang:inline-script>

require 'java'

include_class 'org.springframework.scripting.Messenger'

class RubyMessenger &lt; Messenger

    def setMessage(message)
        @@message = message
    end

    def getMessage
        @@message
    end

end

        </lang:inline-script>
    <lang:property name="message" value="Hello World!"/>
</lang:jruby>

在动态语言支持的bean的上下文中了解构造函数注入

关于Spring的动态语言支持,有一件非常重要的事情需要注意。也就是说,(当前)不可能为动态语言支持的bean提供构造函数参数(因此构造函数注入不适用于动态语言支持的bean)。为了使构造函数和属性的这种特殊处理100%清晰,以下代码和配置的混合将不起作用。

// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;

import org.springframework.scripting.Messenger

class GroovyMessenger implements Messenger {

    GroovyMessenger() {}

    // this constructor is not available for Constructor Injection
    GroovyMessenger(String message) {
        this.message = message;
    }

    String message

    String anotherMessage

}
<lang:groovy id="badMessenger"
    script-source="classpath:Messenger.groovy">
    <!-- this next constructor argument will not be injected into the GroovyMessenger -->
    <!-- in fact, this isn't even allowed according to the schema -->
    <constructor-arg value="This will not work"/>

    <!-- only property values are injected into the dynamic-language-backed object -->
    <lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object"/>

</lang>

在实践中,这种限制并不像它最初看起来那么重要,因为二手注入是绝大多数开发人员所青睐的注入风格(让我们继续讨论这对于另一天来说是否是好事)。

35.3.2 JRuby beans


The JRuby library dependencies

Spring中的JRuby脚本支持要求以下库位于应用程序的类路径中。

  • jruby.jar

来自JRuby主页......

“JRuby是Ruby编程语言的100%纯Java实现。”

为了与Spring提供选择的理念保持一致,Spring的动态语言支持还支持JRuby语言中定义的bean。 JRuby语言基于非常直观的Ruby语言,并且支持内联正则表达式,块(闭包)以及一系列其他功能,这些功能确实使一些域问题的解决方案更容易开发。

Spring中JRuby动态语言支持的实现非常有趣,因为它发生了这样的事情:Spring创建了一个JDK动态代理,实现了 <lang:ruby> 元素的 'script-interfaces' 属性值中指定的所有接口(这就是为什么你必须在属性值中至少有一个接口,并且(相应地)在使用JRuby支持的bean时编程到接口。

让我们看一个使用基于JRuby的bean的完整工作示例。以下是本章前面定义的 Messenger 接口的JRuby实现(为方便起见,下面重复)。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}
require 'java'

class RubyMessenger
    include org.springframework.scripting.Messenger

    def setMessage(message)
        @@message = message
    end

    def getMessage
        @@message
    end
end

# this last line is not essential (but see below)
RubyMessenger.new

这里是Spring XML,它定义了 RubyMessenger JRuby bean的一个实例。

<lang:jruby id="messageService"
        script-interfaces="org.springframework.scripting.Messenger"
        script-source="classpath:RubyMessenger.rb">

    <lang:property name="message" value="Hello World!"/>

</lang:jruby>

记下JRuby源代码的最后一行( 'RubyMessenger.new' )。在Spring的动态语言支持的上下文中使用JRuby时,建议您实例化并返回一个JRuby类的新实例,该实例要作为执行JRuby源的结果用作动态语言支持的bean。您可以通过简单地在源文件的最后一行实例化JRuby类的新实例来实现这一点,如下所示:

require 'java'

include_class 'org.springframework.scripting.Messenger'

# class definition same as above...

# instantiate and return a new instance of the RubyMessenger class
RubyMessenger.new

如果你忘记这样做,那就不是世界末日;然而,这将导致Spring必须通过JRuby类的类型表示(反射)来寻找要实例化的类。在宏观方案中,这将是如此之快,以至于你永远不会注意到它,但它可以通过简单地使用上面的一行作为JRuby脚本的最后一行来避免。如果你没有提供这样的一行,或者你的脚本中没有找到一个JRuby类来实例化,那么在JRuby解释器执行源之后会立即抛出一个不透明的 ScriptCompilationException 。可以在下面找到将此标识为异常的根本原因的关键文本(因此,如果您的Spring容器在创建动态语言支持的bean时抛出以下异常,并且相应的堆栈跟踪中存在以下文本,则希望能让您识别并轻松纠正问题):

org.springframework.scripting.ScriptCompilationException: Compilation of JRuby script returned ''

要解决这个问题,只需将您希望公开的类的新实例实例化为JRuby动态语言支持的bean(如上所示)。另请注意,您可以在JRuby脚本中实际定义任意数量的类和对象;重要的是源文件作为一个整体必须返回一个对象(对于Spring来配置)。

有关您可能希望使用基于JRuby的bean的某些情况,请参阅 Section 35.4, “Scenarios”

35.3.3 Groovy bean


The Groovy library dependencies

Spring中的Groovy脚本支持要求以下库位于应用程序的类路径中。

  • groovy-1.8.jar

  • asm-3.2.jar

  • antlr-2.7.7.jar


来自Groovy主页......

“Groovy很敏捷Java 2平台的动态语言,它具有人们在Python,Ruby和Smalltalk等语言中非常喜欢的许多功能,使Java开发人员可以使用类似Java的语法。 “

如果您已经从顶部开始阅读本章,那么您将拥有一个由Groovy动态语言支持的bean的 seen an example 。让我们看另一个例子(再次使用Spring测试套件中的一个例子)。

package org.springframework.scripting;

public interface Calculator {

    int add(int x, int y);

}

这是Groovy中 Calculator 接口的实现。

// from the file 'calculator.groovy'
package org.springframework.scripting.groovy

class GroovyCalculator implements Calculator {

    int add(int x, int y) {
        x + y
    }

}
<-- from the file 'beans.xml' -->
<beans>
    <lang:groovy id="calculator" script-source="classpath:calculator.groovy"/>
</beans>

最后,这是一个小应用程序来执行上述配置。

package org.springframework.scripting;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void Main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Calculator calc = (Calculator) ctx.getBean("calculator");
        System.out.println(calc.add(2, 8));
    }
}

运行上述程序得到的结果将是(毫不奇怪)10。(令人兴奋的例子,是吗?请记住,目的是为了说明这个概念。请参考动态语言展示项目以获得更复杂的例子,或者事实上 Section 35.4, “Scenarios” 章节)。

重要的是,不要为每个Groovy源文件定义多个类。虽然这在Groovy中是完全合法的,但它(可以说)是一种不好的做法:为了一致的方法,你应该(在本作者看来)尊重每个源文件的一个(公共)类的标准Java约定。

通过回调自定义Groovy对象

GroovyObjectCustomizer 接口是一个回调,允许您将其他创建逻辑 hook 到创建Groovy支持的bean的过程中。例如,此接口的实现可以调用任何所需的初始化方法,或设置一些默认属性值,或指定自定义 MetaClass

public interface GroovyObjectCustomizer {

    void customize(GroovyObject goo);
}

Spring框架将实例化Groovy支持的bean的实例,然后将创建的 GroovyObject 传递给指定的 GroovyObjectCustomizer (如果已定义)。您可以使用提供的 GroovyObject 引用执行任何您喜欢的操作:预期自定义 MetaClass 的设置是大多数人想要对此回调执行的操作,您可以在下面看到执行此操作的示例。

public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer {

    public void customize(GroovyObject goo) {
        DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) {

            public Object invokeMethod(Object object, String methodName, Object[] arguments) {
                System.out.println("Invoking '" + methodName + "'.");
                return super.invokeMethod(object, methodName, arguments);
            }
        };
        metaClass.initialize();
        goo.setMetaClass(metaClass);
    }

}

完整讨论Groovy中的元编程超出了Spring参考手册的范围。请参阅Groovy参考手册的相关部分,或在线搜索:有很多关于此主题的文章。如果您使用Spring命名空间支持,实际上很容易使用 GroovyObjectCustomizer

<!-- define the GroovyObjectCustomizer just like any other bean -->
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>

    <!-- ... and plug it into the desired Groovy bean via the 'customizer-ref' attribute -->
    <lang:groovy id="calculator"
        script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy"
        customizer-ref="tracingCustomizer"/>

如果您不使用Spring命名空间支持,您仍然可以使用 GroovyObjectCustomizer 功能。

<bean id="calculator" class="org.springframework.scripting.groovy.GroovyScriptFactory">
    <constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/>
    <!-- define the GroovyObjectCustomizer (as an inner bean) -->
    <constructor-arg>
        <bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
    </constructor-arg>
</bean>

<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>

从Spring Framework 4.3.3开始,您还可以在与Spring的GroovyObjectCustomizer相同的位置指定Groovy CompilationCustomizer(例如ImportCustomizer)或甚至完整的Groovy CompilerConfiguration对象。

35.3.4 BeanShell bean


The BeanShell library dependencies

Spring中的BeanShell脚本支持要求以下库位于应用程序的类路径中。

  • bsh-2.0b4.jar

来自BeanShell主页......

“BeanShell是一个小型的,免费的,可嵌入的Java源代码解释器,具有动态语言功能,用Java编写.BeanShell动态执行标准Java语法,并使用常见的脚本编写方便性扩展它,例如松散的类型,命令和方法闭包,如Perl和JavaScript中的那些“。

与Groovy相比,BeanShell支持的bean定义需要一些(小)额外配置。 Spring中BeanShell动态语言支持的实现很有意思,它发生的是:Spring创建了一个JDK动态代理,实现了 <lang:bsh> 元素的 'script-interfaces' 属性值中指定的所有接口(这就是为什么你必须提供属性值中至少有一个接口,并且(相应地)在使用BeanShell支持的bean时编程到接口。这意味着对BeanShell支持的对象的每个方法调用都将通过JDK动态代理调用机制。

让我们看一个使用基于BeanShell的bean的完整工作示例,该bean实现本章前面定义的 Messenger 接口(为方便起见,下面重复)。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

这是 Messenger 接口的BeanShell'实现'(这里使用的术语很松散)。

String message;

String getMessage() {
    return message;
}

void setMessage(String aMessage) {
    message = aMessage;
}

这里是定义上述“类”的“实例”的Spring XML(同样,这里使用的术语非常松散)。

<lang:bsh id="messageService" script-source="classpath:BshMessenger.bsh"
    script-interfaces="org.springframework.scripting.Messenger">

    <lang:property name="message" value="Hello World!"/>
</lang:bsh>

有关您可能希望使用基于BeanShell的bean的某些情况,请参阅 Section 35.4, “Scenarios”

35.4 场景

当然,以脚本语言定义Spring托管bean的可能场景是有益的,当然,这些场景可能有很多种。本节介绍Spring中动态语言支持的两种可能用例。

35.4.1 脚本化的Spring MVC控制器

可以从使用动态语言支持的bean中受益的一组类是Spring MVC控制器。在纯Spring MVC应用程序中,通过Web应用程序的导航流程在很大程度上取决于Spring MVC控制器中封装的代码。作为导航流程和需要更新Web应用程序的其他表示层逻辑以响应支持问题或更改业务需求,通过编辑一个或多个动态语言源文件并查看这些更改立即反映出来,可能更容易实现任何此类所需更改正在运行的应用程序的状态。

请记住,在Spring等项目所支持的轻量级架构模型中,您通常希望拥有一个非常精简的表示层,应用程序的所有内容业务逻辑都包含在域和服务层类中。将Spring MVC控制器开发为动态语言支持的bean允许您通过简单地编辑和保存文本文件来更改表示层逻辑;对此类动态语言源文件的任何更改(取决于配置)将自动反映在由动态语言源文件支持的Bean中。

为了实现对动态语言支持bean的任何更改的自动“拾取”,您必须启用“可刷新bean”功能。有关此功能的完整处理,请参阅“可刷新的bean”一节。

下面是使用Groovy动态语言实现 org.springframework.web.servlet.mvc.Controller 的示例。

// from the file '/WEB-INF/groovy/FortuneController.groovy'
package org.springframework.showcase.fortune.web

import org.springframework.showcase.fortune.service.FortuneService
import org.springframework.showcase.fortune.domain.Fortune
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.mvc.Controller

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class FortuneController implements Controller {

    @Property FortuneService fortuneService

    ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse httpServletResponse) {
        return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune())
    }

}
<lang:groovy id="fortune"
        refresh-check-delay="3000"
        script-source="/WEB-INF/groovy/FortuneController.groovy">
    <lang:property name="fortuneService" ref="fortuneService"/>
</lang:groovy>

35.4.2 脚本验证器

Spring可以从动态语言支持的bean提供的灵活性中受益的另一个应用程序开发领域是验证。与常规Java相比,使用松散类型的动态语言(也可能支持内联正则表达式)表达复杂的验证逻辑可能更容易。

再次,将验证器开发为动态语言支持的bean允许您通过简单地编辑和保存简单的文本文件来更改验证逻辑;任何此类更改将(根据配置)自动反映在正在运行的应用程序的执行中,并且不需要重新启动应用程序。

请注意,为了实现对动态语言支持的bean的任何更改的自动“拾取”,您必须启用“可刷新的bean”功能。有关此功能的完整详细信息,请参阅“可刷新的bean”一节。

下面是使用Groovy动态语言实现的Spring org.springframework.validation.Validator 的示例。 (有关 Validator 接口的讨论,请参见 Section 9.2, “Validation using Spring’s Validator interface” 。)

import org.springframework.validation.Validator
import org.springframework.validation.Errors
import org.springframework.beans.TestBean

class TestBeanValidator implements Validator {

    boolean supports(Class clazz) {
        return TestBean.class.isAssignableFrom(clazz)
    }

    void validate(Object bean, Errors errors) {
        if(bean.name?.trim()?.size() > 0) {
            return
        }
        errors.reject("whitespace", "Cannot be composed wholly of whitespace.")
    }

}

35.5 比特和鲍勃

最后一节包含与动态语言支持相关的一些位和bobs。

35.5.1 AOP - 建议脚本bean

可以使用Spring AOP框架来建议脚本bean。 Spring AOP框架实际上并不知道正在被建议的bean可能是一个脚本bean,因此您可能正在使用或旨在使用的所有AOP用例和功能都将与脚本bean一起使用。在建议脚本化bean时,您需要注意一件事(小事)......您不能使用基于类的代理,您必须使用 interface-based proxies

您当然不仅限于为脚本bean提供建议......您还可以使用受支持的动态语言编写方面本身,并使用此类bean来建议其他Spring bean。这确实是动态语言支持的高级用法。

35.5.2 范围界定

如果它不是很明显,脚本bean当然可以像任何其他bean一样限定范围。各种 <lang:language/> 元素上的 scope 属性允许您控制底层脚本bean的范围,就像使用常规bean一样。 (默认范围是 singleton ,就像'常规'bean一样。)

下面是一个使用 scope 属性来定义作为 prototype 的Groovy bean的示例。

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">

    <lang:groovy id="messenger" script-source="classpath:Messenger.groovy" scope="prototype">
        <lang:property name="message" value="I Can Do The RoboCop"/>
    </lang:groovy>

    <bean id="bookingService" class="x.y.DefaultBookingService">
        <property name="messenger" ref="messenger"/>
    </bean>

</beans>

有关Spring Framework中作用域支持的更全面讨论,请参阅 Chapter 7, The IoC container 中的 Section 7.5, “Bean scopes”

35.6 更多资源

请在下面找到有关本章所述各种动态语言的更多资源的链接。

Updated at: 5 months ago
34.6.3. 使用触发器和SchedulerFactoryBean连接作业Table of content36. 缓存抽象
Comment
You are not logged in.

There are no comments.