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>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章