Spring Framework 中文文档

4.3.21.RELEASE

35. 动态语言支持

35.1 简介

Spring 2.0 引入了对使用 classes 和 objects 的全面支持,这些 classes 和 objects 是使用动态语言(如 JRuby)和 Spring 定义的。此支持允许您以受支持的动态语言编写任意数量的 classes,并使 Spring 容器透明地实例化,配置和依赖 inject 得到的 objects。

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

  • JRuby 1.5

  • Groovy 1.8

  • BeanShell 2.0


为什么只有这些语言?

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


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

35.2 第一个 example

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

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

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

以下是 class 的定义,该 class 依赖于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接口的 implementation。

// 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-defined Messenger 实现注入DefaultBookingService class 的实例。

要使用自定义动态语言标记来定义 dynamic-language-backed beans,您需要在 Spring XML configuration 文件的顶部放置 XML Schema 前导码。您还需要使用 Spring ApplicationContext implementation 作为 IoC 容器。支持使用带有普通BeanFactory implementation 的 dynamic-language-backed beans,但您必须管理 Spring 内部的管道才能执行此操作。

有关 schema-based configuration 的更多信息,请参阅第 41 章,XML Schema-based configuration

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

希望上面的 XML 片段是 self-explanatory,但如果不是,请不要过分担心。继续阅读 in-depth 详细信息,了解上述配置的原因和原因。

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

本节将详细介绍如何在任何支持的动态语言中定义 Spring 托管 beans。

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

35.3.1 共同概念

使用 dynamic-language-backed beans 所涉及的步骤如下:

  • 为动态语言编写测试 source code(自然)

  • 然后编写动态语言 source code 本身:)

  • 使用 XML configuration 中的相应<lang:language/>元素定义 dynamic-language-backed beans(您当然可以使用 Spring API 以编程方式定义此类 beans - 尽管您必须查阅 source code 以获取有关如何执行此操作的说明,因为此类高级 configuration 是本章未涉及)。请注意,这是一个迭代的 step。每个动态语言源文件至少需要一个 bean 定义(尽管同一个动态语言源文件当然可以由多个 bean 定义引用)。

前两个步骤(测试和编写动态语言源 files)超出了本章的范围。请参阅所选动态语言的语言规范和/或 reference 手册,并使用 developing 动态语言源 files 进行破解。您首先要阅读本章的 rest,因为 Spring 的动态语言支持确实对动态语言源 files 的内容做了一些(小的)假设。

< hh:// +29+ 3 >元素

最后的 step 涉及定义 dynamic-language-backed bean 定义,每个 bean 用于您要配置的一个(这与普通的 JavaBean configuration 没有区别)。但是,不是指定要由容器实例化和配置的 class 的完全限定类名,而是使用<lang:language/>元素来定义动态 language-backed bean。

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

  • <lang:jruby/>(JRuby)

  • <lang:groovy/>(Groovy)

  • <lang:bsh/>(BeanShell)

configuration 可用的确切属性和 child 元素取决于 bean 已定义的语言(下面的 language-specific 部分提供了完整的低位)。

可刷新的 beans

其中一个(如果不是)最引人注目的 value 在 Spring 中添加了动态语言支持,这是一个可以刷新的 bean'feature。

可刷新的 bean 是 dynamic-language-backed bean,只需少量 configuration,dynamic-language-backed bean 就可以监视其底层源文件资源的更改,然后在更改动态语言源文件时重新加载(对于 example,当开发人员编辑并保存更改时)到文件系统上的文件)。

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

请注意,此 feature 默认为关闭。

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

<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'属性指定负 value 来关闭刷新行为。请记住,默认情况下,禁用刷新行为。如果您不想要刷新行为,则只需不要定义属性。

如果我们然后运行以下 application,我们可以运行可刷新的 feature;请在下一段 code 中请原谅'jumping-through-hoops-to-pause-the-execution'shenanigans。 System.in.read()调用仅在那里,以便在我(作者)关闭并编辑底层动态语言源文件时暂停程序的执行,以便在程序恢复执行时刷新将在 dynamic-language-backed 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 __mplementations 的getMessage()方法的所有 calls,以便消息被引号括起来。以下是我(作者)暂停执行程序时对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。在对源文件进行更改并保存并且程序恢复执行之后,在 dynamic-language-backed Messenger implementation 上调用getMessage()方法的结果将是'I Can Do The Frug'(注意包含附加引号)。

重要的是要理解,如果在'refresh-check-delay' value 的窗口内发生更改,对脚本的更改将不会触发刷新。同样重要的是要理解,在 dynamic-language-backed bean 上调用方法之前,对脚本的更改实际上并未“拾取”。只有在 dynamic-language-backed bean 上调用方法时,它才会检查其底层脚本源是否已更改。任何与刷新脚本相关的 exceptions(例如遇到编译错误或发现脚本文件已被删除)都会导致致命的 exception 传播到调用 code。

上面描述的可刷新的 bean 行为不适用于使用<lang:inline-script/>元素表示法定义的动态语言源 files(请参阅名为“内联动态语言源 files”的部分)。此外,它仅适用于实际可以检测到对基础源文件的更改的 beans; for example,由 code 检查文件系统上存在的动态语言源文件的最后修改 date。

内联动态语言源 files

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

<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 configuration 文件中定义动态语言源的优良做法的问题放在一边,那么<lang:inline-script/>元素在某些情况下会很有用。例如,我们可能希望快速将 Spring Validator implementation 添加到 Spring MVC Controller。这只是 moment 使用内联源的工作。 (对于这样的 example.),见第 35.4.2 节,“脚本验证器”

在下面找到一个使用inline:表示法直接在 Spring XML configuration 文件中定义 JRuby-based bean 源的示例。 (注意使用<字符表示'<'字符.在这种情况下,<![CDATA[]]>区域内的内联源可能是 better.)

<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>

了解 dynamic-language-backed beans 的 context 中的构造函数注入

关于 Spring 的动态语言支持,有一件非常重要的事情需要注意。也就是说,(目前)不可能为 dynamic-language-backed beans 提供构造函数 arguments(因此 dynamic-language-backed 不适用于 dynamic-language-backed beans)。为了使构造函数和 properties 的这种特殊处理 100%清晰,以下 code 和 configuration 的混合将不起作用。

// 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


JRuby library 依赖

Spring 中的 JRuby 脚本支持要求以下 libraries 位于 application 的 classpath 中。

  • jruby.jar

来自 JRuby 主页......

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

为了与提供选择的 Spring 哲学保持一致,Spring 的动态语言支持也支持 JRuby 语言中定义的 beans。 JRuby 语言基于非常直观的 Ruby 语言,并且支持内联正则表达式,块(闭包)以及其他 features 的整个 host,它们使得某些域问题的解决方案变得更容易开发。

Spring 中 JRuby 动态语言支持的 implementation 很有意思,它发生的是:Spring 创建一个 JDK 动态代理,实现<lang:ruby>元素的'script-interfaces'属性 value 中指定的所有接口(这就是你必须提供的原因)在属性的 value 中至少有一个接口,并且(相应地)在使用 JRuby-backed beans 时编程到接口。

让我们看一下使用 JRuby-based bean 的完全工作示例。以下是本章前面定义的Messenger接口的 JRuby implementation(为方便起见,下面重复)。

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 的动态语言支持的 context 中使用 JRuby 时,建议您实例化并 return 一个新的 JRuby class 实例,该实例要作为执行 JRuby 源的结果用作 dynamic-language-backed bean。您可以通过简单地在源文件的最后行上实例化 JRuby class 的新实例来实现这一点,如下所示:

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 class 的类型表示(反射)搜索,以查找 class 来实例化。在事物的宏观方面,这将是如此之快,以至于你永远不会注意到它,但是通过简单地使用上面的 line 作为 JRuby 脚本的最后行,可以避免这种情况。如果你没有提供这样的 line,或者 Spring 在你的脚本中找不到 JRuby class 来实例化,那么在 JRuby interpreter 执行源之后会立即抛出一个 opaque ScriptCompilationException。可以在下面找到将此标识为 exception 的根本原因的 key 文本(因此,如果您的 Spring 容器在_创建 dynamic-language-backed bean 并且相应的堆栈跟踪中存在以下文本时抛出以下 exception,这将有希望允许您确定然后轻松纠正问题):

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

要解决这个问题,只需将您希望公开的 class 的新实例实例化为 JRuby-dynamic-language-backed bean(如上所示)。另请注意,您可以在 JRuby 脚本中实际定义任意数量的 classes 和 objects;重要的是源文件作为一个整体必须 return object(用于 Spring 配置)。

对于您可能想要使用 JRuby-based beans 的某些情况,请参阅第 35.4 节,“场景”

35.3.3 Groovy beans


Groovy library 依赖

Spring 中的 Groovy 脚本支持要求以下 libraries 位于 application 的 classpath 中。

  • groovy-1.8.jar

  • asm-3.2.jar

  • antlr-2.7.7.jar


来自 Groovy 主页......

“Groovy 是 Java 2 平台的一种敏捷动态语言,它具有人们非常喜欢 Python,Ruby 和 Smalltalk 等语言的许多 features,使得 Java 开发人员可以使用 Java-like 语法。”

如果您直接从顶部阅读本章,那么您已经让我们看另一个 example(再次使用 Spring 测试套件中的 example)。

package org.springframework.scripting;

public interface Calculator {

    int add(int x, int y);

}

这是 Groovy 中Calculator接口的 implementation。

// 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>

最后,这是一个小应用程序来执行上面的 configuration。

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。(令人兴奋的例如,呵呵?请记住,目的是为了说明这个概念.请参考动态语言展示项目以获得更复杂的 example,或者确实在第 35.4 节,“场景”之后章节)。

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

通过回调自定义 Groovy objects

GroovyObjectCustomizer接口是一个回调函数,允许您将 hook 的另一个创建逻辑添加到_创建 Groovy-backed bean 的 process 中。对于 example,此接口的 implementations 可以调用任何所需的初始化 method(s),或设置一些默认的 property 值,或指定自定义MetaClass

public interface GroovyObjectCustomizer {

    void customize(GroovyObject goo);
}

Spring Framework 将实例化 Groovy-backed bean 的实例,然后将已创建的GroovyObject传递给指定的GroovyObjectCustomizer(如果已定义)。您可以使用提供的GroovyObject reference 做任何您喜欢的事情:预期自定义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 中对 meta-programming 的完整讨论超出了 Spring reference 手册的范围。请参阅 Groovy reference 手册的相关部分,或在线搜索:有很多关于此 topic 的文章。如果您使用 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 object。

35.3.4 BeanShell beans


BeanShell library 依赖项

Spring 中的 BeanShell 脚本支持要求以下 libraries 位于 application 的 classpath 上。

  • bsh-2.0b4.jar

来自 BeanShell 主页......

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

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

让我们看一个使用 BeanShell-based bean 的完全工作的例子,它实现了本章前面定义的Messenger接口(为方便起见,在下面重复)。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

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

String message;

String getMessage() {
    return message;
}

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

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

<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-based beans 的某些情况,请参阅第 35.4 节,“场景”

35.4 场景

当然,在脚本语言中定义 Spring 托管 beans 会有用的可能场景是多种多样的。本节介绍了 Spring 中动态语言支持的两种可能用例。

35.4.1 脚本 Spring MVC 控制器

可能受益于使用 dynamic-language-backed beans 的一组 classes 类是 Spring MVC 控制器。在纯 Spring MVC applications 中,通过 web application 的导航流程在很大程度上由封装在 Spring MVC 控制器中的 code 决定。由于需要更新 web application 的导航流和其他表示层逻辑以响应支持问题或更改业务需求,因此通过编辑一个或多个动态语言源 files 并查看这些所需的更改可能更容易实现更改立即反映在 running application 的 state 中。

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

为了实现对 dynamic-language-backed beans 的任何更改的自动“拾取”,您将不得不启用“可刷新的 beans”功能。有关此 feature 的完整处理,请参阅这个名为“Refreshable beans”的部分

在下面找到使用 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 进行 application 开发的另一个领域可能会受益于 dynamic-language-backed beans 提供的灵活性。与常规 Java 相比,使用松散类型的动态语言(也可能支持内联正则表达式)表达复杂的验证逻辑可能更容易。

再次,developing validators as dynamic-language-backed beans 允许您通过简单编辑和保存简单的文本文件来更改验证逻辑;任何此类更改将(根据 configuration)自动反映在 running application 的执行中,并且不需要重新启动 application。

请注意,在 order 中实现对 dynamic-language-backed beans 的任何更改的自动“拾取”,您将不得不启用'refreshable beans'feature。有关此 feature 的详细信息,请参阅这个名为“Refreshable beans”的部分

在下面找到使用 Groovy 动态语言实现的 Spring org.springframework.validation.Validator的 example。 (有关Validator interface.)的讨论,请参阅第 9.2 节,“使用 Spring 的 Validator 接口验证”

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 - 建议脚本 beans

可以使用 Spring AOP framework 来建议脚本 beans。 Spring AOP framework 实际上并不知道正在被建议的 bean 可能是脚本 bean,因此您可能正在使用或旨在使用的所有 AOP 用例和功能都将与脚本 beans 一起使用。在建议脚本 beans 时你需要注意的只有一个(小)的东西......你不能使用 class-based 代理,你必须使用interface-based 代理

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

35.5.2 范围界定

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

在下面找到使用scope属性的示例来定义作为原型的 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 中作用域支持的更全面讨论,请参阅第 7 章,IoC 容器中的第 7.5 节,“ Bean 范围”

35.6 更多资源

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

Updated at: 6 months ago
34.6.3. 使用触发器和 SchedulerFactoryBean 连接作业Table of content36. 缓存抽象