Spring 5.0.3.RELEASE中的 Kotlin 语言等支持

Spring 5.0.3.RELEASE中的 Kotlin 语言支持

https://docs.spring.io/spring/docs/current/spring-framework-reference/languages.html

返回目录

1. Kotlin

Kotlin是静态类型语言定位的JVM(以及其他平台),它允许写简洁而优雅的代码,同时提供很好 的互操作性与Java编写的现有的库。

Spring框架提供了 Kotlin 一等支持,允许Kotlin 程序员无缝使用 Spring框架。

1.1。要求

弹簧框架支持科特林1.1+并且需要 kotlin-stdlib (或它的一个kotlin-stdlib-jre7 / kotlin-stdlib-jre8变体)和kotlin-reflect 对存在于类路径。他们在默认情况下,如果一个自举在科特林项目提供 start.spring.io

1.2。扩展

科特林扩展提供到具有附加功能扩展现有的类的能力。Spring框架科特林的API利用这些扩展到新的科特林具体的便利添加到现有的Spring的API。

Spring框架KDOC API列表和文档的所有科特林扩展和提供的DSL。

科特林扩展需要 import 使用。例如,在GenericApplicationContext.registerBean如果科特林扩展将只能import org.springframework.context.support.registerBean

例如,科特林具体化类型参数 提供JVM一种变通方法泛型类型擦除,和Spring框架提供了一些扩展至利用此功能优势。这样可以更好的科特林API RestTemplate,新WebClient的春天WebFlux和各种其他的API。

像 Reactor 和 Spring Data 数据的其他库还提供Kotlin 扩展 API。

要检索列表Foo中的Java对象,人们通常会写:

Flux<User> users  = client.get().retrieve().bodyToFlux(User.class)

虽然与科特林和Spring框架的扩展,一个是能写:

val users = client.get().retrieve().bodyToFlux<User>()
// or (both are equivalent)
val users : Flux<User> = client.get().retrieve().bodyToFlux()

正如在Java中,users在科特林是强类型,但Kotlin 的聪明的类型推断允许更短的语法。

1.3。空安全

一个科特林的主要特点是空的安全 -这干净地涉及null在编译的时候,而不是撞到著名的值 NullPointerException在运行时。

这使得应用更加安全,通过空性的声明,并表示“有值或没有值” 的语义无需支付包装成本Optional。参考: http://www.baeldung.com/kotlin-null-safety

虽然Java不允许一个来表达它的类型系统空安全,Spring框架现在提供整个Spring框架API的空安全 通过的声明工装友好注释org.springframework.lang包。默认情况下,在科特林使用的Java API类型被认为是 平台类型 为其中空检查是放松。 对于JSR 305个注解科特林支持 +春空性的注释为整个Spring框架API来开发科特林空安全,与涉及的优势null在编译时的相关问题。

像 Reactor 反应堆或 Spring Data 库提供空安全的API利用此功能。

JSR 305检查可以通过添加被配置-Xjsr305具有以下选项的编译标志:

-Xjsr305={strict|warn|ignore}

对于科特林版本1.1.50+,默认行为是一样的来-Xjsr305=warn。该strict值是必需的。

泛型类型参数,可变参数和数组元素为空性尚不支持,而应在未来版本中,看到这个dicussion 达最新信息。

1.4。类和接口

弹簧框架支持各种科特林构造等经由主构造实例科特林类,不可变的类数据绑定和具有默认值的功能的可选参数。

科特林参数名通过专用的认可KotlinReflectionParameterNameDiscoverer ,其允许,而不需要在Java 8找到接口方法的参数名称-parameters 编译时启用编译器标志。

序列化/反序列化JSON数据的 jackson-module-kotlin在类路径发现,如果没有 jackson-module-kotlin 被检测到,有警告消息。

1.5。注解

Spring框架与 科特林空安全

例如:确定是否一个HTTP参数是必须的,而无需显式定义required属性。这意味着@RequestParam name: String?如不是必需的,反之将被视为@RequestParam name: String为被需要。此功能还支持在 Spring Messaging
消息@Header注解。

以类似的方式,Spring的bean注射@Autowired@Inject使用该信息来确定是否需要与否的bean。

@Autowired lateinit var foo: Foo

暗示Bean Foo必须在应用程序上下文中进行注册,而

@Autowired lateinit var foo: Foo?

如果这样的Bean不存在不会引发错误。

如果您使用的是带班Bean验证 主构造属性,确保使用 注释使用现场的目标 在描述这个堆栈溢出响应

1.6。bean定义DSL

弹簧框架5介绍了使用的lambda作为替代XML或JavaConfig(功能性的方式来登记豆一种新的方式@Configuration@Bean)。简单地说,它能够与作为一个拉姆达注册豆FactoryBean。这个机制是非常有效的,因为它不需要任何的反射或CGLIB代理。

在Java中,一个例如可以写:

    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean(Foo.class);
    context.registerBean(Bar.class, () -> new Bar(context.getBean(Foo.class))
);

虽然在科特林与具体化类型参数和GenericApplicationContext 科特林扩展一个可以代替简单的写:

val context = GenericApplicationContext().apply {
    registerBean<Foo>()
    registerBean { Bar(it.getBean<Foo>()) }
}

为了让更声明的方式和更清晰的语法,Spring框架提供了科特林bean定义DSL 它声明的ApplicationContextInitializer通过一个干净的声明API,它使一个处理配置文件和Environment自定义豆是如何注册。

fun beans() = beans {
    bean<UserHandler>()
    bean<Routes>()
    bean<WebHandler>("webHandler") {
        RouterFunctions.toWebHandler(
            ref<Routes>().router(),
            HandlerStrategies.builder().viewResolver(ref()).build()
        )
    }
    bean("messageSource") {
        ReloadableResourceBundleMessageSource().apply {
            setBasename("messages")
            setDefaultEncoding("UTF-8")
        }
    }
    bean {
        val prefix = "classpath:/templates/"
        val suffix = ".mustache"
        val loader = MustacheResourceTemplateLoader(prefix, suffix)
        MustacheViewResolver(Mustache.compiler().withLoader(loader)).apply {
            setPrefix(prefix)
            setSuffix(suffix)
        }
    }
    profile("foo") {
        bean<Foo>()
    }
}

在这个例子中,bean<Routes>()使用由自动装配构造和ref<Routes>() 为捷径applicationContext.getBean(Routes::class.java)

beans()则函数可用于注册应用程序上下文豆。

val context = GenericApplicationContext().apply {
    beans().initialize(this)
    refresh()
}

这DSL是程序化,从而它允许豆的定制注册逻辑经由if表达式,一个for环或任何其他科特林构建体。

Beans.kt 为一个具体的例子。

春季启动基于Java的配置,并 没有提供的功能bean定义中还支持,但一个实验可以通过Spring Boot的使用功能bean定义ApplicationContextInitializer的支持,看到这个堆栈溢出的答案 的详细信息和先进的最新信息。

1.7。Kotlin Web

1.7.1。WebFlux功能DSL

Spring框架现在使用了 科特林路由DSL ,使人们得以充分利用WebFlux功能API编写干净地道科特林代码:

router {
    accept(TEXT_HTML).nest {
        GET("/") { ok().render("index") }
        GET("/sse") { ok().render("sse") }
        GET("/users", userHandler::findAllView)
    }
    "/api".nest {
        accept(APPLICATION_JSON).nest {
            GET("/users", userHandler::findAll)
        }
        accept(TEXT_EVENT_STREAM).nest {
            GET("/users", userHandler::stream)
        }
    }
    resources("/**", ClassPathResource("static/"))
}

这DSL是程序化,从而它允许豆的定制注册逻辑经由if表达式,一个for环或任何其他科特林构建体。当路由需要根据动态数据进行登记(例如,从数据库中),其可以是有用的。

MIXIT项目路线 的一个具体的例子。

1.7.2。科特林脚本模板

对于4.3版本,Spring框架提供了一个 ScriptTemplateView 渲染使用的脚本引擎,支持模板 JSR-223。Spring框架5走得更远通过扩展这个功能WebFlux并支持 国际化和嵌套模板

科特林提供类似的支持,并允许根据科特林模板渲染,看到 这次提交的详细资料。

这使得一些有趣的使用情况-比如使用书写类型安全模板 kotlinx.html DSL,或者干脆利用科特林多String用插。

这可以允许一个写在支持的IDE完全自动完成和重构支持科特林模板:

import io.spring.demo.*

"""
${include("header")}
<h1>${i18n("title")}</h1>
<ul>
${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""

科特林脚本,模板化示例项目的更多细节。

1.8。在科特林的Spring项目

本节提供了一些具体的提示和建议值得科特林开发Spring项目时,了解的重点。

1.8.1。最终默认

默认情况下,在科特林所有的类都是final。在open一类调节剂是Java的相反final:它允许别人从这个类继承。这也适用于成员函数,因为它们需要被标记为open被覆盖。

虽然科特林的JVM友好的设计通常与春季摩擦,这个特定的科特林功能可以防止应用程序无法启动,如果这一点不考虑拍摄。这是因为春豆通常由CGLIB代理-比如@Configuration类-这需要在运行时因技术原因被继承。解决方法是添加一个open对CGLIB代理的Spring bean如每个类和成员函数关键字@Configuration类,很快就会变得疼痛,是对保持代码简洁和可预测的科特林原则。

幸运的是,现在科特林提供了一个 kotlin-spring 插件,一个预配置版本kotlin-allopen的插件,自动打开了注解的类型或元注解与以下注释的一个类及其成员函数:

  • @Component

  • @Async

  • @Transactional

  • @Cacheable

元注释支持意味着标注了类型@Configuration@Controller@RestController@Service或者@Repository因为这些注解元标注有自动打开@Component

start.spring.io使得它在默认情况下,所以在实践中你就可以写你的科特林豆没有任何额外的open关键词,像Java中。

1.8.2。使用持久不变的类实例

在科特林,这是非常方便和最佳实践是主构造内声明只读属性,如下面的例子:

class Person(val name: String, val age: Int)

您可以选择添加data关键字 ,使编译器自动获得来自主构造声明的所有属性的成员如下:

  • 等于()/ hashCode()方法对

  • toString()将形式的 “用户(名称=约翰,年龄= 42)”

  • 对应于该属性在其声明的顺序componentN()函数

  • 副本()函数

这可以很容易地改变,即使个别属性Person的属性是只读的:

data class Person(val name: String, val age: Int)

val jack = Person(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

常见的持久化技术,如JPA需要一个默认的构造,防止这种设计。幸运的是,现在这个解决办法 “默认构造函数地狱” ,因为科特林提供了科特林,JPA 插件,它生成与JPA注解类合成的无参数的构造函数。

如果您需要利用这种机制对其他持久化技术,你可以配置科特林-noarg 插件。

|

作为凯释放列车,春季数据支持科特林不可改变类实例和不需要kotlin-noarg如果模块利用弹簧数据对象映射(如使用的MongoDB,Redis的,卡桑德拉等)插件。

1.8.3。注入依赖

我们的建议是尝试并有利于构造注射val只读(和非空的可能时) 的属性

@Component
class YourBean(
    private val mongoTemplate: MongoTemplate,
    private val solrClient: SolrClient
)

由于Spring框架4.3的,有一个构造函数的类都有自己的参数自动自动连接,这就是为什么没有必要明确地@Autowired constructor在上面显示的例子。

如果一个人真正需要使用字段注入,使用lateinit var结构,即,

@Component
class YourBean {

    @Autowired
    lateinit var mongoTemplate: MongoTemplate

    @Autowired
    lateinit var solrClient: SolrClient
}

1.8.4。注入配置属性

在Java中,一个可以使用注释等注入配置属性@Value("${property}"),但是在科特林$是用于保留字符串内插

因此,如果希望使用@Value在科特林注释,该$ 角色将需要通过写逃脱@Value("\${property}")

作为替代方案,能够以自定义属性通过声明以下配置豆占位前缀:

@Bean
fun propertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
    setPlaceholderPrefix("%{")
}

现有的代码(如Spring引导致动器或@LocalServerPort一个使用)${…​}的语法,可以用配置Bean进行定制,例如,如下所示:

@Bean
fun kotlinPropertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
    setPlaceholderPrefix("%{")
    setIgnoreUnresolvablePlaceholders(true)
}

@Bean
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()

如果使用Spring Boot,那么 可以使用@ConfigurationProperties 替代@Value注释,但是目前这只能用于可空var 属性(这远非理想),因为不支持由构造函数初始化的不可变类。查看有关@ConfigurationProperties绑定不可变POJO@ConfigurationProperties绑定接口 的更多详细信息。

1.8.5。注释阵列属性

Kotlin注释大部分与Java类似,但是在Spring中广泛使用的数组属性 - 行为不同。正如Kotlin文档中所述, 与其他属性不同,value属性名称可以省略,当它是数组属性时,它将被指定为vararg参数。

要明白这意味着什么,让我们@RequestMapping,这是最广泛使用Spring注解作为例子之一。此Java注释声明如下:

public @interface RequestMapping {

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};

    // ...
}

典型的使用情况@RequestMapping是将处理程序方法映射到一个特定的路径和方法。在Java中,有可能指定注释阵列属性一个单一的值,它将被自动转换成一个数组。

这就是为什么一个可以写入 @RequestMapping(value = "/foo", method = RequestMethod.GET)@RequestMapping(path = "/foo", method = RequestMethod.GET)

然而,在科特林,一个将不得不写@RequestMapping("/foo", method = arrayOf(RequestMethod.GET))。使用变体path是不推荐,因为它需要被写入 @RequestMapping(path = arrayOf("/foo"), method = arrayOf(RequestMethod.GET))

一种用于此特定解决方法method属性(最常见的一种)是使用快捷方式注释诸如@GetMapping@PostMapping

提醒:如果@RequestMapping method没有指定属性,所有的HTTP方法将被匹配,不仅GET方法。

改善科特林注释阵列属性的语法和一致性中讨论 此科特林语言的设计问题

1.8.6。测试

每类的生命周期

科特林允许指定反引号之间有意义的测试函数名,并作为JUnit的5个科特林测试类可以使用@TestInstance(TestInstance.Lifecycle.PER_CLASS) 注释以使测试类的单个实例,其允许使用@BeforeAll@AfterAll 在非静态方法的注解,这是一个良好的配合对于科特林。

现在可以更改默认行为,PER_CLASS多亏了 junit-platform.properties一个文件junit.jupiter.testinstance.lifecycle.default = per_class属性。

class IntegrationTests {

  val application = Application(8181)
  val client = WebClient.create("http://localhost:8181")

  @BeforeAll
  fun beforeAll() {
    application.start()
  }

  @Test
  fun `Find all users on HTML page`() {
    client.get().uri("/users")
        .accept(TEXT_HTML)
        .retrieve()
        .bodyToMono<String>()
        .test()
        .expectNextMatches { it.contains("Foo") }
        .verifyComplete()
  }

  @AfterAll
  fun afterAll() {
    application.stop()
  }
}
规范类测试

它可以使用JUnit 5和科特林创建规范样测试。

class SpecificationLikeTests {

  @Nested
  @DisplayName("a calculator")
  inner class Calculator {
     val calculator = SampleCalculator()

     @Test
     fun `should return the result of adding the first number to the second number`() {
        val sum = calculator.sum(2, 4)
        assertEquals(6, sum)
     }

     @Test
     fun `should return the result of subtracting the second number from the first number`() {
        val subtract = calculator.subtract(4, 2)
        assertEquals(2, subtract)
     }
  }
}
WebTestClient 在科特林类型推断问题

WebTestClient不可用但在科特林由于 类型推理问题预计将被固定为1.3科特林的。你可以看 SPR-16057以获取最新的最新信息。同时,所提出的替代方案是直接用WebClient其反应堆和Spring科特林扩展到嵌入式WebFlux服务器上进行集成测试。

1.9。入门

1.9.1。start.spring.io

开始在科特林一个新的Spring框架5项目最简单的方法是创建一个新的春天启动二期工程start.spring.io

也可以作为描述的创建一个独立的WebFlux项目 这个博客帖子

1.9.2。选择Web味道

Spring框架现在带有2个不同的网络栈:Spring MVC的春天WebFlux

如果想要创建将处理时延的应用,长期连接,流方案或干脆如果想使用网络功能科特林DSL建议春季WebFlux。

对于其他用途的情况下,特别是如果你使用的是封锁的技术,如JPA,Spring MVC和它的基于注解的编程模型是一个完全有效的,并完全支持选择。

1.10。资源

1.10.1。博客文章

1.10.2。例子

1.10.3。教程

1.10.4。问题

下面是有关的未决春+科特林支持问题的列表。

Spring框架
春季启动
科特林

2.阿帕奇的Groovy

Groovy是一种功能强大,可选类型和动态语言,与静态打字和静态编译能力。它提供了一个简洁的语法,并与任何现有的Java应用程序顺利集成。

Spring框架提供了一个专用ApplicationContext,支持基于Groovy的bean定义DSL。有关详细信息,请参阅 Groovy的bean定义DSL

对Groovy,包括用Groovy,刷新脚本豆豆,更加进一步的支持是在明年的部分提供动态语言的支持

3.动态语言支持

3.1。介绍

弹簧2.0引入了使用类和已使用与弹簧的动态语言(例如JRuby)定义的对象的全面支持。这种支持允许你写任意数量的类别中支持动态语言,并有Spring容器透明的实例化,配置,依赖注入其最终对象。

目前支持的动态语言是:

  • JRuby的1.5+

  • Groovy的1.8+

  • BeanShell的2.0

为什么只有这些语言?

支持的语言被选中,因为一)语言有很多Java企业社区牵引,B)不要求被其他语言的那个加入这一支持时作出的,而C) Spring开发者最熟悉它们。

充分的地方这个动态语言的支持可立即有用的工作实例描述场景

3.2。第一个例子

本章的大部分内容的关注点都在详细地描述动态语言的支持。潜入所有的插件和动态语言支持的细节之前,让我们来看看在动态语言定义的bean的一个简单的例子。第一个bean的动态语言Groovy的是(这个例子的基础上,从Spring的测试套件采取的,所以如果你想看到的任何其他支持的语言相同的例子,看看源代码)。

找到下面Messenger的Groovy的豆将要实现接口,并注意该接口是使用纯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...
    }

}

下面是一个实现Messenger在Groovy接口。

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

| |

要使用自定义动态语言标签来定义动态语言支持豆,你需要在Spring XML配置文件的顶部相应的XML Schema。你还需要使用弹簧ApplicationContext实现作为IoC容器。使用动态语言的支持豆,一个普通BeanFactory 的实施是支持的,但你必须管理Spring内部的管道这样做。

有关基于模式的配置的详细信息,请参阅基于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豆(一DefaultBookingService)现在可以使用其私有 messenger成员变量作为正常的,因为Messenger这是注射到它的实例就是一个Messenger实例。没有什么特别的地方,只是简单的Java和Groovy的。

希望上面的XML片段是不言自明,但不要过分担心,如果事实并非如此。保持阅读在原因和上述结构的wherefores深入的细节。

3.3。定义由动态语言支持的bean

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

请注意,本章不试图解释的语法和支持的动态语言的成语。例如,如果你想使用Groovy来编写某些应用程序中的类,那么假设是你已经知道的Groovy。如果您需要了解和动态语言本身有关的更多细节,请参考更多的资源在这一章的结尾。

3.3.1。常见的概念

是涉及使用动态语言实现的bean的步骤如下:

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

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

  • 定义使用适当的动态语言实现的bean <lang:language/> 的XML配置元素(你当然可以定义这样的豆使用Spring API -尽管你要咨询的源代码就如何做到这一点,因为这类型的方向先进的配置不本章中)。请注意,这是一个反复的一步。你需要每一个动态语言的源文件至少一个bean定义(同一个动态语言的源文件当然可以在多个bean定义中引用)。

前两步(测试并编写动态语言源文件)超出了本章的范围。请参考语言规范和/或参考手册您选择的动态语言,并与显影动态语言的源文件上裂纹。你首先要阅读本章的其余部分,如Spring的动态语言支持确实让你的动态语言的源文件的内容有一些(小)的假设。

所述的<lang:language />元素

最后一步是定义动态语言支持的bean定义,一个用于您要配置(这是不正常的JavaBean配置不同)的每个bean。然而,而不是指定要被实例化和容器配置的类的完全限定类名的,你可以使用 <lang:language/>元素来定义动态语言实现的bean。

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

  • <lang:groovy/> (Groovy的)

  • <lang:bsh/> (BeanShell的)

  • <lang:std/> (JSR-223)

可用于配置的确切属性和子元素正是依赖于豆已经(以下特定语言有关的章节会揭示全部内幕)中定义的语言。

刷新豆

其中一个(如果没有)Spring对动态语言支持的最引人注目的价值在于增加了对“刷新豆”功能。

可刷新的豆是一个动态语言支持的豆与配置少量,动态语言支持的bean可以监控底层源文件资源更改,然后当动态语言源文件被改变重新加载本身(例如开发者编辑并保存更改文件在文件系统)。

这使得开发人员以后部署任意数量的动态语言的源文件作为应用程序的一部分,配置Spring容器来创建动态语言源文件支持(使用本章描述的机制)豆,然后根据需求的变化或一些外部因素开始发挥作用,只需编辑动态语言源文件,并将它们会反射由已改变的动态语言源文件为bean的变化。有没有必要关闭正在运行的应用程序(或Web应用程序的情况下重新部署)。动态语言支持的bean能够自我修正,新的状态和逻辑从改变的动态语言源文件。

| |

请注意,此功能是关闭默认。

|

让我们来看一个例子,看看它是多么容易开始使用刷新豆。要打开的刷新豆功能,你只需要明确指定 一个上附加属性<lang:language/>的bean定义的元素。因此,如果我们坚持的例子从本章前面,这里就是我们将在Spring XML配置变化来实现刷新豆:

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

这就是所有你需要做的。在'refresh-check-delay'所定义的属性 'messenger'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());
    }
}

假设那么,在这个例子的目的,是对所有呼叫 getMessage()的方法Messenger的实现必须改变,以使得该消息被引号括起来。下面是改变我(作者)做出的 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
    }
}

当程序执行时,在输入暂停之前的输出将是我可以做Frug。源文件做出更改并保存后,程序继续执行,调用的结果getMessage()对动态语言支持的方法Messenger实施将是“我可以做Frug” (注意附加报价列入) 。

要明白,修改脚本将是很重要的不是如果的窗口内发生的变化触发刷新'refresh-check-delay'值。要明白,修改剧本是同样重要的是没有真正“拿起”,直到一个方法被调用的动态语言实现的bean。只有当一个方法被调用的动态语言实现的bean,它检查是否它的底层脚本源发生了变化。有关刷新脚本(如遇到编译错误,或找到脚本文件已被删除)的任何异常都会导致致命的异常传播到调用代码。

上述的可刷新的豆的行为并没有适用于使用所定义的动态语言的源文件<lang:inline-script/>元素符号(参照 内置动态语言的源文件)。此外,它适用于其中改变底层源文件实际上可以检测豆; 例如,通过检查该文件系统上的存在的动态语言的源文件的最后修改日期代码。

内置动态语言源文件

动态语言的支持也能满足那些直接嵌入在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%清楚的这种特殊处理的利益,代码和配置以下混合物将无法正常工作。

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

在实践中,这种限制是不是第一次出现,因为setter注入是通过反正绝大多数的开发商青睐注射风格显著(让我们离开讨论是否这是一件好事,以另一天)。

3.3.2。的Groovy bean

Groovy依赖库

在Spring支持的Groovy脚本需要以下库在你的应用程序的classpath。

  • groovy-1.8.jar

  • asm-3.2.jar

  • antlr-2.7.7.jar

来自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);

}

下面是一个实现Calculator在Groovy接口。

// 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。(令人兴奋的例子,是吧?记住我们的目的是为了说明这个概念,请参考动态语言的示例项目更复杂的例子,或甚 方案本章后面)。

你是很重要的明确Groovy源文件中不止一个类。虽然这是完全合法的Groovy中,它是(可以说)一个不好的做法:在一个一致的方法的利益,你应该(在笔者的意见)尊重每个源文件中的一个(public)类标准Java约定。

通过回调定制Groovy对象

GroovyObjectCustomizer接口是一个回调,它允许你将附属的创建逻辑添加到创建一个Groovy的bean的过程。例如,这个接口的实现可以调用任何所需的初始化方法(一个或多个),或设置某些默认属性值,或指定自定义MetaClass

public interface GroovyObjectCustomizer {

    void customize(GroovyObject goo);
}

Spring框架将实例你的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);
    }

}

元编程的完整讨论Groovy已经超出了本参考手册的范围。查阅Groovy参考手册的相关部分,或者做一个网上搜索:有大量的关于这方面的文章。其实利用的GroovyObjectCustomizer是容易的,如果你使用的是Spring命名空间支持。

<!-- 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框架4.3.3,你也可以指定一个Groovy CompilationCustomizer (如ImportCustomizer),或者甚至是完全的Groovy CompilerConfiguration在同一个地方作为春天的对象GroovyObjectCustomizer

|

3.3.3。BeanShell的豆

BeanShell的依赖库

在Spring支持的BeanShell脚本需要以下库在你的应用程序的classpath。

  • bsh-2.0b4.jar

来自BeanShell官方网页...

BeanShell的是一个小的,自由的,可嵌入的Java源代码解释器具有动态语言功能,用Java编写的。BeanShell动态执行标准的Java语法和与常见的脚本的便利,如松散类型延伸它,命令和方法封闭像那些在Perl和JavaScript 。

和Groovy相比,基于BeanShell的bean定义需要一些(小)的额外配置。Spring对BeanShell动态语言支持的实现是有趣的地方是这样的:春创建了JDK动态代理实现在指定的接口'script-interfaces' 中的属性值<lang:bsh>元素(这就是为什么你必须至少提供一个接口中的属性,以及使用基于BeanShell的豆时(相应地)程序接口)的值。这意味着,基于BeanShell对象的每一个方法调用正在经历的JDK动态代理调用机制。

让我们来看看使用它实现了一个基于BeanShell的bean的可工作的完整例子Messenger这是本章(下面重复为了您的方便)在早期定义的接口。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

这里是的BeanShell的“执行”(该术语的随意) Messenger接口。

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。

3.4。方案

这里定义Spring管理豆脚本语言可能出现的情况将是有益的,当然,多种多样的。本节介绍Spring对动态语言支持两种可能的使用情况。

3.4.1。脚本Spring MVC控制器

那可以使用动态语言支持的bean得益于有一组类是Spring MVC的控制器。在纯Spring MVC应用中,导航流程通过web应用是通过在Spring MVC的控制器内包封的码来确定在很大程度上。作为一个Web应用程序的导航流程和其他表示层逻辑需要进行更新,以应对支持问题或变化的业务需求,它很可能会更容易通过编辑一个或多个动态语言源文件,看到那些进行任何此类要求的变化变化被立即反映在正在运行的应用程序的状态。

请记住,在由项目支持的轻量级架构模型如Spring,你通常目标是有一个非常薄的表示层,所有的应用程序的肉香业务逻辑被包含在域和服务层的类。开发Spring MVC控制器动态语言实现的bean允许你通过简单的编辑保存文本文件就可以修改表示层逻辑; 这样的动态语言的源文件的任何更改将(取决于配置)在由动态语言的源文件备份豆自动反映。

| |

为了实现的任何变化动态语言支持的bean这一自动“皮卡”,你将不得不启用'refreshable bean的功能。见 刷新的豆子一个完整的处理这个功能的。

|

请看下面的一个例子org.springframework.web.servlet.mvc.Controller是使用Groovy动态语言实现。

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

3.4.2。脚本校验

可通过动态语言支持的bean提供的灵活性,有利于应用开发与Spring的另一个领域是验证。它可能 是更容易使用松散类型的动态语言(也可能有内置正则表达式的支持),而不是常规的Java来表达复杂的验证逻辑。

再次,制定验证动态语言实现的bean可以让你通过简单的编辑和保存一个简单的文本文件来改变验证逻辑; 任何这样的改变将(取决于配置)自动反映在运行中的应用程序的执行并且不需要应用程序的重新启动。

| |

请注意,为了实现的任何变化动态语言支持的bean自动“皮卡”,你将不得不启用“刷新豆”功能。见刷新的豆子一个完整和详细的治疗此功能。

|

下面为一个春天的一个例子org.springframework.validation.Validator是使用Groovy动态语言实现。(请参阅使用Spring的Validator接口验证的的讨论 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.")
    }

}

3.5。位和鲍勃

最后一节包含了一些位和相关的动态语言支持羁绊。

3.5.1。AOP - 通知脚本化bean

它可以使用Spring AOP框架建议脚本豆。Spring AOP框架实际上是不知道正在被通知一个bean可能是一个脚本豆,所以所有的AOP的使用情况和功能,你可能会使用或旨在利用将与脚本豆类工作。只有一个,你需要知道通知脚本化bean时的(小)的东西......你不能使用基于类的代理,则必须使用基于接口的代理

你当然不局限于通知脚本豆...你也可以写在一个支持动态语言本身方面和使用这些bean来提醒其他的Spring bean。这确实是一个先进的使用动态语言的支持,虽然。

3.5.2。作用域

在情况下,它是不是很明显,脚本化bean当然可以,就像任何其他的bean作用域。在scope对各种属性<lang:language/>的元素可以让你控制底层脚本bean的范围,只是因为它与常规的豆一样。(默认范围,只是因为它是与“常规”豆)。

发现下面使用的示例scope属性来定义作用域作为一个Groovy豆原型

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

Bean的作用域的IoC容器 在Spring框架的作用域支持的更详细的讨论。

3.5.3。Lang XML模式

lang与暴露已写入动态语言如JRuby的或Groovy为Spring容器豆对象Spring XML配置协议的标签。

这些标签(和动态语言的支持)的全面覆盖在章标题为动态语言支持。请不要咨询,了解该支持全细节和该章节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 http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd"> <!-- bean definitions here -->

</beans>

3.6。更多资源

查找以下链接,了解有关本章介绍的各种动态语言的更多资源。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容