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。更多資源

查找以下鏈接,了解有關本章介紹的各種動態語言的更多資源。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容