动态语言支持

Spring提供了全面的支持,可以使用已经通过动态语言(如Groovy)定义的类和对象与Spring一起使用。这种支持使您可以使用受支持的动态语言编写任意数量的类,并使Spring容器透明地实例化、配置和注入依赖的对象。

Spring的脚本支持主要针对Groovy和BeanShell。除了这些特定支持的语言外,Spring 4.2版本开始支持JSR-223脚本机制,以便与任何支持JSR-223的语言提供者集成,例如JRuby。

您可以在场景中找到动态语言支持可以立即有用的完整工作示例。

第一个示例

本章的主要内容是详细描述动态语言支持。在深入了解动态语言支持的各个方面之前,我们先看一个在动态语言中定义的bean的快速示例。这个第一个bean的动态语言是Groovy。(此示例的基础取自Spring测试套件。如果您想在任何其他支持的语言中看到等效示例,请查看源代码)。

下一个示例展示了Messenger接口,Groovy bean将要实现该接口。请注意,此接口是用普通Java定义的。被注入对Messenger的引用的依赖对象并不知道底层实现是Groovy脚本。以下清单显示了Messenger接口:

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() {
		// 使用注入的Messenger对象...
	}
}

以下示例在Groovy中实现了Messenger接口:

package org.springframework.scripting.groovy

// 导入要实现的Messenger接口(用Java编写)
import org.springframework.scripting.Messenger

// 在Groovy中定义实现,文件名为'Messenger.groovy'
class GroovyMessenger implements Messenger {

	String message
}

要使用自定义的动态语言标签来定义支持动态语言的bean,您需要在Spring XML配置文件的顶部具有XML Schema前言。您还需要将Spring的ApplicationContext实现用作您的IoC容器。使用普通的BeanFactory实现支持动态语言的bean是可以的,但您必须管理Spring内部的管道才能这样做。

有关基于模式的配置的更多信息,请参阅基于XML Schema的配置

最后,以下示例显示了影响将Groovy定义的Messenger实现注入到DefaultBookingService类实例中的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 https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">

	<!-- 这是用于Groovy支持的Messenger实现的bean定义 -->
	<lang:groovy id="messenger" script-source="classpath:Messenger.groovy">
		<lang:property name="message" value="I Can Do The Frug" />
	</lang:groovy>

	<!-- 一个普通的bean,将由Groovy支持的Messenger注入 -->
	<bean id="bookingService" class="x.y.DefaultBookingService">
		<property name="messenger" ref="messenger" />
	</bean>

</beans>

bookingService bean(一个DefaultBookingService)现在可以像往常一样使用其私有的messenger成员变量,因为注入到其中的Messenger实例是一个Messenger实例。这里没有任何特殊的操作,只是普通的Java和普通的Groovy。

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

定义由动态语言支持的Bean

本节描述了如何在任何受支持的动态语言中定义Spring管理的bean。

请注意,本章不试图解释受支持的动态语言的语法和习惯用法。例如,如果您想要使用Groovy来编写应用程序中的某些类,我们假定您已经了解Groovy。如果您需要关于动态语言本身的更多详细信息,请参见本章末尾的更多资源

常见概念

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

  1. 编写动态语言源代码的测试(自然而然地)。

  2. 然后编写动态语言源代码本身。

  3. 通过在XML配置中使用适当的<lang:language/>元素来定义您的动态语言支持的bean(您也可以使用Spring API在程序中定义这些bean,不过您需要查阅源代码以获取如何执行此操作的指导,因为本章不涵盖这种高级配置类型)。请注意,这是一个迭代步骤。您至少需要为每个动态语言源文件定义一个bean定义(尽管多个bean定义可以引用同一个源文件)。

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

<lang:language/>元素

前一节中列出的列表中的最后一步涉及定义动态语言支持的bean定义,每个要配置的bean都需要一个(这与普通JavaBean配置没有区别)。但是,与其指定要由容器实例化和配置的类的完全限定类名不同,您可以使用<lang:language/>元素来定义动态语言支持的bean。

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

  • <lang:groovy/>(Groovy)

  • <lang:bsh/>(BeanShell)

  • <lang:std/>(JSR-223,例如使用JRuby)

可用于配置的确切属性和子元素取决于bean所定义的确切语言(本章后面的语言特定部分详细介绍了这一点)。

可刷新的Bean

在Spring中,动态语言支持最具吸引力的价值之一(也许是唯一的)是“可刷新的bean”功能。

可刷新的bean是由动态语言支持的bean。通过少量配置,动态语言支持的bean可以监视其基础源文件资源的更改,然后在动态语言源文件发生更改时重新加载自身(例如,当您编辑并保存文件系统上的文件时)。

这使您可以将任意数量的动态语言源文件部署为应用程序的一部分,配置Spring容器以创建由动态语言源文件支持的bean(使用本章描述的机制),然后(随后,当需求发生变化或其他外部因素发挥作用时)编辑动态语言源文件,并使其所做的任何更改都反映在由更改的动态语言源文件支持的bean中。无需关闭正在运行的应用程序(或在Web应用程序的情况下重新部署)。修改后的动态语言支持的bean会从更改的动态语言源文件中获取新的状态和逻辑。

此功能默认处于关闭状态。

现在我们可以看一个示例,看看启用可刷新的bean有多么容易。要启用可刷新的bean功能,您必须在bean定义的<lang:language/>元素上指定一个额外的属性。因此,如果我们继续使用本章前面的示例,下面的示例显示了在Spring XML配置中要更改的内容以实现可刷新的bean:

<beans>

	<!-- 由于存在'refresh-check-delay'属性,此bean现在是“可刷新的” -->
	<lang:groovy id="messenger"
			refresh-check-delay="5000" <!-- 每5秒进行一次刷新检查 -->
			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());
		// 暂停执行,让我去修改源文件...
		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() {
		// 更改实现以在消息周围加上引号
		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/>元素表示的内联动态语言源文件(请参见内联动态语言源文件)。此外,它仅适用于可以实际检测到基础源文件更改的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 MVC Controller中添加一个Spring Validator实现。使用内联源只需一刹那的工作。 (请参阅脚本验证器以获取示例。)

在动态语言支持的背景下理解构造函数注入

关于Spring的动态语言支持,有一件非常重要的事情需要注意。即,您目前无法为动态语言支持的bean提供构造函数参数(因此,动态语言支持的bean不支持构造函数注入)。为了使构造函数和属性的特殊处理变得完全清晰,以下代码和配置的混合不起作用:

无法工作的方法
package org.springframework.scripting.groovy

import org.springframework.scripting.Messenger

// 来自文件 'Messenger.groovy'
class GroovyMessenger implements Messenger {

	GroovyMessenger() {}

	// 这个构造函数不可用于构造函数注入
	GroovyMessenger(String message) {
		this.message = message;
	}

	String message

	String anotherMessage
}
<lang:groovy id="badMessenger"
	script-source="classpath:Messenger.groovy">
	<!-- 这个构造函数参数将不会被注入到GroovyMessenger中 -->
	<!-- 实际上,根据模式,这甚至是不允许的 -->
	<constructor-arg value="This will not work" />

	<!-- 只有属性值会被注入到动态语言支持的对象中 -->
	<lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object" />

</lang>

实际上,这个限制并不像一开始看起来那么重要,因为绝大多数开发人员更喜欢setter注入这种注入方式(我们将讨论这是否是一件好事留到另一天)。

Groovy Beans

本节描述了如何在Spring中使用Groovy中定义的bean。

Groovy主页包含以下描述:

“Groovy是Java 2平台的一种敏捷动态语言,具有许多人们在Python、Ruby和Smalltalk等语言中非常喜欢的特性,使这些特性可以通过类似Java的语法提供给使用Java的开发人员。”

如果您从头开始阅读本章节,您已经看到了一个示例,展示了一个Groovy动态语言支持的bean。现在考虑另一个示例(再次使用Spring测试套件中的示例):

package org.springframework.scripting;

public interface Calculator {

	int add(int x, int y);
}

以下示例在Groovy中实现了Calculator接口:

package org.springframework.scripting.groovy

// from the file 'calculator.groovy'
class GroovyCalculator implements Calculator {

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

以下bean定义使用了在Groovy中定义的计算器:

<!-- 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 = ctx.getBean("calculator", Calculator.class);
		System.out.println(calc.add(2, 8));
	}
}

运行上述程序的结果输出为(毫不意外)10。(有关更有趣的示例,请参阅动态语言展示项目以获取更复杂的示例,或者请参阅本章后面的示例场景)。

您不应在一个Groovy源文件中定义多个类。虽然这在Groovy中是完全合法的,但这(可以说)是一个不好的实践。为了保持一致的方法,您应该(在Spring团队的意见中)遵守一个(公共)类对应一个源文件的标准Java约定。

通过回调函数自定义Groovy对象

GroovyObjectCustomizer接口是一个回调函数,允许您将额外的创建逻辑钩入到创建基于Groovy的bean的过程中。例如,该接口的实现可以调用任何必需的初始化方法,设置一些默认属性值,或指定自定义的MetaClass。以下清单显示了GroovyObjectCustomizer接口的定义:

public interface GroovyObjectCustomizer {

	void customize(GroovyObject goo);
}

Spring Framework实例化您的Groovy-backed 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);
	}

}

在Spring参考手册的范围之外,对Groovy中的元编程进行全面讨论。请参阅Groovy参考手册的相关部分或在网上搜索。有很多文章涉及这个主题。实际上,如果您使用Spring命名空间支持,使用GroovyObjectCustomizer非常容易,如下例所示:

<!-- 像定义任何其他bean一样定义GroovyObjectCustomizer -->
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>

	<!-- ... 并通过 'customizer-ref' 属性将其插入到所需的Groovy bean中 -->
	<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"/>
	<!-- 定义GroovyObjectCustomizer(作为内部bean) -->
	<constructor-arg>
		<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
	</constructor-arg>
</bean>

<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
您还可以在与Spring的GroovyObjectCustomizer相同的位置指定Groovy的CompilationCustomizer(例如ImportCustomizer)甚至完整的GroovyCompilerConfiguration对象。此外,您可以在ConfigurableApplicationContext.setClassLoader级别为您的bean设置一个通用的GroovyClassLoader,这也会导致共享的GroovyClassLoader使用,因此在大量脚本化bean的情况下是值得推荐的(避免每个bean使用一个孤立的GroovyClassLoader实例)。

BeanShell Beans

本节描述了如何在Spring中使用BeanShell beans。

BeanShell主页包含以下描述:

BeanShell是一个小型、免费、可嵌入的Java源代码解释器,具有动态语言特性,用Java编写。BeanShell动态运行标准Java语法,并通过常见的脚本便利功能进行扩展,如松散类型、命令和方法闭包,类似于Perl和JavaScript中的功能。

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

现在我们可以展示一个完全工作的示例,使用一个基于BeanShell的bean来实现本章前面定义的Messenger接口。我们再次展示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的一些情况。

场景

定义Spring管理的bean在脚本语言中可能有很多不同的有益场景。本节描述了Spring中动态语言支持的两种可能用例。

脚本化的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

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 jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse

// from the file '/WEB-INF/groovy/FortuneController.groovy'
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>

脚本化的验证器

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

同样,将验证器开发为动态语言支持的bean可以让您通过编辑和保存简单的文本文件来更改验证逻辑。任何这样的更改(取决于配置)会自动反映在运行应用程序的执行中,而不需要重新启动应用程序。

要实现对动态语言支持的bean的任何更改的自动“捕捉”,您必须启用“可刷新的bean”功能。请参阅可刷新的Bean以获取有关此功能的全面和详细的介绍。

以下示例显示了使用Groovy动态语言实现的Spring org.springframework.validation.Validator(有关Validator接口的讨论,请参阅使用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", "不能完全由空格组成。")
	}
}

附加细节

这最后一节包含一些与动态语言支持相关的附加细节。

AOP — 为脚本化Bean提供建议

您可以使用Spring AOP框架为脚本化Bean提供建议。Spring AOP框架实际上不知道正在提供建议的Bean可能是一个脚本化Bean,因此您使用(或打算使用)的所有AOP用例和功能都适用于脚本化Bean。当您为脚本化Bean提供建议时,您不能使用基于类的代理。您必须使用基于接口的代理

您不仅限于为脚本化Bean提供建议。您还可以在支持的动态语言中编写方面本身,并使用这些Bean来为其他Spring Bean提供建议。不过,这确实是对动态语言支持的高级使用。

作用域

如果不是显而易见的话,脚本化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 https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/lang https://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>

请参阅Bean作用域中的IoC容器,以全面讨论Spring Framework中的作用域支持。

lang XML模式

Spring XML配置中的lang元素处理将以动态语言(如Groovy或BeanShell)编写的对象公开为Spring容器中的Bean。

这些元素(以及动态语言支持)在动态语言支持中得到了全面覆盖。请查看该部分,以获取有关此支持和lang元素的完整详细信息。

要使用lang模式中的元素,您需要在Spring XML配置文件的顶部具有以下前言。以下片段中的文本引用了正确的模式,以便lang命名空间中的标记对您可用:

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">

	<!-- 这里是bean定义 -->

</beans>

更多资源

以下链接提供了有关本章中引用的各种动态语言的更多资源: