动态语言支持
Spring 为使用动态语言(如 Groovy)定义的类和对象提供全面支持。此支持允许您使用受支持的动态语言编写任意数量的类,并让 Spring 容器透明地实例化、配置和依赖注入结果对象。
Spring 的脚本支持主要针对 Groovy 和 BeanShell。除了这些专门支持的语言之外,JSR-223 脚本机制还支持与任何支持 JSR-223 的语言提供程序(如 Spring 4.2)集成,例如 JRuby。
您可以在 场景 中找到此动态语言支持可以立即派上用场的完整工作示例。
第一个示例
本章的大部分内容详细介绍了动态语言支持。在深入了解动态语言支持的来龙去脉之前,我们先来看一个用动态语言定义的 bean 的快速示例。此第一个 bean 的动态语言是 Groovy。(此示例的基础取自 Spring 测试套件。如果您想查看任何其他受支持语言中的等效示例,请查看源代码)。
以下示例显示了 Groovy bean 将要实现的 Messenger
接口。请注意,此接口是在纯 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() {
// use the injected Messenger object...
}
}
以下示例在 Groovy 中实现了 Messenger
接口
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 in file 'Messenger.groovy'
class GroovyMessenger implements Messenger {
String message
}
要使用自定义动态语言标记来定义动态语言支持的 bean,您需要在 Spring XML 配置文件的顶部放置 XML 模式前导部分。您还需要使用 Spring 有关基于模式的配置的更多信息,请参阅 基于 XML 模式的配置。 |
最后,以下示例显示了影响将 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">
<!-- 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 代码段不言自明,但如果它不是这样,也不必过分担心。继续阅读,深入了解前面配置的来龙去脉。
定义由动态语言支持的 bean
本节详细描述如何在任何受支持的动态语言中定义 Spring 管理的 Bean。
请注意,本章不会尝试解释受支持动态语言的语法和习惯用法。例如,如果你想使用 Groovy 来编写应用程序中的某些类,我们假设你已经了解 Groovy。如果你需要有关动态语言本身的更多详细信息,请参阅本章末尾的其他资源。
通用概念
使用动态语言支持的 Bean 涉及以下步骤
-
编写动态语言源代码的测试(自然地)。
-
然后编写动态语言源代码本身。
-
通过在 XML 配置中使用适当的
<lang:language/>
元素来定义你的动态语言支持的 Bean(你可以使用 Spring API 以编程方式定义此类 Bean,尽管你必须查阅源代码才能了解如何执行此操作,因为本章不涵盖此类高级配置)。请注意,这是一个迭代步骤。你至少需要为每个动态语言源文件定义一个 Bean 定义(尽管多个 Bean 定义可以引用同一个源文件)。
前两个步骤(测试和编写动态语言源文件)超出了本章的范围。请参阅你选择的动态语言的语言规范和参考手册,并着手开发你的动态语言源文件。不过,你首先要阅读本章的其余部分,因为 Spring 的动态语言支持确实对你的动态语言源文件的内容做了一些(小)假设。
<lang:language/> 元素
上一节中列表中的最后一步涉及定义动态语言支持的 Bean 定义,每个 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>
<!-- 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/>
元素符号定义的动态语言源文件(请参阅 内联动态语言源文件)。此外,它仅适用于底层源文件可以实际检测到更改的 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
。使用内联源只需片刻即可完成此操作。(有关此类示例,请参见 脚本验证器。)
了解动态语言支持的 Bean 中的构造函数注入
关于 Spring 的动态语言支持,有一件非常重要的事情需要注意。即,你不能(目前)向动态语言支持的 Bean 提供构造函数参数(因此,动态语言支持的 Bean 不支持构造函数注入)。为了让构造函数和属性的这种特殊处理 100% 清晰,以下代码和配置的混合不起作用
package org.springframework.scripting.groovy
import org.springframework.scripting.Messenger
// from the file 'Messenger.groovy'
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>
实际上,这个限制并不像最初看起来那么重要,因为 setter 注入是绝大多数开发人员偏爱的注入样式(我们留待以后讨论这是否是件好事)。
Groovy Bean
本节介绍如何在 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 团队的意见)遵守每个源文件一个(public)类的标准 Java 惯例。
使用回调自定义 Groovy 对象
GroovyObjectCustomizer
接口是一个回调,它允许您将其他创建逻辑挂接到创建 Groovy 支持的 bean 的过程中。例如,此接口的实现可以调用任何必需的初始化方法,设置一些默认属性值,或指定一个自定义的 MetaClass
。以下清单显示了 GroovyObjectCustomizer
接口定义
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"/>
您还可以指定一个 Groovy CompilationCustomizer (例如一个 ImportCustomizer )甚至一个完整的 Groovy CompilerConfiguration 对象,与 Spring 的 GroovyObjectCustomizer 在相同的位置。此外,您可以在 ConfigurableApplicationContext.setClassLoader 级别为您的 bean 设置一个通用的 GroovyClassLoader ,并进行自定义配置;这也导致共享 GroovyClassLoader 的使用,因此在大量脚本化 bean 的情况下建议使用(避免每个 bean 出现一个孤立的 GroovyClassLoader 实例)。
|
BeanShell Bean
本节介绍如何在 Spring 中使用 BeanShell bean。
BeanShell 主页 包含以下说明
BeanShell is a small, free, embeddable Java source interpreter with dynamic language features, written in Java. BeanShell dynamically runs standard Java syntax and extends it with common scripting conveniences such as loose types, commands, and method closures like those in Perl and JavaScript.
与 Groovy 相比,BeanShell 支持的 bean 定义需要一些(少量)的其他配置。Spring 中 BeanShell 动态语言支持的实现很有趣,因为 Spring 创建了一个 JDK 动态代理,它实现了 <lang:bsh>
元素的 script-interfaces
属性值中指定的所有接口(这就是为什么您必须在属性值中至少提供一个接口,并且在使用 BeanShell 支持的 bean 时,必须对接口进行编程)。这意味着对 BeanShell 支持的对象的每个方法调用都会通过 JDK 动态代理调用机制进行。
现在,我们可以展示一个完全有效的示例,演示如何使用基于 BeanShell 的 bean,该 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。 |
以下示例显示了一个 Spring org.springframework.validation.Validator
,它使用 Groovy 动态语言实现(有关 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", "Cannot be composed wholly of whitespace.")
}
}
其他详细信息
本节最后包含与动态语言支持相关的其他一些详细信息。
AOP — 建议脚本化 Bean
您可以使用 Spring AOP 框架来建议脚本化 Bean。Spring AOP 框架实际上并不知道正在建议的 Bean 可能是一个脚本化 Bean,因此您使用的(或打算使用)的所有 AOP 用例和功能都适用于脚本化 Bean。当您建议脚本化 Bean 时,您不能使用基于类的代理。您必须使用基于接口的代理。
您并不局限于建议脚本化 Bean。您还可以在受支持的动态语言中编写方面本身,并使用此类 Bean 来建议其他 Spring Bean。但这实际上是动态语言支持的高级用法。
作用域
如果它不是显而易见的,脚本化 Bean 可以像任何其他 Bean 一样具有作用域。各种 <lang:language/>
元素上的 scope
属性允许您控制底层脚本化 Bean 的作用域,就像它对常规 Bean 所做的那样。(默认作用域是单例,就像“常规”Bean 一样。)
以下示例使用 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 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>
lang
XML 架构
Spring XML 配置中的 lang
元素用于将以动态语言(例如 Groovy 或 BeanShell)编写的对象作为 Bean 公开到 Spring 容器中。
这些元素(和动态语言支持)在 动态语言支持 中得到了全面介绍。请参阅该部分以全面了解此支持和 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 definitions here -->
</beans>