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是靜態(tài)類型語言定位的JVM(以及其他平臺)压汪,它允許寫簡潔而優(yōu)雅的代碼,同時(shí)提供很好 的互操作性與Java編寫的現(xiàn)有的庫古瓤。

Spring框架提供了 Kotlin 一等支持止剖,允許Kotlin 程序員無縫使用 Spring框架落君。

1.1穿香。要求

彈簧框架支持科特林1.1+并且需要 kotlin-stdlib (或它的一個(gè)kotlin-stdlib-jre7 / kotlin-stdlib-jre8變體)和kotlin-reflect 對存在于類路徑。他們在默認(rèn)情況下绎速,如果一個(gè)自舉在科特林項(xiàng)目提供 start.spring.io皮获。

1.2。擴(kuò)展

科特林擴(kuò)展提供到具有附加功能擴(kuò)展現(xiàn)有的類的能力纹冤。Spring框架科特林的API利用這些擴(kuò)展到新的科特林具體的便利添加到現(xiàn)有的Spring的API洒宝。

Spring框架KDOC API列表和文檔的所有科特林?jǐn)U展和提供的DSL。

科特林?jǐn)U展需要 import 使用萌京。例如雁歌,在GenericApplicationContext.registerBean如果科特林?jǐn)U展將只能import org.springframework.context.support.registerBean

例如枫夺,科特林具體化類型參數(shù) 提供JVM一種變通方法泛型類型擦除将宪,和Spring框架提供了一些擴(kuò)展至利用此功能優(yōu)勢。這樣可以更好的科特林API RestTemplate,新WebClient的春天WebFlux和各種其他的API较坛。

像 Reactor 和 Spring Data 數(shù)據(jù)的其他庫還提供Kotlin 擴(kuò)展 API印蔗。

要檢索列表Foo中的Java對象,人們通常會寫:

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

雖然與科特林和Spring框架的擴(kuò)展丑勤,一個(gè)是能寫:

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

正如在Java中华嘹,users在科特林是強(qiáng)類型罕模,但Kotlin 的聰明的類型推斷允許更短的語法南窗。

1.3⊙龀空安全

一個(gè)科特林的主要特點(diǎn)是空的安全 -這干凈地涉及null在編譯的時(shí)候岔霸,而不是撞到著名的值 NullPointerException在運(yùn)行時(shí)薛躬。

這使得應(yīng)用更加安全,通過空性的聲明呆细,并表示“有值或沒有值” 的語義無需支付包裝成本Optional型宝。參考: http://www.baeldung.com/kotlin-null-safety

雖然Java不允許一個(gè)來表達(dá)它的類型系統(tǒng)空安全,Spring框架現(xiàn)在提供整個(gè)Spring框架API的空安全 通過的聲明工裝友好注釋org.springframework.lang包絮爷。默認(rèn)情況下趴酣,在科特林使用的Java API類型被認(rèn)為是 平臺類型 為其中空檢查是放松。 對于JSR 305個(gè)注解科特林支持 +春空性的注釋為整個(gè)Spring框架API來開發(fā)科特林空安全坑夯,與涉及的優(yōu)勢null在編譯時(shí)的相關(guān)問題岖寞。

像 Reactor 反應(yīng)堆或 Spring Data 庫提供空安全的API利用此功能。

JSR 305檢查可以通過添加被配置-Xjsr305具有以下選項(xiàng)的編譯標(biāo)志:

-Xjsr305={strict|warn|ignore}柜蜈。

對于科特林版本1.1.50+仗谆,默認(rèn)行為是一樣的來-Xjsr305=warn。該strict值是必需的跨释。

泛型類型參數(shù)胸私,可變參數(shù)和數(shù)組元素為空性尚不支持厌处,而應(yīng)在未來版本中鳖谈,看到這個(gè)dicussion 達(dá)最新信息。

1.4阔涉。類和接口

彈簧框架支持各種科特林構(gòu)造等經(jīng)由主構(gòu)造實(shí)例科特林類缆娃,不可變的類數(shù)據(jù)綁定和具有默認(rèn)值的功能的可選參數(shù)。

科特林參數(shù)名通過專用的認(rèn)可KotlinReflectionParameterNameDiscoverer 瑰排,其允許贯要,而不需要在Java 8找到接口方法的參數(shù)名稱-parameters 編譯時(shí)啟用編譯器標(biāo)志。

序列化/反序列化JSON數(shù)據(jù)的 jackson-module-kotlin在類路徑發(fā)現(xiàn)椭住,如果沒有 jackson-module-kotlin 被檢測到崇渗,有警告消息。

1.5。注解

Spring框架與 科特林空安全 宅广。

例如:確定是否一個(gè)HTTP參數(shù)是必須的葫掉,而無需顯式定義required屬性。這意味著@RequestParam name: String?如不是必需的跟狱,反之將被視為@RequestParam name: String為被需要俭厚。此功能還支持在 Spring Messaging
消息@Header注解。

以類似的方式驶臊,Spring的bean注射@Autowired@Inject使用該信息來確定是否需要與否的bean挪挤。

@Autowired lateinit var foo: Foo

暗示Bean Foo必須在應(yīng)用程序上下文中進(jìn)行注冊,而

@Autowired lateinit var foo: Foo?

如果這樣的Bean不存在不會引發(fā)錯(cuò)誤关翎。

如果您使用的是帶班Bean驗(yàn)證 主構(gòu)造屬性扛门,確保使用 注釋使用現(xiàn)場的目標(biāo) 在描述這個(gè)堆棧溢出響應(yīng)

1.6纵寝。bean定義DSL

彈簧框架5介紹了使用的lambda作為替代XML或JavaConfig(功能性的方式來登記豆一種新的方式@Configuration@Bean)尖飞。簡單地說,它能夠與作為一個(gè)拉姆達(dá)注冊豆FactoryBean店雅。這個(gè)機(jī)制是非常有效的政基,因?yàn)樗恍枰魏蔚姆瓷浠駽GLIB代理。

在Java中闹啦,一個(gè)例如可以寫:

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

雖然在科特林與具體化類型參數(shù)和GenericApplicationContext 科特林?jǐn)U展一個(gè)可以代替簡單的寫:

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

為了讓更聲明的方式和更清晰的語法沮明,Spring框架提供了科特林bean定義DSL 它聲明的ApplicationContextInitializer通過一個(gè)干凈的聲明API,它使一個(gè)處理配置文件和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>()
    }
}

在這個(gè)例子中荐健,bean<Routes>()使用由自動裝配構(gòu)造和ref<Routes>() 為捷徑applicationContext.getBean(Routes::class.java)

beans()則函數(shù)可用于注冊應(yīng)用程序上下文豆琳袄。

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

這DSL是程序化江场,從而它允許豆的定制注冊邏輯經(jīng)由if表達(dá)式,一個(gè)for環(huán)或任何其他科特林構(gòu)建體窖逗。

Beans.kt 為一個(gè)具體的例子址否。

春季啟動基于Java的配置,并 沒有提供的功能bean定義中還支持碎紊,但一個(gè)實(shí)驗(yàn)可以通過Spring Boot的使用功能bean定義ApplicationContextInitializer的支持佑附,看到這個(gè)堆棧溢出的答案 的詳細(xì)信息和先進(jìn)的最新信息。

1.7仗考。Kotlin Web

1.7.1音同。WebFlux功能DSL

Spring框架現(xiàn)在使用了 科特林路由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是程序化秃嗜,從而它允許豆的定制注冊邏輯經(jīng)由if表達(dá)式权均,一個(gè)for環(huán)或任何其他科特林構(gòu)建體顿膨。當(dāng)路由需要根據(jù)動態(tài)數(shù)據(jù)進(jìn)行登記(例如,從數(shù)據(jù)庫中)叽赊,其可以是有用的虽惭。

MIXIT項(xiàng)目路線 的一個(gè)具體的例子。

1.7.2蛇尚⊙看剑科特林腳本模板

對于4.3版本,Spring框架提供了一個(gè) ScriptTemplateView 渲染使用的腳本引擎取劫,支持模板 JSR-223匆笤。Spring框架5走得更遠(yuǎn)通過擴(kuò)展這個(gè)功能WebFlux并支持 國際化和嵌套模板

科特林提供類似的支持谱邪,并允許根據(jù)科特林模板渲染炮捧,看到 這次提交的詳細(xì)資料。

這使得一些有趣的使用情況-比如使用書寫類型安全模板 kotlinx.html DSL惦银,或者干脆利用科特林多String用插咆课。

這可以允許一個(gè)寫在支持的IDE完全自動完成和重構(gòu)支持科特林模板:

import io.spring.demo.*

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

科特林腳本,模板化示例項(xiàng)目的更多細(xì)節(jié)扯俱。

1.8书蚪。在科特林的Spring項(xiàng)目

本節(jié)提供了一些具體的提示和建議值得科特林開發(fā)Spring項(xiàng)目時(shí),了解的重點(diǎn)迅栅。

1.8.1殊校。最終默認(rèn)

默認(rèn)情況下,在科特林所有的類都是final读存。在open一類調(diào)節(jié)劑是Java的相反final:它允許別人從這個(gè)類繼承为流。這也適用于成員函數(shù),因?yàn)樗鼈冃枰粯?biāo)記為open被覆蓋让簿。

雖然科特林的JVM友好的設(shè)計(jì)通常與春季摩擦敬察,這個(gè)特定的科特林功能可以防止應(yīng)用程序無法啟動,如果這一點(diǎn)不考慮拍攝尔当。這是因?yàn)榇憾雇ǔS蒀GLIB代理-比如@Configuration類-這需要在運(yùn)行時(shí)因技術(shù)原因被繼承莲祸。解決方法是添加一個(gè)open對CGLIB代理的Spring bean如每個(gè)類和成員函數(shù)關(guān)鍵字@Configuration類,很快就會變得疼痛居凶,是對保持代碼簡潔和可預(yù)測的科特林原則虫给。

幸運(yùn)的是藤抡,現(xiàn)在科特林提供了一個(gè) kotlin-spring 插件侠碧,一個(gè)預(yù)配置版本kotlin-allopen的插件,自動打開了注解的類型或元注解與以下注釋的一個(gè)類及其成員函數(shù):

  • @Component

  • @Async

  • @Transactional

  • @Cacheable

元注釋支持意味著標(biāo)注了類型@Configuration缠黍,@Controller弄兜, @RestController@Service或者@Repository因?yàn)檫@些注解元標(biāo)注有自動打開@Component

start.spring.io使得它在默認(rèn)情況下替饿,所以在實(shí)踐中你就可以寫你的科特林豆沒有任何額外的open關(guān)鍵詞语泽,像Java中。

1.8.2视卢。使用持久不變的類實(shí)例

在科特林踱卵,這是非常方便和最佳實(shí)踐是主構(gòu)造內(nèi)聲明只讀屬性,如下面的例子:

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

您可以選擇添加data關(guān)鍵字 据过,使編譯器自動獲得來自主構(gòu)造聲明的所有屬性的成員如下:

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

  • toString()將形式的 “用戶(名稱=約翰惋砂,年齡= 42)”

  • 對應(yīng)于該屬性在其聲明的順序componentN()函數(shù)

  • 副本()函數(shù)

這可以很容易地改變,即使個(gè)別屬性Person的屬性是只讀的:

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

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

常見的持久化技術(shù)绳锅,如JPA需要一個(gè)默認(rèn)的構(gòu)造西饵,防止這種設(shè)計(jì)。幸運(yùn)的是鳞芙,現(xiàn)在這個(gè)解決辦法 “默認(rèn)構(gòu)造函數(shù)地獄” 眷柔,因?yàn)榭铺亓痔峁┝?a target="_blank" rel="nofollow">科特林,JPA 插件原朝,它生成與JPA注解類合成的無參數(shù)的構(gòu)造函數(shù)驯嘱。

如果您需要利用這種機(jī)制對其他持久化技術(shù),你可以配置科特林-noarg 插件喳坠。

|

作為凱釋放列車宙拉,春季數(shù)據(jù)支持科特林不可改變類實(shí)例和不需要kotlin-noarg如果模塊利用彈簧數(shù)據(jù)對象映射(如使用的MongoDB,Redis的丙笋,卡桑德拉等)插件谢澈。

1.8.3。注入依賴

我們的建議是嘗試并有利于構(gòu)造注射val只讀(和非空的可能時(shí)) 的屬性御板。

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

由于Spring框架4.3的锥忿,有一個(gè)構(gòu)造函數(shù)的類都有自己的參數(shù)自動自動連接,這就是為什么沒有必要明確地@Autowired constructor在上面顯示的例子怠肋。

如果一個(gè)人真正需要使用字段注入敬鬓,使用lateinit var結(jié)構(gòu),即笙各,

@Component
class YourBean {

    @Autowired
    lateinit var mongoTemplate: MongoTemplate

    @Autowired
    lateinit var solrClient: SolrClient
}

1.8.4钉答。注入配置屬性

在Java中,一個(gè)可以使用注釋等注入配置屬性@Value("${property}")杈抢,但是在科特林$是用于保留字符串內(nèi)插数尿。

因此,如果希望使用@Value在科特林注釋惶楼,該$ 角色將需要通過寫逃脫@Value("\${property}")右蹦。

作為替代方案诊杆,能夠以自定義屬性通過聲明以下配置豆占位前綴:

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

現(xiàn)有的代碼(如Spring引導(dǎo)致動器或@LocalServerPort一個(gè)使用)${…?}的語法,可以用配置Bean進(jìn)行定制何陆,例如晨汹,如下所示:

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

@Bean
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()

如果使用Spring Boot,那么 可以使用@ConfigurationProperties 替代@Value注釋贷盲,但是目前這只能用于可空var 屬性(這遠(yuǎn)非理想)淘这,因?yàn)椴恢С钟蓸?gòu)造函數(shù)初始化的不可變類。查看有關(guān)@ConfigurationProperties綁定不可變POJO@ConfigurationProperties綁定接口 的更多詳細(xì)信息巩剖。

1.8.5慨灭。注釋陣列屬性

Kotlin注釋大部分與Java類似,但是在Spring中廣泛使用的數(shù)組屬性 - 行為不同球及。正如Kotlin文檔中所述氧骤, 與其他屬性不同,value屬性名稱可以省略吃引,當(dāng)它是數(shù)組屬性時(shí)筹陵,它將被指定為vararg參數(shù)。

要明白這意味著什么镊尺,讓我們@RequestMapping朦佩,這是最廣泛使用Spring注解作為例子之一。此Java注釋聲明如下:

public @interface RequestMapping {

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

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

    RequestMethod[] method() default {};

    // ...
}

典型的使用情況@RequestMapping是將處理程序方法映射到一個(gè)特定的路徑和方法庐氮。在Java中语稠,有可能指定注釋陣列屬性一個(gè)單一的值,它將被自動轉(zhuǎn)換成一個(gè)數(shù)組弄砍。

這就是為什么一個(gè)可以寫入 @RequestMapping(value = "/foo", method = RequestMethod.GET)@RequestMapping(path = "/foo", method = RequestMethod.GET)仙畦。

然而,在科特林音婶,一個(gè)將不得不寫@RequestMapping("/foo", method = arrayOf(RequestMethod.GET))慨畸。使用變體path是不推薦,因?yàn)樗枰粚懭?@RequestMapping(path = arrayOf("/foo"), method = arrayOf(RequestMethod.GET))衣式。

一種用于此特定解決方法method屬性(最常見的一種)是使用快捷方式注釋諸如@GetMapping@PostMapping

提醒:如果@RequestMapping method沒有指定屬性寸士,所有的HTTP方法將被匹配,不僅GET方法碴卧。

改善科特林注釋陣列屬性的語法和一致性中討論 此科特林語言的設(shè)計(jì)問題弱卡。

1.8.6。測試

每類的生命周期

科特林允許指定反引號之間有意義的測試函數(shù)名住册,并作為JUnit的5個(gè)科特林測試類可以使用@TestInstance(TestInstance.Lifecycle.PER_CLASS) 注釋以使測試類的單個(gè)實(shí)例婶博,其允許使用@BeforeAll@AfterAll 在非靜態(tài)方法的注解,這是一個(gè)良好的配合對于科特林界弧。

現(xiàn)在可以更改默認(rèn)行為凡蜻,PER_CLASS多虧了 junit-platform.properties一個(gè)文件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()
  }
}
規(guī)范類測試

它可以使用JUnit 5和科特林創(chuàng)建規(guī)范樣測試垢箕。

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不可用但在科特林由于 類型推理問題預(yù)計(jì)將被固定為1.3科特林的划栓。你可以看 SPR-16057以獲取最新的最新信息。同時(shí)条获,所提出的替代方案是直接用WebClient其反應(yīng)堆和Spring科特林?jǐn)U展到嵌入式WebFlux服務(wù)器上進(jìn)行集成測試忠荞。

1.9。入門

1.9.1帅掘。start.spring.io

開始在科特林一個(gè)新的Spring框架5項(xiàng)目最簡單的方法是創(chuàng)建一個(gè)新的春天啟動二期工程start.spring.io委煤。

也可以作為描述的創(chuàng)建一個(gè)獨(dú)立的WebFlux項(xiàng)目 這個(gè)博客帖子

1.9.2修档。選擇Web味道

Spring框架現(xiàn)在帶有2個(gè)不同的網(wǎng)絡(luò)棧:Spring MVC的春天WebFlux碧绞。

如果想要?jiǎng)?chuàng)建將處理時(shí)延的應(yīng)用,長期連接吱窝,流方案或干脆如果想使用網(wǎng)絡(luò)功能科特林DSL建議春季W(wǎng)ebFlux讥邻。

對于其他用途的情況下,特別是如果你使用的是封鎖的技術(shù)院峡,如JPA兴使,Spring MVC和它的基于注解的編程模型是一個(gè)完全有效的,并完全支持選擇照激。

1.10发魄。資源

1.10.1。博客文章

1.10.2。例子

1.10.3口柳。教程

1.10.4赏淌。問題

下面是有關(guān)的未決春+科特林支持問題的列表。

Spring框架
春季啟動
科特林

2.阿帕奇的Groovy

Groovy是一種功能強(qiáng)大,可選類型和動態(tài)語言荣茫,與靜態(tài)打字和靜態(tài)編譯能力想帅。它提供了一個(gè)簡潔的語法,并與任何現(xiàn)有的Java應(yīng)用程序順利集成啡莉。

Spring框架提供了一個(gè)專用ApplicationContext港准,支持基于Groovy的bean定義DSL旨剥。有關(guān)詳細(xì)信息,請參閱 Groovy的bean定義DSL浅缸。

對Groovy轨帜,包括用Groovy,刷新腳本豆豆衩椒,更加進(jìn)一步的支持是在明年的部分提供動態(tài)語言的支持蚌父。

3.動態(tài)語言支持

3.1。介紹

彈簧2.0引入了使用類和已使用與彈簧的動態(tài)語言(例如JRuby)定義的對象的全面支持毛萌。這種支持允許你寫任意數(shù)量的類別中支持動態(tài)語言苟弛,并有Spring容器透明的實(shí)例化,配置阁将,依賴注入其最終對象膏秫。

目前支持的動態(tài)語言是:

  • JRuby的1.5+

  • Groovy的1.8+

  • BeanShell的2.0

為什么只有這些語言?

支持的語言被選中做盅,因?yàn)?em>一)語言有很多Java企業(yè)社區(qū)牽引缤削,B)不要求被其他語言的那個(gè)加入這一支持時(shí)作出的,而C) Spring開發(fā)者最熟悉它們言蛇。

充分的地方這個(gè)動態(tài)語言的支持可立即有用的工作實(shí)例描述場景僻他。

3.2。第一個(gè)例子

本章的大部分內(nèi)容的關(guān)注點(diǎn)都在詳細(xì)地描述動態(tài)語言的支持腊尚。潛入所有的插件和動態(tài)語言支持的細(xì)節(jié)之前吨拗,讓我們來看看在動態(tài)語言定義的bean的一個(gè)簡單的例子。第一個(gè)bean的動態(tài)語言Groovy的是(這個(gè)例子的基礎(chǔ)上婿斥,從Spring的測試套件采取的劝篷,所以如果你想看到的任何其他支持的語言相同的例子,看看源代碼)民宿。

找到下面Messenger的Groovy的豆將要實(shí)現(xiàn)接口娇妓,并注意該接口是使用純Java定義。這與該參考注入依賴的對象Messenger將不知道底層的實(shí)現(xiàn)是Groovy腳本活鹰。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

下面是有一個(gè)依賴一類的定義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...
    }

}

下面是一個(gè)實(shí)現(xiàn)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執(zhí)行到的實(shí)例 DefaultBookingService類着绷。

| |

要使用自定義動態(tài)語言標(biāo)簽來定義動態(tài)語言支持豆,你需要在Spring XML配置文件的頂部相應(yīng)的XML Schema锌云。你還需要使用彈簧ApplicationContext實(shí)現(xiàn)作為IoC容器荠医。使用動態(tài)語言的支持豆,一個(gè)普通BeanFactory 的實(shí)施是支持的,但你必須管理Spring內(nèi)部的管道這樣做彬向。

有關(guān)基于模式的配置的詳細(xì)信息兼贡,請參閱基于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)現(xiàn)在可以使用其私有 messenger成員變量作為正常的娃胆,因?yàn)?code>Messenger這是注射到它的實(shí)例就是一個(gè)Messenger實(shí)例遍希。沒有什么特別的地方,只是簡單的Java和Groovy的缕棵。

希望上面的XML片段是不言自明孵班,但不要過分擔(dān)心涉兽,如果事實(shí)并非如此招驴。保持閱讀在原因和上述結(jié)構(gòu)的wherefores深入的細(xì)節(jié)。

3.3枷畏。定義由動態(tài)語言支持的bean

本節(jié)描述了如何在任何支持的動態(tài)語言定義Spring管理豆别厘。

請注意,本章不試圖解釋的語法和支持的動態(tài)語言的成語拥诡。例如触趴,如果你想使用Groovy來編寫某些應(yīng)用程序中的類,那么假設(shè)是你已經(jīng)知道的Groovy渴肉。如果您需要了解和動態(tài)語言本身有關(guān)的更多細(xì)節(jié)冗懦,請參考更多的資源在這一章的結(jié)尾。

3.3.1仇祭。常見的概念

是涉及使用動態(tài)語言實(shí)現(xiàn)的bean的步驟如下:

  • 寫測試針對動態(tài)語言的源代碼(自然)

  • 然后編寫動態(tài)語言源碼:)

  • 定義使用適當(dāng)?shù)膭討B(tài)語言實(shí)現(xiàn)的bean <lang:language/> 的XML配置元素(你當(dāng)然可以定義這樣的豆使用Spring API -盡管你要咨詢的源代碼就如何做到這一點(diǎn)披蕉,因?yàn)檫@類型的方向先進(jìn)的配置不本章中)。請注意乌奇,這是一個(gè)反復(fù)的一步没讲。你需要每一個(gè)動態(tài)語言的源文件至少一個(gè)bean定義(同一個(gè)動態(tài)語言的源文件當(dāng)然可以在多個(gè)bean定義中引用)。

前兩步(測試并編寫動態(tài)語言源文件)超出了本章的范圍礁苗。請參考語言規(guī)范和/或參考手冊您選擇的動態(tài)語言爬凑,并與顯影動態(tài)語言的源文件上裂紋。你首先要閱讀本章的其余部分试伙,如Spring的動態(tài)語言支持確實(shí)讓你的動態(tài)語言的源文件的內(nèi)容有一些(朽倚拧)的假設(shè)。

所述的<lang:language />元素

最后一步是定義動態(tài)語言支持的bean定義疏叨,一個(gè)用于您要配置(這是不正常的JavaBean配置不同)的每個(gè)bean潘靖。然而,而不是指定要被實(shí)例化和容器配置的類的完全限定類名的考廉,你可以使用 <lang:language/>元素來定義動態(tài)語言實(shí)現(xiàn)的bean秘豹。

每個(gè)支持的語言都有一個(gè)相應(yīng)的<lang:language/>元素:

  • <lang:groovy/> (Groovy的)

  • <lang:bsh/> (BeanShell的)

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

可用于配置的確切屬性和子元素正是依賴于豆已經(jīng)(以下特定語言有關(guān)的章節(jié)會揭示全部內(nèi)幕)中定義的語言。

刷新豆

其中一個(gè)(如果沒有)Spring對動態(tài)語言支持的最引人注目的價(jià)值在于增加了對“刷新豆”功能昌粤。

可刷新的豆是一個(gè)動態(tài)語言支持的豆與配置少量既绕,動態(tài)語言支持的bean可以監(jiān)控底層源文件資源更改啄刹,然后當(dāng)動態(tài)語言源文件被改變重新加載本身(例如開發(fā)者編輯并保存更改文件在文件系統(tǒng))。

這使得開發(fā)人員以后部署任意數(shù)量的動態(tài)語言的源文件作為應(yīng)用程序的一部分凄贩,配置Spring容器來創(chuàng)建動態(tài)語言源文件支持(使用本章描述的機(jī)制)豆誓军,然后根據(jù)需求的變化或一些外部因素開始發(fā)揮作用,只需編輯動態(tài)語言源文件疲扎,并將它們會反射由已改變的動態(tài)語言源文件為bean的變化昵时。有沒有必要關(guān)閉正在運(yùn)行的應(yīng)用程序(或Web應(yīng)用程序的情況下重新部署)。動態(tài)語言支持的bean能夠自我修正椒丧,新的狀態(tài)和邏輯從改變的動態(tài)語言源文件壹甥。

| |

請注意,此功能是關(guān)閉默認(rèn)壶熏。

|

讓我們來看一個(gè)例子句柠,看看它是多么容易開始使用刷新豆。要打開的刷新豆功能棒假,你只需要明確指定 一個(gè)上附加屬性<lang:language/>的bean定義的元素溯职。因此,如果我們堅(jiān)持的例子從本章前面帽哑,這里就是我們將在Spring XML配置變化來實(shí)現(xiàn)刷新豆:

<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定義是在此之后,豆將與底層動態(tài)語言的源文件所作的任何更改被刷新的毫秒數(shù)妻枕。您可以通過分配負(fù)值的關(guān)閉刷新行為 'refresh-check-delay'屬性僻族。請記住,在默認(rèn)情況下佳头,該刷新行為被禁止鹰贵。如果你不希望刷新行為,那么根本就沒有定義屬性康嘉。

如果我們再運(yùn)行下面的應(yīng)用程序碉输,我們可以鍛煉刷新的功能; 請?jiān)?em>“跳躍通箍到暫停中,執(zhí)行”惡作劇在這個(gè)代碼下一個(gè)切片亭珍。該System.in.read()電話只能有這樣程序的執(zhí)行暫停敷钾,而我(作者)组底,這個(gè)時(shí)候去修改底層的動態(tài)語言源文件轴咱,以便刷新將在動態(tài)語言實(shí)現(xiàn)的bean觸發(fā)時(shí)远搪,程序繼續(xù)執(zhí)行惧眠。

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());
    }
}

假設(shè)那么,在這個(gè)例子的目的始衅,是對所有呼叫 getMessage()的方法Messenger的實(shí)現(xiàn)必須改變摇庙,以使得該消息被引號括起來勋锤。下面是改變我(作者)做出的 Messenger.groovy源文件時(shí),程序的執(zhí)行被暫停羊壹。

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

當(dāng)程序執(zhí)行時(shí)蓖宦,在輸入暫停之前的輸出將是我可以做Frug。源文件做出更改并保存后油猫,程序繼續(xù)執(zhí)行稠茂,調(diào)用的結(jié)果getMessage()對動態(tài)語言支持的方法Messenger實(shí)施將是“我可以做Frug” (注意附加報(bào)價(jià)列入) 。

要明白情妖,修改腳本將是很重要的不是如果的窗口內(nèi)發(fā)生的變化觸發(fā)刷新'refresh-check-delay'值睬关。要明白,修改劇本是同樣重要的是沒有真正“拿起”毡证,直到一個(gè)方法被調(diào)用的動態(tài)語言實(shí)現(xiàn)的bean电爹。只有當(dāng)一個(gè)方法被調(diào)用的動態(tài)語言實(shí)現(xiàn)的bean,它檢查是否它的底層腳本源發(fā)生了變化情竹。有關(guān)刷新腳本(如遇到編譯錯(cuò)誤藐不,或找到腳本文件已被刪除)的任何異常都會導(dǎo)致致命的異常傳播到調(diào)用代碼匀哄。

上述的可刷新的豆的行為并沒有適用于使用所定義的動態(tài)語言的源文件<lang:inline-script/>元素符號(參照 內(nèi)置動態(tài)語言的源文件)秦效。此外,它適用于其中改變底層源文件實(shí)際上可以檢測豆; 例如涎嚼,通過檢查該文件系統(tǒng)上的存在的動態(tài)語言的源文件的最后修改日期代碼阱州。

內(nèi)置動態(tài)語言源文件

動態(tài)語言的支持也能滿足那些直接嵌入在Spring bean定義動態(tài)語言源文件。更具體地說法梯, <lang:inline-script/>元素苔货,可以立即定義Spring配置文件中的動態(tài)語言源代碼。下面的例子或許可以將嵌入腳本功能一清二楚:

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

如果我們把一側(cè)周圍是否是很好的做法立哑,定義一個(gè)Spring配置文件中的動態(tài)語言源的問題夜惭,該<lang:inline-script/> 元素可以在某些情況下非常有用。例如铛绰,我們可能想快速Spring的增加Validator實(shí)現(xiàn)的Spring MVC的Controller诈茧。這不過是使用內(nèi)聯(lián)源片刻的工夫。(請參閱腳本驗(yàn)證器為這樣的例子捂掰。)

了解構(gòu)造器注入動態(tài)語言實(shí)現(xiàn)的bean的情況下

有一個(gè)非常要注意的是關(guān)于Spring的動態(tài)語言支持重要的事情敢会。也就是說,它不是(目前)可以提供構(gòu)造器參數(shù)这嚣,以動態(tài)語言實(shí)現(xiàn)的bean(因此構(gòu)造注射不適用于動態(tài)語言支持的bean)鸥昏。為了將構(gòu)造器和屬性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>

在實(shí)踐中吏垮,這種限制是不是第一次出現(xiàn),因?yàn)閟etter注入是通過反正絕大多數(shù)的開發(fā)商青睞注射風(fēng)格顯著(讓我們離開討論是否這是一件好事,以另一天)膳汪。

3.3.2像樊。的Groovy bean

Groovy依賴庫

在Spring支持的Groovy腳本需要以下庫在你的應(yīng)用程序的classpath。

  • groovy-1.8.jar

  • asm-3.2.jar

  • antlr-2.7.7.jar

來自Groovy官方網(wǎng)頁...

Groovy是一種敏捷的動態(tài)語言旅敷,Java 2平臺有很多的功能生棍,人們喜歡這么多的像Python,Ruby和Smalltalk語言媳谁,使其可使用Java的語法Java開發(fā)人員涂滴。

如果您已經(jīng)閱讀從頂部本章直,你應(yīng)該已經(jīng) 看到一個(gè)例子一個(gè)Groovy的動態(tài)語言實(shí)現(xiàn)的bean的晴音。讓我們來看看另一個(gè)例子(還是選自Spring的測試套件的例子)柔纵。

package org.springframework.scripting;

public interface Calculator {

    int add(int x, int y);

}

下面是一個(gè)實(shí)現(xiàn)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>

最后锤躁,這里是一個(gè)小的應(yīng)用程序來測試上面的配置搁料。

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));
    }
}

從運(yùn)行上述程序的輸出結(jié)果得到的將是(不出所料)10。(令人興奮的例子系羞,是吧郭计?記住我們的目的是為了說明這個(gè)概念,請參考動態(tài)語言的示例項(xiàng)目更復(fù)雜的例子椒振,或甚 方案本章后面)昭伸。

你是很重要的明確Groovy源文件中不止一個(gè)類。雖然這是完全合法的Groovy中澎迎,它是(可以說)一個(gè)不好的做法:在一個(gè)一致的方法的利益庐杨,你應(yīng)該(在筆者的意見)尊重每個(gè)源文件中的一個(gè)(public)類標(biāo)準(zhǔn)Java約定。

通過回調(diào)定制Groovy對象

GroovyObjectCustomizer接口是一個(gè)回調(diào)夹供,它允許你將附屬的創(chuàng)建邏輯添加到創(chuàng)建一個(gè)Groovy的bean的過程灵份。例如,這個(gè)接口的實(shí)現(xiàn)可以調(diào)用任何所需的初始化方法(一個(gè)或多個(gè))哮洽,或設(shè)置某些默認(rèn)屬性值填渠,或指定自定義MetaClass

public interface GroovyObjectCustomizer {

    void customize(GroovyObject goo);
}

Spring框架將實(shí)例你的Groovy-backed bean的實(shí)例袁铐,然后將通過創(chuàng)建GroovyObject到指定GroovyObjectCustomizer如果已經(jīng)定義揭蜒。你可以做任何你所提供的喜歡GroovyObject 參考:預(yù)計(jì)自定義的設(shè)置MetaClass是什么,大多數(shù)人都希望這個(gè)回調(diào)做剔桨,你可以看到這樣做屉更,下面的例子。

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已經(jīng)超出了本參考手冊的范圍洒缀。查閱Groovy參考手冊的相關(guān)部分瑰谜,或者做一個(gè)網(wǎng)上搜索:有大量的關(guān)于這方面的文章欺冀。其實(shí)利用的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渤早,你也可以指定一個(gè)Groovy CompilationCustomizer (如ImportCustomizer)职车,或者甚至是完全的Groovy CompilerConfiguration在同一個(gè)地方作為春天的對象GroovyObjectCustomizer

|

3.3.3鹊杖。BeanShell的豆

BeanShell的依賴庫

在Spring支持的BeanShell腳本需要以下庫在你的應(yīng)用程序的classpath悴灵。

  • bsh-2.0b4.jar

來自BeanShell官方網(wǎng)頁...

BeanShell的是一個(gè)小的,自由的骂蓖,可嵌入的Java源代碼解釋器具有動態(tài)語言功能积瞒,用Java編寫的。BeanShell動態(tài)執(zhí)行標(biāo)準(zhǔn)的Java語法和與常見的腳本的便利登下,如松散類型延伸它茫孔,命令和方法封閉像那些在Perl和JavaScript 。

和Groovy相比被芳,基于BeanShell的bean定義需要一些(戌直础)的額外配置。Spring對BeanShell動態(tài)語言支持的實(shí)現(xiàn)是有趣的地方是這樣的:春創(chuàng)建了JDK動態(tài)代理實(shí)現(xiàn)在指定的接口'script-interfaces' 中的屬性值<lang:bsh>元素(這就是為什么你必須至少提供一個(gè)接口中的屬性筐钟,以及使用基于BeanShell的豆時(shí)(相應(yīng)地)程序接口)的值揩瞪。這意味著,基于BeanShell對象的每一個(gè)方法調(diào)用正在經(jīng)歷的JDK動態(tài)代理調(diào)用機(jī)制篓冲。

讓我們來看看使用它實(shí)現(xiàn)了一個(gè)基于BeanShell的bean的可工作的完整例子Messenger這是本章(下面重復(fù)為了您的方便)在早期定義的接口。

package org.springframework.scripting;

public interface Messenger {

    String getMessage();

}

這里是的BeanShell的“執(zhí)行”(該術(shù)語的隨意) Messenger接口宠哄。

String message;

String getMessage() {
    return message;
}

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

而這里的Spring XML定義了上述“類”的一個(gè)“實(shí)例”(這里對術(shù)語的使用非常的隨意)壹将。

<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管理豆腳本語言可能出現(xiàn)的情況將是有益的,當(dāng)然承粤,多種多樣的暴区。本節(jié)介紹Spring對動態(tài)語言支持兩種可能的使用情況。

3.4.1辛臊。腳本Spring MVC控制器

那可以使用動態(tài)語言支持的bean得益于有一組類是Spring MVC的控制器仙粱。在純Spring MVC應(yīng)用中,導(dǎo)航流程通過web應(yīng)用是通過在Spring MVC的控制器內(nèi)包封的碼來確定在很大程度上彻舰。作為一個(gè)Web應(yīng)用程序的導(dǎo)航流程和其他表示層邏輯需要進(jìn)行更新伐割,以應(yīng)對支持問題或變化的業(yè)務(wù)需求候味,它很可能會更容易通過編輯一個(gè)或多個(gè)動態(tài)語言源文件,看到那些進(jìn)行任何此類要求的變化變化被立即反映在正在運(yùn)行的應(yīng)用程序的狀態(tài)隔心。

請記住白群,在由項(xiàng)目支持的輕量級架構(gòu)模型如Spring,你通常目標(biāo)是有一個(gè)非常薄的表示層硬霍,所有的應(yīng)用程序的肉香業(yè)務(wù)邏輯被包含在域和服務(wù)層的類帜慢。開發(fā)Spring MVC控制器動態(tài)語言實(shí)現(xiàn)的bean允許你通過簡單的編輯保存文本文件就可以修改表示層邏輯; 這樣的動態(tài)語言的源文件的任何更改將(取決于配置)在由動態(tài)語言的源文件備份豆自動反映。

| |

為了實(shí)現(xiàn)的任何變化動態(tài)語言支持的bean這一自動“皮卡”唯卖,你將不得不啟用'refreshable bean的功能崖堤。見 刷新的豆子一個(gè)完整的處理這個(gè)功能的。

|

請看下面的一個(gè)例子org.springframework.web.servlet.mvc.Controller是使用Groovy動態(tài)語言實(shí)現(xiàn)耐床。

// 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密幔。腳本校驗(yàn)

可通過動態(tài)語言支持的bean提供的靈活性,有利于應(yīng)用開發(fā)與Spring的另一個(gè)領(lǐng)域是驗(yàn)證撩轰。它可能 是更容易使用松散類型的動態(tài)語言(也可能有內(nèi)置正則表達(dá)式的支持)胯甩,而不是常規(guī)的Java來表達(dá)復(fù)雜的驗(yàn)證邏輯。

再次堪嫂,制定驗(yàn)證動態(tài)語言實(shí)現(xiàn)的bean可以讓你通過簡單的編輯和保存一個(gè)簡單的文本文件來改變驗(yàn)證邏輯; 任何這樣的改變將(取決于配置)自動反映在運(yùn)行中的應(yīng)用程序的執(zhí)行并且不需要應(yīng)用程序的重新啟動偎箫。

| |

請注意,為了實(shí)現(xiàn)的任何變化動態(tài)語言支持的bean自動“皮卡”皆串,你將不得不啟用“刷新豆”功能淹办。見刷新的豆子一個(gè)完整和詳細(xì)的治療此功能。

|

下面為一個(gè)春天的一個(gè)例子org.springframework.validation.Validator是使用Groovy動態(tài)語言實(shí)現(xiàn)恶复。(請參閱使用Spring的Validator接口驗(yàn)證的的討論 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。位和鮑勃

最后一節(jié)包含了一些位和相關(guān)的動態(tài)語言支持羈絆谤牡。

3.5.1副硅。AOP - 通知腳本化bean

它可以使用Spring AOP框架建議腳本豆。Spring AOP框架實(shí)際上是不知道正在被通知一個(gè)bean可能是一個(gè)腳本豆翅萤,所以所有的AOP的使用情況和功能恐疲,你可能會使用或旨在利用將與腳本豆類工作。只有一個(gè)套么,你需要知道通知腳本化bean時(shí)的(信嗉骸)的東西......你不能使用基于類的代理,則必須使用基于接口的代理胚泌。

你當(dāng)然不局限于通知腳本豆...你也可以寫在一個(gè)支持動態(tài)語言本身方面和使用這些bean來提醒其他的Spring bean省咨。這確實(shí)是一個(gè)先進(jìn)的使用動態(tài)語言的支持,雖然诸迟。

3.5.2茸炒。作用域

在情況下愕乎,它是不是很明顯,腳本化bean當(dāng)然可以壁公,就像任何其他的bean作用域感论。在scope對各種屬性<lang:language/>的元素可以讓你控制底層腳本bean的范圍,只是因?yàn)樗c常規(guī)的豆一樣紊册。(默認(rèn)范圍比肄,只是因?yàn)樗桥c“常規(guī)”豆)。

發(fā)現(xiàn)下面使用的示例scope屬性來定義作用域作為一個(gè)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框架的作用域支持的更詳細(xì)的討論芳绩。

3.5.3。Lang XML模式

lang與暴露已寫入動態(tài)語言如JRuby的或Groovy為Spring容器豆對象Spring XML配置協(xié)議的標(biāo)簽撞反。

這些標(biāo)簽(和動態(tài)語言的支持)的全面覆蓋在章標(biāo)題為動態(tài)語言支持妥色。請不要咨詢,了解該支持全細(xì)節(jié)和該章節(jié)lang標(biāo)簽本身遏片。

為了完整起見嘹害,為了使用lang模式中的標(biāo)簽,您需要在Spring XML配置文件的頂部有以下前導(dǎo)碼; 以下片段中的文本引用了正確的模式吮便,以便可以使用lang名稱空間中的標(biāo)記笔呀。

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

查找以下鏈接髓需,了解有關(guān)本章介紹的各種動態(tài)語言的更多資源许师。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市僚匆,隨后出現(xiàn)的幾起案子微渠,更是在濱河造成了極大的恐慌,老刑警劉巖白热,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敛助,死亡現(xiàn)場離奇詭異,居然都是意外死亡屋确,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門续扔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來攻臀,“玉大人,你說我怎么就攤上這事纱昧∨傩ィ” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵识脆,是天一觀的道長设联。 經(jīng)常有香客問我善已,道長,這世上最難降的妖魔是什么离例? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任换团,我火速辦了婚禮,結(jié)果婚禮上宫蛆,老公的妹妹穿的比我還像新娘艘包。我一直安慰自己,他們只是感情好耀盗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布想虎。 她就那樣靜靜地躺著,像睡著了一般叛拷。 火紅的嫁衣襯著肌膚如雪舌厨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天忿薇,我揣著相機(jī)與錄音裙椭,去河邊找鬼。 笑死煌恢,一個(gè)胖子當(dāng)著我的面吹牛骇陈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瑰抵,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼你雌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了二汛?” 一聲冷哼從身側(cè)響起婿崭,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肴颊,沒想到半個(gè)月后氓栈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婿着,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年授瘦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竟宋。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡提完,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丘侠,到底是詐尸還是另有隱情徒欣,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布蜗字,位于F島的核電站打肝,受9級特大地震影響脂新,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粗梭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一争便、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楼吃,春花似錦始花、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躬窜,卻和暖如春浇垦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荣挨。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工男韧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人默垄。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓此虑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親口锭。 傳聞我的和親對象是個(gè)殘疾皇子朦前,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容