Spring Framework Core - The IoC Container (12) - 基於java的容器配置 (1)基本概念


本節的內容:如何使用註解配置Spring容器。包含下面的主題:

  • 基本概念:@Bean 和 @Configuration

12.1 基本概念:@Bean 和 @Configuration

Spring新的java配置支持中的核心構件是@Configuration-annotated類和@Bean-annotated方法。

@Bean註釋用於指示方法實例化、配置和初始化要由Spring IoC 容器管理的新對象。對於熟悉Spring的XML配置beans元素的人來說,@Bean註釋的作用與bean元素相同。您可以對任何Spring @Component使用@bean註釋的方法。但是,它們最常與@Configuration 註解的beans一起使用。

使用@Configuration註釋類表明其主要目的是作爲bean定義的源。
此外,@Configuration類允許通過調用同一類中的其他@Bean方法來定義bean之間的依賴關係。最簡單的@Configuration類如下:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

前面的AppConfig類等價於下面的Spring beans的 XML:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

對比:Full @Configuration vs “lite” @Bean mode?

當@Bean方法在沒有使用@Configuration註釋的類中聲明時,它們以“lite”模式處理。在@Component或POJO類中聲明的Bean方法被認爲是“精簡的”,包含類的主要用途不同,而@Bean方法在這裏是一種附加功能。例如,服務組件可以通過每個適用組件類上的附加@Bean方法向容器暴露管理視圖。在這些場景中,@Bean方法是一種通用的工廠方法機制。

與完整的@Configuration不同,lite @Bean方法不能聲明內部bean之間的依賴關係。相反,它們對包含它們的組件的內部狀態以及可能聲明的參數(可選)進行操作。因此,這樣的@Bean方法不應該調用其他@Bean方法。每個這樣的方法實際上只是一個特定bean引用的工廠方法,沒有任何特殊的運行時語義。這樣做的好處是不需要在運行時應用CGLIB子類,所以在類設計方面沒有限制(也就是說,包含的類可能是final等等)。在常見的使用場景中,@Bean方法是在@Configuration類中聲明的,以確保總是使用“full”模式,從而將交叉方法引用重定向到容器的生命週期管理。這可以防止通過常規Java調用意外調用相同的@Bean方法,這有助於減少在“lite”模式下操作時難以跟蹤的細微bug。

下面幾節將深入討論@Bean和@Configuration註解。但是,我們首先介紹了通過基於java的配置創建spring容器的各種方法。

12.2 使用AnnotationConfigApplicationContext實例化Spring容器

下面幾節將介紹Spring 3.0中引入AnnotationConfigApplicationContext。這個通用的ApplicationContext實現不僅可以接受@Configuration類作爲輸入,還可以接受普通的@Component類和使用JSR-330元數據註解的類。

當@Configuration類作爲輸入提供時,@Configuration類本身註冊爲bean定義,類中所有聲明的@Bean方法也註冊爲bean定義

當提供@Component和JSR-330類時,它們被註冊爲bean定義,並假設依賴注入元數據(如@Autowired或@Inject)在這些類中使用。

簡單構造

與在實例化ClassPathXmlApplicationContext時將Spring XML文件用作輸入非常相似,您可以在實例化註釋configapplicationcontext時將@Configuration類用作輸入。這使得Spring容器可以完全不使用xml,如下面的例子所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

如前所述,AnnotationConfigApplicationContext不僅限於使用@Configuration類。任何@Component或JSR-330註釋類都可以作爲輸入提供給構造函數,如下例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

通過使用register(Class<?>…)程序化構建容器

您可以使用一個無參數的構造函數來實例化一個帶註釋的configapplicationcontext,然後使用register()方法來配置它。當以編程方式構建AnnotationConfigApplicationContext時,這種方法特別有用。下面的例子演示瞭如何做到這一點:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

使用sacn(String…)啓用組件掃描

要啓用組件掃描,可以按如下方式註解@Configuration類

@Configuration
@ComponentScan(basePackages = "com.acme") //啓用組件掃描
public class AppConfig  {
    ...
}

有經驗的Spring用戶可能熟悉與Spring上下文等價的XML聲明:namespace,如下例所示:

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

前面的例子中,com.acme 包被掃描以查找所有基於@Componont註解的類,這些類在容器中註冊爲Spring bean定義。AnnotationConfigApplicationContext提供scan(String…)方法,允許使用相同的組件掃描功能,如下面的示例所示:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

請記住@Configuration類是用@Component進行元註釋的,因此它們是組件掃描的候選對象。在前面的例子中,假設AppConfig是在com.acme中聲明的。acme包(或下面的任何包),它在調用scan()時被獲取。在refresh()中,它的所有@Bean方法都將作爲bean定義在容器中進行處理和註冊。

支持AnnotationConfigWebapplicationContext的Web應用

AnnotationConfigApplicationContext的WebApplicationContext變體AnnotationConfigWebApplicationContext,您可以在配置Spring ContextLoaderListener servlet監聽器、Spring MVC DispatcherServlet等時使用這個實現。下面的web.xml片段配置了一個典型的Spring MVC web應用程序(注意contex-param和init-param的使用):

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet --> 
    <servlet-mapping>
     <servlet-name>dispatcher</servlet-name>
       <url-pattern>/app/*</url-pattern>
     </servlet-mapping>
</web-app>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章