Spring-Context之三:使用XML和Groovy DSL配置Bean

黃博文發表於2014-03-13

在第一講中顯示瞭如何使用註解配置bean,其實這是Spring3引進的特性,Spring2使用的是XML的方式來配置Bean,那時候漫天的XML檔案使得Spring有著配置地獄的稱號。Spring也一直在力求改變這一缺陷。Spring3引入的註解方式確實使配置精簡不少,而Spring4則引入了Groovy DSL來配置,其語法比XML要簡單很多,而且Groovy本身是門語言,其配置檔案就相當於程式碼,可以用來實現複雜的配置。

廢話少說,讓我們來對Groovy DSL配置來個第一次親密接觸。

首先我們先實現一個XML的bean配置,沿用第一講中的例子。

configuration.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="movieService" class="huangbowen.net.service.DefaultMovieService"/>

    <bean id="cinema" class="huangbowen.net.service.Cinema">
        <property name="movieService" ref="movieService"/>
    </bean>
</beans>

這個XML檔案就不用我多做解釋了,很清晰明瞭。Ok,照例寫個測試來測一下。

XmlConfigurationTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package huangbowen.net;

import huangbowen.net.service.Cinema;
import huangbowen.net.service.DefaultMovieService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/configuration.xml"})
public class XmlConfigurationTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private Cinema cinema;

    @Test
    public void shouldGetCinemaInstance()  {
        Cinema cinema = applicationContext.getBean(Cinema.class);
        assertNotNull(cinema);
    }

    @Test
    public void shouldGetAutowiredCinema() {
        assertNotNull(cinema);
    }

    @Test
    public void shouldGetMovieServiceInstance() {
        assertNotNull(cinema.getMovieService());
        assertThat(cinema.getMovieService(), instanceOf(DefaultMovieService.class));
    }


}

這個測試與第二講中的測試基本上一樣,不過Spring配置的讀取是從configuration.xml來的,在@ContextConfiguration中指定了該xml檔案為Spring配置檔案。

如果想使用Groovy DSL的話第一步需要引入groovy依賴。

pom.xml
1
2
3
4
5
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.2.2</version>
</dependency>

然後就可以新建一個groovy檔案來實現配置編寫。

Configuration.groovy
1
2
3
4
5
6
7
beans {

   movieService huangbowen.net.service.DefaultMovieService

   cinema huangbowen.net.service.Cinema, movieService : movieService

}

這其實體現不出來Groovy DSL的強大靈活,因為我們的例子太簡單了。

beans相當於xml中的beans標籤,第一行中是 bean id + class的形式。 第二行是bean id + class + properties map的形式。第二個引數是一個map陣列,分別對應property和值。

實現同樣的Bean配置有很多種寫法。

1
2
3
movieService (huangbowen.net.service.DefaultMovieService)

cinema(huangbowen.net.service.Cinema, {movieService : movieService})

上面這種其實是Groovy語法的一個特性,在呼叫方法時括號是可選的,既可以加,也可以不加。

1
2
3
4
5
movieService huangbowen.net.service.DefaultMovieService

cinema (huangbowen.net.service.Cinema) {
    movieService :ref movieService
}

上面這中使用了另一個設定屬性的方法,通過一個閉包將屬性設定進去。

1
2
3
4
5
movieService huangbowen.net.service.DefaultMovieService

cinema (huangbowen.net.service.Cinema) {
    movieService : movieService
}

這種更好理解了,ref方法也是可選的。

來照舊寫個測試來測一下。

GroovyDSLConfigurationTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package huangbowen.net;

import huangbowen.net.service.Cinema;
import huangbowen.net.service.DefaultMovieService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractGenericContextLoader;

import static huangbowen.net.GroovyDSLConfigurationTest.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:Configuration.groovy", loader = GenericGroovyContextLoader.class)
public class GroovyDSLConfigurationTest {

    public static class GenericGroovyContextLoader extends
            AbstractGenericContextLoader {

        @Override
        protected BeanDefinitionReader createBeanDefinitionReader(
                GenericApplicationContext context) {
            return new GroovyBeanDefinitionReader(context);
        }

        @Override
        protected String getResourceSuffix() {
            return ".groovy";
        }

    }

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private Cinema cinema;

    @Test
    public void shouldGetCinemaInstance()  {
        Cinema cinema = applicationContext.getBean(Cinema.class);
        assertNotNull(cinema);
    }

    @Test
    public void shouldGetAutowiredCinema() {
        assertNotNull(cinema);
    }

    @Test
    public void shouldGetMovieServiceInstance() {
        assertNotNull(cinema.getMovieService());
        assertThat(cinema.getMovieService(), instanceOf(DefaultMovieService.class));
    }


}

在整合測試中如果載入xml配置檔案,Spring提供了GenericXmlContextLoader類,如果載入註解方式的配置類,Spring提供了AnnotationConfigContextLoader類。但是對於Groovy配置檔案Spring testContext框架還未提供相應的Loader,所以在本測試方法中需要自己實現一個Loader,其實也簡單,只要實現兩個方法即可。

本例中的原始碼請在我的GitHub上自行下載。

相關文章