IOC容器

介绍Spring IoC容器和Bean

IoC也叫dependency injection(DI)。通过这种方式,对象可以通过构造函数参数、工厂方法参数和属性定义它们的依赖对象。容器在创建bean的时候会将依赖注入。这个过程和bean自身通过类构造器和Service Location机制控制初始化和定位依赖的位置是完全相反的,因此称作控制反转。
org.springframework.beans和org.springframework.context这两个包是Spring框架的IoC容器的基础。BeanFactory接口提供一个高级配置机制,能够管理任何类型的对象。ApplicationContext是BeanFactory的子接口。它和Spring的AOP特性更容易集成,包括消息资源处理,事件发布和应用层特定的Context,像在web程序中使用的WebApplicationContext。BeanFactory提供了配置框架和基本功能,ApplicationContext添加了企业专用的功能。
Spring IoC容器管理的对象叫做beans。Bean由Spring IoC容器初始化,装配和管理。Beans和它们的依赖在容器使用的配置元数据中定义。

容器概述

接口org.springframework.context.ApplicationContext代表Spring IoC容器,负责初始化、配置、装配前面提到的beans。容器通过读取配置文件得到初始化、配置和装配对象的指令。配置元数据可以是XML、Java注解或者Java代码。
Spring提供了几个开箱即用的ApplicationContext接口的实现。在独立的应用程序中通常可以创建ClassPathXmlApplicationContext or FileSystemXmlApplicationContext的实例。
你的应用程序类和配置元数据结合,以便在ApplicationContext创建和初始化以后,你有一个完全配置和执行的应用。
在这里插入图片描述

配置元数据

有3中配置方式:
基于XML文件配置
基于注解配置
基于Java配置
Spring配置由至少一个bean definition组成,基于XML文件的配置元数据是由顶层元素<beans>下的<bean>元素定义。基于Java的配置使用@Configuration注解类的@Bean注解方法配置。
基于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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions go here -->
</beans>

id属性用来唯一标识bean definition,class属性定义bean的类型,使用的是完全限定名。

使用容器

// create and configure beans
ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();

Bean概要

Spring IoC容器管理一个或多个bean。这些bean是根据你提供给容器的配置元数据创建的,例如xml的<bean />定义。
在容器内部,bean定义由BeanDefinition对象表示,包括下面的元数据:

  • 合法的包限定名。
  • Bean行为配置元素,描述了bean在容器中如何表现(作用域、生命周期、回调函数等等)。
  • 对其它bean的引用,这些bean被称作合作者或者依赖。
  • 在新创建的对象中的其他的配置,例如,bean中的连接数量,管理一个连接池或者池的大小限制。

这些元数据最终转换成构成每个bean定义的一系列属性。
在这里插入图片描述

除了包含创建一个特定bean的信息的bean定义外,ApplicationContext实现也允许注册用户在容器外创建的已经存在的对象。这是通过getBeanFactory()访问ApplicationContext’s BeanFactory,返回BeanFactory的实现DefaultListableBeanFactory。DefaultListableBeanFactory 支持通过registerSingleton(…) and registerBeanDefinition(…)注册bean。然而,典型的应用程序仅仅是通过元数据定义bean。

Bean实例化

用构造函数实例化bean:

<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

用静态工厂方法实例化bean:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

用实例工厂方法实例化bean:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();
    private DefaultServiceLocator() {}

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

依赖

依赖注入

依赖注入存在2中主要的形式:基于构造函数的依赖注入和基于属性的依赖注入。
基于构造函数的依赖注入
构造函数的依赖注入是由容器调用带有很多参数的构造函数完成的。
构造函数参数解析:
当参数是基本类型时,像<value>true</value>,spring不能判断值的类型,也就不能根据值的类型匹配参数。
例子:

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;
    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

使用type:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

使用index:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

使用name:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateanswer" value="42"/>
</bean>

基于属性的依赖注入
属性依赖注入是在容器使用无参构造函数或者无参的静态工厂方法初始化bean后调用setter方法完成的。

ApplicationContextAware接口

当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中所有引用到的bean对象。
应用场景:当一个singleon的bean A需要引用一个prototype的bean B时,容器只能创建bean A一次,因此只有一次设置属性的机会,容器不能在beanA提供新的beanB。
解决方法:让bean A实现ApplicationContextAware接口,则每当beanA需要beanB时,可以从容器中动态的获取beanB。
例子:

<bean id="commandManager" class="ioc.service.CommandManager" />
<bean id="command" class="ioc.service.Command" scope="prototype" />
package ioc.service;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {
	private ApplicationContext applicationContext;

    public void process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        command.execute();
    }

    public Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

但是这种方法不提倡,因为业务代码和spring框架耦合在一起。方法注入可以以一种简洁的方式解决这个问题。

Lookup方法注入

方法注入使容器可以覆盖bean中的方法,返回结果是容器中的另一个命名的bean。一般涉及到一个原型作用域的bean。Spring实现这种方法通过使用CGLIB库中字节生成的功能更来动态生成一个子类覆盖这个方法。
被注入的方法要求是以下形式:
<public|protected> [abstract] theMethodName(no-arguments);
例子:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="command"/>
</bean>
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }
    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

定制Bean的特性

生命周期回调

为了和容器的理bean的生命周期交互,你可以实现Spring的InitializingBean和DisposableBean接口。容器对前者调用afterPropertiesSet(),对后者调用destroy()方法,当bean进行初始化和销毁的时候进行特定的操作。Spring框架内部使用BeanPostProcessor接口来处理回调接口并且调用正确的方法。
从Spring2.5开始,你有3个选择控制bean的生命周期行为:
1)the InitializingBean和DisposableBean回调接口。
2)定制init()和destroy()方法。
3)@PostConstruct和@PreDestroy注解。
使用上面3种方法配置了bean的不同初始化方法,按下列顺序调用:
@PostConstruct、afterPropertiesSet()、init()。
销毁方法也是一致的。

容器扩展点

一般情况下,应用程序开发者不需要子类化ApplicationContext实现类。因而,Spring IoC容器可以通过实现特殊的集成接口来被扩展。下面几节描述这些集成接口。

使用BeanPostProcessor定制bean

BeanPostProcessor接口定义了你可以实现的回调方法来提供你自己的初始化逻辑、依赖解析逻辑等等。如果你想在Spring容器结束实例化、配置和初始化bean后实现一些定制逻辑,你可以实现BeanPostProcessor接口。
你可以配置多个BeanPostProcessor实例,你可以通过设置order属性控制这些BeanPostProcessors的执行顺序;如果你写自己的BeanPostProcessor,你应该考虑实现Ordered接口。对于更详细的信息,参考BeanPostProcessor和Ordered的java文档。
注意:BeanPostProcessors操作bean实例。也就是说,Spring IoC容器初始化bean实例,然后BeanPostProcessors完成它们的工作。BeanPostProcessors是作用于每个容器中的。这只适用于使用层次结构的容器。如果你在一个容器中定义了一个BeanPostProcessor,那么它只后处理那个容器中的bean。也就是说,定义在一个容器中的beans不会被另一个容器中的BeanPostProcessor处理,即使两个容器是相同层次中的一部分。为了改变实际的bean的定义,你需要使用BeanFactoryPostProcessor。
org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法组成。当在容器中注册一个post-processor时,对于容器创建的每一个bean实例来说,post-processor在容器初始化方法前和bean初始化方法前,会从容器那里获得一个回调。post-processor可以对bean实例采取任何处理,包括完全忽略回调。bean post-processor检测回调接口或者用代理包装一个bean。一些Spring AOP基础设施类被实现为post-processors以便提供代理包装的逻辑。
ApplicationContext自动检测任何定义在配置元数据中的实现了BeanPostProcessor接口的bean。ApplicationContext把这些bean注册为post-processors,以便他们在bean创建后被调用。Bean post-processors可以像其它bean一样部署在容器中。

Example: Hello World, BeanPostProcessor-style

package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean,
            String beanName) throws BeansException {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean,
            String beanName) throws BeansException {
        System.out.println("Bean " + beanName + " created : " + bean.toString());
        return bean;
    }

}
<?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:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>
    <!--
    when the above bean (messenger) is instantiated, this custom
    BeanPostProcessor implementation will output the fact to the system console
    -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>
    
</beans>

Example: The RequiredAnnotationBeanPostProcessor

使用回调接口或者注解联合定制的BeanPostProcessor实现是一个扩展Spring IoC容器的方式。Spring的RequiredAnnotationBeanPostProcessor是BeanPostProcessor的一个实现,能够确保bean的被标记为注解的JavaBean属性实际上是依赖注入了一个值。

使用BeanFactoryPostProcessor定制配置元数据

和BeanPostProcessor的不同之处是:BeanPostProcessor对bean实例进行处理,而BeanFactoryPostProcessor是对bean配置元数据进行处理。容器实例化bean之前,允许BeanFactoryPostProcessor读取配置元数据并且改变它。Spring包含大量预定义的BeanFactoryPostProcessor,例如:PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。
例子:
使用PropertyPlaceholderConfigurer来将bean definition的属性值外部化到一个单独的文件中,使用标准的Java Properties格式。这样做使得我们可以部署应用程序定制特定环境属性,像数据库URL和密码,而不用改变主XML定义文件和容器中的文件。

Example: the Class name substitution PropertyPlaceholderConfigurer

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

Properties文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

引入PropertyPlaceholderConfigurer

<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>

基于注解的容器配置

一个XML的替代方法是基于注解的配置,这依赖于字节码元数据组装组件。不使用XML描述bean的装配,开发者通过使用注解把配置放到组件类自身中。基于注解的依赖注入在XML依赖注入前进行,因此后面的配置会覆盖前面的属性配置。
使用BeanPostProcessor和注解扩充Spring IOC容器是一种常见的方法
可以单个注册这些bean定义,也可以使用下面的标签隐式地注册。

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

隐式注册的post-processors包括AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor

@Required

@Required注解应用到bean属性的setter方法上,该注解表明bean属性一定要通过显式bean属性值或者自动装配在配置时产生。注解注入在XML注入前执行。
从Spring Framework 5.1开始,@Required注释被正式弃用,支持为所需的设置使用构造函数注入(或InitializingBean.afterPropertiesSet()的自定义实现以及bean属性设置器方法)。

@Autowired

@Autowired可以应用到setter属性、任意的方法、构造函数、字段。
注意:@Autowired, @Inject, @Resource, and @Value这些注解是由Spring的BeanPostProcessor实现来处理的,意味着你不能把这些注解应用到BeanPostProcessor和BeanFactoryPostProcessor类型上。这些类型一定是要显式地通过XML或者使用@Bean方法装配。

使用qualifiers对注解微调

类路径扫描和管理组件

@Component和更多的模式注解

Spring提供了注解:@Component, @Service, and @Controller。@Component是一个Spring管理组件的通用的注解。@Repository, @Service, and @Controller是@Component的特化,用于更具体的场景,例如持久层、服务层和展示层。

配置自动扫描

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

context:component-scan配置隐式地包含了context:annotation-config

使用过滤器定制扫描

例如:

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

基于Java配置的注解

@Bean和@Configuration

用 @Configuration注解的类表明该类用于定义bean。
@Bean注解用于表明一个方法实例化、配置和初始化新的由Spring IoC容器管理的对象。
例如:

@Configuration
public class AppConfig {

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

}

等同于

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

使用AnnotationConfigApplicationContext初始化Spring容器

这个灵活的ApplicationContext实现不仅能够接收@Configuration类,还能接收@Component和JSR-330注解的类。当@Configuration作为输入时,@Configuration配置类本身会被注册为bean定义,所有声明的@Bean方法也会被注册为bean定义。
当@Component和JSR-330注解的类被提供时,它们被作为bean定义。

简单的构造

和ClassPathXmlApplicationContext使用Spring XML文件作为输入一样,当实例化AnnotationConfigApplicationContext时会使用@Configuration注解的类作为输入。这允许使用Spring容器完全不用XML。

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

Building the container programmatically using register(Class<?>…)

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

Enabling component scanning with scan(String…)

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>
public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

支持web应用程序的AnnotationConfigWebApplicationContext

AnnotationConfigApplicationContext的一个WebApplicationContext变体是AnnotationConfigWebApplicationContext。当配置ContextLoaderListener 、DispatcherServlet时会使用这个实现类。下面是在web.xml中配置一个典型的Spring MVC web应用程序。

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

Composing Java-based configurations

Using the @Import annotation

@Configuration
public class ConfigA {

     @Bean
    public A a() {
        return new A();
    }

}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }

}
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

Conditionally including @Configuration classes or @Beans

通常根据系统的某个状态有条件地启用或禁用一个完整的@Configuration类或者@Bean方法是非常有用的。一种方式是使用@Profile注解来激活某个profile中的beans。
@Profile注解实际上是通过一个更灵活的@Conditional注解实现的。@Conditional会在bean注册之前会指定org.springframework.context.annotation.Condition实现。
Condition接口的实现也简单地提供了一个返回true或false的matches(…)方法。例如,这是使用 @Profile的实际的Condition实现:

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() != null) {
        // Read the @Profile annotation attributes
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                    return true;
                }
            }
            return false;
        }
    }
    return true;
}

Combining Java and XML configuration

Spring的@Configuration类目的不是在于完全替代Spring XML。Spring XML命名空间的一些功能仍然是配置容器的完美的方式。在这种情况下,XML是简单和必要的,你可以使用"XML-centric"的方式,例如使用ClassPathXmlApplicationContext,或者是"Java-centric",使用AnnotationConfigApplicationContext和@ImportResource注解导入XML。

XML-centric use of @Configuration classes

从XML启动Spring容器,然后包含@Configuration类是比较好的方式。例如,在代码量很大的使用Spring XML的应用程序中,根据需要创建@Configuration类并且把他们包含进存在的XML文件中是更容易的。

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }

}
system-test-config.xml
<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

因为@Configuration被@Component注解,@Configuration注解的类也可以会被自动扫描。

system-test-config.xml
<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

@Configuration class-centric use of XML with @ImportResource

在使用@Configuration类作为配置容器的基本机制的应用程序中,使用一些XML仍然是必要的。

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }

}
properties-config.xml
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

web应用程序中的ApplicationContext实例化

你可以声明式地创建ApplicationContext实例,通过使用ContextLoader。当然,你也可以在程序中创建ApplicationContext实例通过使用ApplicationContext实现类。
像下面这样使用ContextLoaderListener,你可以注册ApplicationContext。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- or use the ContextLoaderServlet instead of the above listener
<servlet>
    <servlet-name>context</servlet-name>
    <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
-->

这个监听器检测contextConfigLocation参数。如果参数不存在,监听器默认使用/WEB-INF/applicationContext.xml。当参数存在时,监听器通过使用预定的义界定符的分隔字符串,并且使用这些值作为地址共application contexts查找。你也可以使用ContextLoaderServlet代替ContextLoaderListener。Servlet也使用contextConfigLocation参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值