Spring文档

Spring Framework:https://spring.io/projects/spring-framework

本次阅读Spring 5.3.21:https://docs.spring.io/spring-framework/docs/current/reference/html/

由官网目录可知,大致可分为八个章节进行阅读:

image-20220711214809779

当前阅读进度:Core篇

Core Technologies

名词说明:

Inversion of Control (Ioc)

Aspect-Oriented Programming (AOP)

dependency injection(DI)

bean

dependency

configuration metadata

在Spring文档中,“factory bean”指的是在Spring容器中配置的bean,它通过实例或静态工厂方法创建对象。相比之下,FactoryBean(注意大写)指的是spring特定的FactoryBean实现类。

Spring框架有自己的AOP框架,它在概念上很容易理解,并且成功地解决了Java企业编程中AOP需求的80%的最佳点。

Spring集成了AspectJ。

1. The IoC Container

1.1. Introduction to the Spring IoC Container and Beans

IoC也被称为依赖注入(DI)。

IoC是一个过程,对象仅通过构造函数参数工厂方法的参数在对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系。

org.springframework.beansorg.springframework.context包是Spring框架IoC容器的基础。

BeanFactory接口提供了一种高级的配置机制,能够管理任何类型的对象。

ApplicationContext是BeanFactory的子接口。它具有:

  • 更容易与Spring的AOP特性集成
  • 消息资源处理(用于国际化)
  • 事件发布
  • 应用程序层特定的上下文,如WebApplicationContext用于web应用程序

ApplicationContext是BeanFactory的一个完整超集。

受Spring IoC容器管理的对象称为bean。

bean是由Spring IoC容器实例化、组装和管理的对象。

bean及其之间的依赖关系反映在容器使用的配置元数据中。

1.2. Container Overview

org.springframework.context.ApplicationContext接口代表着Spring IoC容器,并负责实例化、配置和组装bean。

容器通过读取配置元数据来获取关于实例化、配置和组装哪些对象的指令。

配置元数据用XML、Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。

Spring提供了ApplicationContext接口的几个实现。在单体应用程序中,通常会创建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的实例。

container magic

1.2.1. Configuration Metadata

配置元数据是作为一个开发者与container沟通的桥梁。

基于xml的配置元数据将这些bean配置为顶级<beans/>元素中的<bean/>元素。Java配置通常在@Configuration类中使用@bean注释的方法。

这些bean定义对应于构成应用程序的实际对象。通常在定义Service layer对象、data access objects (DAOs)、表示对象(如Struts Action instances, infrastructure objects such as Hibernate SessionFactories, JMS Queues等等。通常,不需要在容器中配置细粒度的domain对象,因为创建和加载domain对象通常是dao和业务逻辑的职责。

最基础的XML配置:

id属性是标识各个bean定义的字符串。(id属性可以用作被其他对象引用的唯一标识)

class属性定义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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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>
1.2.2. Instantiating a Container

提供给ApplicationContext构造器的 location path或paths是resource 字符串,容器可以从各种外部资源加载配置元数据,例如local file system、Java CLASSPATH等。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
//位于resource目录下的services.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- 通过ref引用daos.xml中的两个bean -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>
//位于resource目录下的daos.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>
Composing XML-based Configuration Metadata

通常,每个单独的XML配置文件代表体系结构中的一个逻辑层或模块。可以使用一次或多次<import/>元素从另一个或多个文件加载bean定义。

<beans>
    //当前目录
    <import resource="services.xml"/>
    //resources目录下找
    <import resource="resources/messageSource.xml"/>
    //忽略最前面的斜杠
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

namespace本身提供了导入指令特性。除了普通bean定义之外,Spring提供的一系列XML名称空间还提供了更多的配置特性—例如,contextutil

The Groovy Bean Definition DSL

1.2.3. Using the Container

ApplicationContext是高级工厂的接口,该工厂能够维护不同bean及其依赖项的注册表。

ApplicationContext允许您读取bean定义并访问它们,如下面的示例所示:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

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

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

最灵活的变体是GenericApplicationContext与Reader委托的组合——例如,XML文件的XmlBeanDefinitionReader,如下所示:

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

1.3. Bean Overview

Spring IoC容器管理一个或多个bean。这些bean是用您提供给容器的配置元数据创建的。

在容器本身内部,这些bean definitions 被表示为BeanDefinition对象,它包含且不仅限于以下元数据:

  • 类的全限定名:通常是需要定义的bean的实际实现类。
  • Bean行为配置元素,Bean在容器中需要执行特定的动作(范围、生命周期回调等等)。
  • 对bean执行所需的其他bean的引用。这些引用也称为协作者或依赖项。
  • 要对第三方配置类通过在新创建的对象中设置成符合适合自己项目的bean—例如,池的大小限制或在管理连接池的bean中使用的连接数。

bean definitions内的属性

详情可参见:org.springframework.beans.factory.config.BeanDefinition

PropertyExplained in…
ClassInstantiating Beans
NameNaming Beans
ScopeBean Scopes
Constructor argumentsDependency Injection
PropertiesDependency Injection
Autowiring modeAutowiring Collaborators
Lazy initialization modeLazy-initialized Beans
Initialization methodInitialization Callbacks
Destruction methodDestruction Callbacks

除了包含如何创建特定bean的信息的bean definitions 之外,ApplicationContext实现还允许注册在容器外部(由用户)创建的现有对象。它是通过getBeanFactory()方法访问ApplicationContext的BeanFactory来完成的,该方法返回DefaultListableBeanFactory实现。DefaultListableBeanFactory通过registerSingleton(…)和registerBeanDefinition(…)方法支持这种注册。但是,通常应用程序都只使用常规bean definitions元数据定义的bean,像这种由用户创建的情况较为少见,且官方并不推荐这种方式。【暂时没有从源码中验证这一段】

1.3.1. Naming Beans

每个bean可以有一个或多个标识符。这些标识符在bean的当前容器中必须是唯一的。一个bean一般只有一个标识符,如果它需要一个以上的标识符,则额外的可以被认为是别名。

在基于xml的配置元数据中,可以使用id属性、name属性或两者来指定bean标识符。id属性允许您指定一个id。按照惯例,这些名称是字母数字(‘myBean’, ’ someeService '等,按Java程序员的习惯一般都是小驼峰命名,这是官方推荐的写法)【一般情况组件会取简单类名并将第一个字符从大写转换为小写,但是在特殊情况下,即当有多个字符并且第一个和第二个字符都是大写字符时,将保留原来的大小写。全是小写也一样。】,但它们也可以包含特殊字符。如果希望引入bean的其他别名,还可以在name属性中指定它们,用逗号(,)、分号(;)或空格分隔【进一步证明name属性可以有多个,而且是唯一的标识符】。

如果您没有为bean提供name或id,容器会自动为该bean生成一个唯一的name。但是,如果想通过name引用该bean,则需要使用ref element 或Service Locator style 查找,则必须提供name。不提供name的原因与 inner beans 的使用和 autowiring collaborators有关。

Aliasing a Bean outside the Bean Definition

指定bean实际定义的所有别名并不总是足够的,有时需要为在其他地方定义的bean引入别名。这种情况在大型系统中很常见,配置被划分到每个子系统中,每个子系统有自己的一组对象定义。在基于xml的配置元数据中,可以使用<alias/>元素来完成此任务。下面的例子展示了如何这样做:

<alias name="fromName" alias="toName"/>

在该案例中,在使用别名定义之后,名为fromName的bean在同一个容器中可以被称为toName。

例如:子系统A的配置元数据可能以subsystemA-dataSource的名称引用数据源。子系统B的配置元数据可以通过subsystemB-dataSource的名称引用数据源。当组合使用这两个子系统的主应用程序时,主应用程序以myApp-dataSource的名称引用数据源。要让这三个名称都指向同一个对象,可以在配置元数据中添加以下别名定义:

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

现在,每个组件和主应用程序都可以通过一个唯一的名称引用dataSource,该名称保证不会与任何其他定义(实际上创建了一个名称空间)冲突,但它们引用的是同一个bean。【当然也可以使用Java注解配置@Bean中的别名进行定义】

1.3.2. Instantiating Beans

原文引用:

A bean definition is essentially a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.

bean定义本质上是创建一个或多个对象的方法。容器在被请求时查看指定bean的配方,并使用该bean定义封装的配置元数据来创建(或获取)实际对象。【我觉得recipe翻译成模板可能更合理一点】

如果使用xml的配置元数据,则需要在<bean/>元素的class属性中指定要实例化的对象的类型(或类)。这个类属性(在内部是BeanDefinition实例上的class属性)通常是强制性的。使用Class属性有两种方式:

  • 通常,在容器本身通过反射机制调用其构造函数来直接创建bean的情况下,指定要构造的bean类,这在某种程度上相当于使用new操作符的Java代码。
  • 要指定包含用于创建对象的静态工厂方法的实际类,在较不常见的情况下,容器调用类上的静态工厂方法来创建bean。调用静态工厂方法返回的对象类型可以是同一个类,也可以完全是另一个类。

如果您希望为内部类配置bean定义,您可以使用内部类的二进制名称或源名称。

例如,如果您在 com.example 包下有一个名为SomeThing的类。这个SomeThing类有一个静态内部类OtherThing,它们可以用一个美元符号( ) 或一个点 ( . ) 分隔。因此, b e a n 定义中的 c l a s s 属性值可以是 ‘ c o m . e x a m p l e . S o m e T h i n g )或一个点(.)分隔。因此,bean定义中的class属性值可以是 `com.example.SomeThing )或一个点(.)分隔。因此,bean定义中的class属性值可以是com.example.SomeThingOtherThing 或者com.example.SomeThing.OtherThing`。

Instantiation with a Constructor

当您通过构造函数方法创建bean时,Spring可以使用并兼容所有普通类。也就是说,正在开发的类不需要实现任何特定的接口,也不需要以特定的方式编码。简单地指定bean类就足够了。但是,根据对特定bean使用的IoC类型的不同,可能需要一个默认(空)构造函数。

Spring IoC容器实际上可以管理您希望它管理的任何类。它并不仅限于管理真正的JavaBean。大多数Spring用户更喜欢实际的JavaBean(只有一个默认(无参数)构造函数,并根据容器中的属性创建相应的setter和getter)。

使用xml的配置元数据,您可以如下所示指定bean类:

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

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

此处为使用类的默认空构造器创建对应的bean,当然也可以通过有参构造器注入相应的依赖。

Instantiation with a Static Factory Method

在定义使用静态工厂方法创建的bean时,使用class属性来指定包含静态工厂方法的类,并使用factory-method属性来指定静态工厂方法名。这种实例化方式也会被当成构造注入,没有携带参数默认是无参构造。

下面的bean定义指定该bean将通过调用工厂方法来创建。定义没有指定返回对象的类型(类),而是指定包含工厂方法的类。在这个例子中,createInstance()方法必须是一个静态方法。下面的例子展示了如何指定一个工厂方法:

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

该实例化方式同样可以有参构造注入。

Instantiation by Using an Instance Factory Method

与通过静态工厂方法进行的实例化类似,使用实例工厂方法进行的实例化从容器中调用现有bean的非静态方法来创建新bean。要使用此机制,需要将class属性保留为空,并在factory-bean属性中指定包含用于创建对象的实例方法的当前(或父或祖先)容器中的bean名称。使用factory-method属性设置工厂方法本身的名称。下面的例子展示了如何配置这样一个bean:

<!-- 工厂方法Bean,包含有实例化方法createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- 该Bean会通过指定的factory bean携带的实例化方法createClientServiceInstance()进行实例化 -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一个工厂类也可以包含多个工厂方法,如下所示:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

这种方法表明工厂bean本身可以通过依赖注入(DI)进行管理和配置。

Determining a Bean’s Runtime Type

特定bean的运行时类型的确定并不简单。bean metadata definition中的指定类只是一个初始类引用,可能与声明的工厂方法结合在一起,或者是可能导致bean的不同运行时类型的FactoryBean类,或者在实例级工厂方法(而是通过指定的工厂bean名称解析)的情况下根本不设置。此外,AOP代理可以用基于接口的代理包装一个bean实例,该代理有限地公开目标bean的实际类型(只公开其实现的接口)。

原文引用:

The recommended way to find out about the actual runtime type of a particular bean is a BeanFactory.getType call for the specified bean name. This takes all of the above cases into account and returns the type of object that a BeanFactory.getBean call is going to return for the same bean name.

推荐找到实际运行时类型的方式是通过BeanFactory.getType调用具体的Bean名。这将考虑上述所有情况,并返回BeanFactory的对象类型。getBean调用将返回相同的bean名称。【翻译的很奇怪,后面想起来的再回来修改。】

插一句:这是读文档的第二天,这是我读这篇Spring文档的第二遍了,是真的感觉到很枯燥很难熬。但是我还想再坚持几天看看结果到底怎样。

1.4. Dependencies

通常的企业应用程序不可能仅由单个对象(bean)组成的。即使是最简单的应用程序也有几个对象一起工作,以呈现最终用户眼中的一致应用程序。下一节将解释如何从定义许多独立的bean定义到完全实现的应用程序(其中对象协作以实现目标)。

1.4.1. Dependency Injection

IoC是一个过程,对象仅通过构造函数参数工厂方法的参数在对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系。

当对象与它们的依赖项一起提供时使用依赖注入原则可以使得代码更加整洁,解耦会更有效。对象不需要查找其依赖项,也不需要知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系在接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。

DI主要有两种形式:构造函数的依赖注入和setter的依赖注入。

Constructor-based Dependency Injection

基于构造函数的DI是由容器调用带有许多参数的构造函数来完成的,每个参数代表一个依赖项。调用带有特定参数的静态工厂方法来构造bean与其几乎是等价的,本文将以类似的方式处理构造函数和静态工厂方法的参数。下面的例子展示了一个只能通过构造函数注入来进行依赖注入的类:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

Constructor Argument Resolution

构造函数参数解析匹配在使用参数的类型是发生。如果bean定义的构造函数参数中不存在潜在的歧义,那么在bean definition中定义构造函数参数的顺序就是在实例化bean时将这些参数提供给适当的构造函数的顺序。考虑下面的类:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

假设ThingTwo和ThingThree类通过继承不相关,那么就不存在潜在的歧义。因此,下面的配置工作得很好,并且您不需要在<constructor-arg/>元素中显式地指定构造函数参数索引或类型。

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

当引用另一个bean时,类型是已知的,可以进行匹配(如上例所示)。当使用简单类型时,例如<value>true</value>, Spring无法确定值的类型,因此无法在没有帮助的情况下通过类型进行匹配。考虑下面的类:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

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

Constructor argument type matching

在上述场景中,如果使用type属性显式指定构造函数实参的类型,容器可以使用简单类型匹配的类型,如下所示:

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

Constructor argument index

你可以使用index属性来显式指定构造函数参数的索引,如下面的例子所示:

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

除了解决多个简单值的不确定性外,指定索引还可以解决构造函数具有两个相同类型参数时的不确定性。【索引从0开始】

Constructor argument name

您还可以使用构造函数参数名来消除值歧义,如下示例所示:

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

请记住,要使此功能开箱即用,必须在编译代码时启用调试标志,以便Spring可以从构造函数中查找参数名称。如果你不能或不想使用调试标志来编译代码,也可以使用@ConstructorProperties JDK注解显式地命名构造函数参数。然后,示例类必须如下所示:

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
Setter-based Dependency Injection

基于setter的DI是由容器在调用无参数构造函数或无参数静态工厂方法来实例化bean之后调用bean中的setter方法来完成的。

下面的例子展示了一个只能通过使用纯setter注入来进行依赖注入的类。这个类是Java传统的不依赖于容器特定接口、基类或注解的POJO。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

ApplicationContext为其管理的bean支持基于构造函数和基于setter的DI。它还支持在已经通过构造函数方法注入了一些依赖项之后,继续基于setter的DI。您以BeanDefinition的形式配置依赖项,您可以将其与PropertyEditor实例一起使用,以将属性从一种格式转换为另一种格式。然而,大多数Spring用户并不直接(即通过编程方式)使用这些类,而是使用XML bean定义、带注解的组件(即使用@Component、@Controller等注解的类)或基于java的@Configuration类中的@Bean方法。然后将这些源在内部转换为BeanDefinition的实例,并用于加载整个Spring IoC容器实例。

Dependency Resolution Process

容器执行bean依赖解析流程如下:

  • 创建和初始化ApplicationContext的同时记录所有bean的配置元数据。配置元数据可以是XML、Java代码或注解形式的。

  • 对于每个bean,其依赖关系以属性、构造函数参数或静态工厂方法参数的形式表示(如果使用静态工厂方法而不是普通构造函数)。这些依赖项是在实际创建bean时提供给bean的。

  • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。

  • 每个属性或构造函数参数都从指定格式转换为该属性或构造函数参数的实际类型的值。默认情况下,Spring可以将字符串格式提供的值转换为所有内置类型,例如 int, long, String, boolean 等。

Spring容器在创建容器时就会验证每个bean的配置。但是,只有在实际创建该bean实例时才会设置bean属性。 singleton-scoped 并设置为**预实例化(默认)**的bean是在创建容器时创建的,否则只有在请求bean时才创建它。创建一个bean可能会由于创建和分配了bean的依赖及其依赖的依赖导致创建出一个bean图。请注意,这些依赖项之间的解析不匹配可能会在较晚的时候出现,即在第一次创建受影响的bean时。

Circular dependencies【循环依赖,有空回来再写】

您通常可以相信Spring会做正确的事情。它在容器加载时检测配置问题,例如对不存在的bean的引用和循环依赖项。Spring在bean实际创建时设置属性并解析依赖项。这意味着,正确加载的Spring容器可以在稍后请求对象时生成异常,如果创建该对象或其依赖项存在问题,例如,bean由于缺少或无效属性而抛出异常。某些配置问题的可见性可能会延迟,这就是ApplicationContext实现默认预实例化单例bean的原因。在实际需要这些bean之前创建这些bean需要花费一些前期时间和内存,您会在创建ApplicationContext时发现配置问题,而不是稍后。您仍然可以重写此默认行为,以便单例bean延迟初始化,而不是提前实例化。

如果不存在循环依赖项,那么当一个或多个协作bean被注入到依赖bean中时,每个协作bean在注入到依赖bean之前都要完全配置。这意味着,如果bean A对bean B有依赖,Spring IoC容器在调用bean A上的setter方法之前完全配置了bean B。换句话说,bean被实例化(如果它不是预实例化的单例)时,它的依赖项就被设置了,并且相关的生命周期方法(例如配置的init方法或InitializingBean回调方法)也会被调用。

Examples of Dependency Injection

下面的示例使用基于xml的配置元数据进行基于setter的DI。Spring XML配置文件的一小部分指定了一些bean定义如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter方法的内嵌标签ref注入 -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter方法的内嵌属性ref注入 -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

在前面的示例中,声明了与XML文件中指定的属性匹配的setter。下面的例子使用了基于构造函数的DI:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor的内嵌标签ref注入 -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor的内嵌属性ref注入 -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

现在考虑这个例子的一个变体,其中,Spring被告知调用一个静态工厂方法来返回对象的一个实例,而不是使用构造函数:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {

    // a private constructor
    private ExampleBean(...) {
        ...
    }

    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}

静态工厂方法的参数由<constructor-arg/>元素提供,与实际使用的构造函数完全相同。工厂方法返回的类的类型不必与包含静态工厂方法的类的类型相同(尽管在本例中是相同的)。也可以使用实例工厂方法完成相应的操作,本处不再讨论这些。

1.4.2. Dependencies and Configuration in Detail

如上一节所述,您可以将bean属性和构造函数参数定义为对其他bean的引用或内置类型的值。为此,Spring基于xml的配置元数据在其<property/><constructor-arg/>元素中支持子元素类型。

Straight Values (Primitives, Strings, and so on)

<property/>标签的value属性可以将属性或构造函数参数通过字符串表示。Spring的转换服务可以将这些值从String转换为属性或参数的实际类型。下面的例子显示了正在设置的各种值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>

下面的示例可以使用p-namespace进行更简洁的XML配置:

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

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="misterkaoli"/>

</beans>

你也可以配置 java.util.Properties 实例,如下所示:

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

Spring容器通过使用JavaBeans的 PropertyEditor机制将<value/>元素中的文本转换为java.util.Properties实例。这是一个很好的快捷方式,也是Spring团队喜欢使用嵌套的<value/>元素而不是值属性样式的少数几个地方之一。

The idref element

idref元素只是将容器中另一个bean的id(与value对比:仅仅是字符串,而不是引用)传递给<constructor-arg/><property/>元素的一种防错方法。下面的例子展示了如何使用它:

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

等价片段:

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>

第一种形式比第二种形式更可取,因为使用idref标签可以让容器在部署时验证被引用的命名bean是否确实存在。在第二个片段中,没有对传递给客户端bean的targetName属性的值执行验证。只有在实际实例化客户端bean时才会发现检测出错误(极有可能导致致命的结果)。如果客户端bean是一个 prototype bean,那么这个错误和产生的异常只有在容器部署很久之后才会被发现。

【idref标签中的local属性不要再用了,由bean属性替换了】

<idref/>标签所携带的值(至少在Spring 2.0之前的版本中)是在ProxyFactoryBean bean definition中的AOP拦截器的配置中。在指定拦截器名称时使用<idref/>元素可以防止将拦截器ID拼错。

References to Other Beans (Collaborators)

ref标签是<constructor-arg/><property/>定义标签中的最后一个标签。在这个标签中,您可以将一个bean的指定属性的值设置为同样受容器管理的另一个bean的引用。被引用的bean将会被设置成bean的属性,即作为该bean一个依赖项,并且在设置成属性之前需要对其进行初始化【如果没有在这之前没有被初始化的话】。所有的引用最终都是对另一个对象的引用。Scoping 和 validation 取决于您是否通过 bean 属性或 parent 属性指定其他对象的ID或name。

通过<ref/>标签的bean属性指定目标bean是最通用的形式,它允许在同一容器或父容器中创建对任何bean的引用,而不管它是否在同一个XML文件中。bean属性的值可能与目标bean的id属性相同,或者与目标bean的name属性中的某个值相同。下面的例子展示了如何使用ref元素:

<ref bean="someBean"/>

通过parent属性指定目标bean将创建对当前容器父容器中的bean的引用。父属性的值可能与目标bean的id属性或目标bean的name属性中的一个值相同。目标bean必须位于当前bean的父容器中。当您拥有容器的层次结构,并且您希望使用与父bean同名的代理将现有的bean包装在父容器中时,您应该主要使用此bean引用变体。下面的两个片段展示了如何使用parent属性:

<!-- 父容器中 -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required here -->
</bean>
<!-- 子容器中 -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>
Inner Beans

可以一个在<bean/>标签中的<property/><constructor-arg/>标签的内部使用<bean/>标签定义一个内部bean,如下所示:

<bean id="outer" class="...">
    <!-- 代替ref标签,定义一个简单的内部bean在outer bean的target属性中,此处使用的setter,也可以使用构造器-->
    <property name="target">
        <bean class="com.example.Person"> <!-- 内部bean标签 -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

内部bean定义的时候不需要指定的id和name属性,容器会忽略内部bean的这些标识符。容器在创建时也会忽略 scope 这个标识,因为内部bean总是匿名的,并且总是用外部bean创建的。不可能单独访问内部bean,也不可能将它们注入到协作bean中,而不是外围bean中。

作为一种极端情况,可以从自定义的 scope  中接收到销毁回调——例如,一个singleton bean中包含一个request-scoped的内部bean。内部bean实例和外部bean一起创建,但是却在request-scoped的生命周期中销毁回调。这种情况并不常见。内部bean通常只是简单的共享包含它们的bean的scoped。

Collections

<list/>, <set/>, <map/>, 和<props/> 标签分别设置Java Collection 类型 List, Set, Map, 和Properties, 的属性和参数。下面的例子展示了如何使用它们:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

无论是设置map的key或者value还是设置value的值,都可以使用下列标签中的任何一个进行填充【感觉相当于预设一些特殊的或者常用的标签给别人使用】:

bean | ref | idref | list | set | map | props | value | null //这些都是标签
Collection Merging

Spring容器还支持合并 Collection 。开发人员可以定义父标签 <list/>, <map/>, <set/><props/> ,并让子标签 <list/>, <map/>, <set/><props/> 继承并覆盖父标签 Collection 的值。也就是说,子 Collection 的值是父 Collection 和子 Collection 的标签合并的结果,子 Collection 的标签覆盖父 Collection 中指定的值。

关于合并的这一节讨论了父子bean机制。下面的例子演示了集合合并:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>

注意,在子bean definition 的 adminEmails 属性的 <props/> 元素上使用了merge=true属性。当容器解析并实例化子bean时,实例中会存在一个 adminEmails Properties集合,其中包含子bean的 adminEmails 集合与父bean的 adminEmails 集合合并的结果。下面的清单显示了结果:

administrator=administrator@example.com //继承下来的
sales=sales@example.com
support=support@example.co.uk //重写过的

Properties 集合的值集继承父元素<props/>中的所有属性元素,并且子元素的支持值覆盖父集合中的值。

Limitations of Collection Merging

不能合并不同的 Collection 类型(例如Map和List),否则将会抛出一个适当的Exception。merge属性必须在较低层次的、继承的子定义上指定。在父集合定义上指定merge属性是多余的,并且不会导致所需的合并。

Strongly-typed collection

由于Java对泛型类型的支持,您可以使用强类型集合。也就是说,可以声明一个Collection类型,使其只能包含(例如)String元素。如果您使用Spring来依赖—将强类型集合注入到bean中,那么您可以利用Spring的类型转换支持,这样强类型集合实例的元素在添加到集合之前就被转换为适当的类型。下面的Java类和bean definition 展示了如何这样做:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

something bean的accounts属性准备好注入时,其中的强类型Map<String, Float>元素类型的泛型信息可以通过反射获得。因此,Spring的类型转换基础结构将各种值元素识别为Float类型,并将字符串值(9.99、2.75和3.99)转换为实际的Float类型。

Null and Empty String Values

Spring将属性等的空参数视为空字符串。下面的基于xml的配置元数据片段将email属性设置为空字符串值(“”)。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
exampleBean.setEmail("");//与上面片段等价

可以通过<null/>标签处理空值。如下示例:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>
exampleBean.setEmail(null);//与上面片段等价
XML Shortcut with the p-namespace

p标签可以替换 <property/>标签的内容,但需要引入额外的命名空间,如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"  //额外引入的命名空间
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="someone@somewhere.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/> //两个bean等价
</beans>

如下示例包含另外两个bean definitions ,它们都引用另一个bean:

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

    <bean name="john-classic" class="com.example.Person">//正常引用
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>//使用p命名空间引用

    <bean name="jane" class="com.example.Person">//被引用的bean
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

这个示例不仅包含使用p-namespace的属性值,而且还使用一种特殊的格式来声明属性引用。第一个bean定义使用<property name="spouse" ref="jane"/>来创建从bean john到bean jane的引用,而第二个bean定义使用p:spouse-ref="jane"作为属性来完成完全相同的任务。在本例中,spouse是属性名,而-ref部分表示这不是一个直接的值,而是对另一个bean的引用。

【相对于原本的引用方式,p命名空间给我们提供了更多的选择,但就可读性而言,我个人觉得还是原本的标签使用方式更加直观且便于管理】

XML Shortcut with the c-namespace

c标签可以替换 constructor-arg 标签的内容,但需要引入额外的命名空间,如下所示:

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

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- 正常构造引用定义 -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>

    <!-- c-namespace定义参数 -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

c:p:作用类似。

如果构造参数名字不可用的情况下,可以使用索引下标【作为索引必须要由 _ 开头,如下所示:

<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="something@somewhere.com"/>

【相对于原本的引用方式,c命名空间给我们提供了更多的选择,但就可读性而言,我个人觉得还是原本的标签使用方式更加直观且便于管理】

Compound Property Names

在设置bean属性时,可以使用复合或嵌套属性名称,只要路径的所有组件(除最终属性名称外)不为空。考虑下面的bean定义:

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>

something bean有一个fred属性,fred属性有一个bob属性,bob属性有一个sammy属性,最后的sammy属性被setter注入值为123。为了使其工作,somethingfred 属性和 fredbob 属性在bean构建后不能为空。否则,将抛出NullPointerException。

1.4.3. Using depends-on

在定义bean依赖的时候,通常是需要使用ref进行引用加载,但是依赖的加载需要解析,而有些依赖又是间接的不太明显的,所以需要在初始化使用此该标签的bean之前,使用depend -on属性显式地强制初始化一个或多个bean。下面的例子使用depends-on属性来表示对单个bean的依赖:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

要表达对多个bean的依赖,请提供一个bean名称列表作为依赖属性的值(逗号、空格和分号是有效的分隔符):

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">//指定了强制加载但可以不注入
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

原文引用:

The depends-on attribute can specify both an initialization-time dependency and, in the case of singleton beans only, a corresponding destruction-time dependency. Dependent beans that define a depends-on relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thus, depends-on can also control shutdown order.

看起来好像挺重要的,后面再回来看看。

1.4.4. Lazy-initialized Beans

默认情况下,ApplicationContext 在进行初始化的过程中,会将所有 singleton bean 创建并进行配置。如果不想要在应用程序启动之时就创建的话,可以在 bean definition 中将该bean标记为 lazy-initialized,这样该bean会在应用第一次请求时才会首次创建实例,而不是刚启动时就创建实例。

在XML中,这种行为由<bean/>标签上的 lazy-init 属性控制,如下所示:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

当上述配置被ApplicationContext使用并且ApplicationContext在启动时,lazy bean不会立即预实例化,而not.lazy bean是会被预实例化的。

但是当 lazy bean 作为 not.lazy bean的依赖时,在 not.lazy bean被预实例化时,同样需要预实例化 lazy bean。

您还可以通过在<beans/>标签上使用 default-lazy-init 属性来控制容器级别的 lazy-initialization,如下面的例子所示:

<beans default-lazy-init="true">
    <!-- 内部的bean默认不会被预实例化 -->
</beans>
1.4.5. Autowiring Collaborators

Spring容器可以根据协作bean之间的关系自动装配。您可以通过检查ApplicationContext的内容,让Spring自动为您的bean解析协作者(其他bean)。自动装配有以下优点:

  • 自动装配可以显著减少指定属性或构造函数参数的需要。
  • 自动装配可以随着对象的变更而更新配置。例如,如果您需要向类添加依赖项,那么无需修改配置就可以自动满足该依赖项。因此,自动装配在开发过程中特别有用,而不用在代码库变得更稳定时切换到显式连接。

在基于xml的配置元数据中,您可以使用<bean/>标签的 autotowire 属性为 bean definition 指定自动装配模式。自动装配功能有四种模式。您可以为每个bean指定自动装配,这样就可以选择要自动装配的bean。下表描述了四种自动装配模式:

ModeExplanation
no(默认)没有自动装配。Bean引用必须由ref 标签定义。对于较大的部署,不建议更改默认设置,因为显式指定协作者会提供更大的控制和清晰度。在某种程度上,它记录了系统的结构。
byName通过属性名自动装配。Spring寻找与需要自动装配的和属性同名的bean。例如,如果一个bean definition 被设置为按名称自动装配,并且它包含一个master属性(也就是说,它有一个setMaster(…)方法),Spring会寻找一个名为master的bean definition,并将它设置为该属性。
byType如果容器中只存在一个属性类型的bean,则允许该属性自动装配。如果存在多个,则会抛出一个 fatal exception ,这表明您可能不会对该bean使用byType自动装配。如果没有匹配的bean,则什么也不会发生(属性没有设置)。
constructor类似于byType,但适用于构造函数参数。如果容器中没有构造函数参数类型的bean,则会引发 fatal exception 。

原文引用:

With byType or constructor autowiring mode, you can wire arrays and typed collections. In such cases, all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Map instances if the expected key type is String. An autowired Map instance’s values consist of all bean instances that match the expected type, and the Map instance’s keys contain the corresponding bean names.

使用 byTypeconstructor 自动装配模式,可以注入数组和集合类型。在这种情况下,将提供容器中所有匹配预期类型的自动装配候选者,以满足依赖关系。如果预期的键类型是String,您可以自动装配强类型的Map实例。一个自动装配的Map实例的值由匹配预期类型的所有bean实例组成,Map实例的键包含相应的bean名称。

Limitations and Disadvantages of Autowiring

自动装配在项目中最好一直使用。

考虑自动装配的局限性和缺点:

  • propertyconstructor-arg 设置中的显式依赖总是覆盖自动装配所注入的。您无法自动装配简单属性,如基本类型、Strings, 和 Classes(以及此类简单属性的数组)。这是设计上的限制。
  • 自动装配不如显式注入精确。尽管如此,正如前面的表中所指出的,Spring小心地避免猜测,以防出现可能产生意外结果的歧义。spring管理对象之间的关系不再显式地记录。
  • 装配的信息可能无法用于从Spring容器生成文档的工具。
  • 容器内的多个 bean definitions 可以匹配由要自动装配的setter方法或构造函数参数指定的类型。对于数组、集合或Map实例,这并不一定是问题。然而,对于期望单个值的依赖项,这种不确定性不是任意解决的。如果没有唯一的bean定义可用,则会引发异常。

在后一种情况下,你有几个选择:

  • 放弃自动装配选择显示注入。
  • 通过将 bean definition 的autowire-candidate属性设置为false来避免自动装配,具体看下一节。
  • 通过将<bean/>元素的 primary 属性设置为true,将单个bean定义指定为主要候选。
  • 通过基于注解的配置实现更细粒度的控制。
Excluding a Bean from Autowiring

可以在每一个bean上配置将之从自动装配中排除。如在XML中的将<bean/>标签中的 autowire-candidate 属性设置为false。容器将会使得该bean definition对不再支持自动装配(包括包括注解配置,如@Autowired)。

autowire-candidate属性被设计为只影响基于类型自动装配。它不会影响按名称的显式引用,并且自动装配的如果设计为按名称匹配的模式同样可以继续注入。

您还可以基于对bean名称的模式匹配来限制自动装配候选者。顶级<beans/>标签在其 default-autowire-candidates 属性中接受一个或多个模式。例如,要将自动装配候选状态限制为名称以Repository结尾的任何bean,请提供 *Repository 值。如果需要提供多个模式,可以使用逗号分隔。bean definition 的autowire-candidate属性的显式值true或false总是优先。因此对于这类bean,模式匹配规则不适用。

原文引用:

These techniques are useful for beans that you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured by using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.

这些技术对于您永远不希望通过自动装配注入到其他bean中的bean非常有用。这并不意味着被排除的bean本身不能通过使用自动装配进行配置。相反,bean本身并不是自动装配其他bean的候选者。【有点混乱,暂时没法理解】

1.4.6. Method Injection

在大部分应用场景中,容器中的bean基本都是单例的,当 singleton bean A 作为 singleton bean B 的依赖,或者 non-singleton bean A 作为 non-singleton bean B 的依赖,通常都是将 A 设置为 B 的属性来处理依赖关系。

当bean的生命周期不同时,就出现了一些问题。比如一个singleton bean A 需要使用 non-singleton (prototype) bean B 作为依赖时,容器只会创建一次 A ,因此只会设置一次属性,但是这样容器就不能在每次需要 B 的新实例时就为 A 提供一个。

其中一个解决方案是放弃一些控制反转(inversion of control)。您可以通过实现 ApplicationContextAware 接口使bean A知道,并在bean A每次需要bean B实例时向容器使用 getBean(“B”) 方法来请求bean B的新实例。下面的例子展示了这种方法:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // 获取 Command bean 的新实例
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected 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框架耦合了。Method Injection 是Spring IoC容器的一种比较高级的特性,它允许您干净地处理这个用例。

Lookup Method Injection

Lookup method injection 是容器用来重写容器管理bean上的方法并返回容器中另一个命名bean的查找结果。查找通常涉及一个 prototype bean,如上一节描述的场景所示。Spring框架通过使用从CGLIB库生成字节码来动态生成重写该方法的子类来实现这种方法注入。

原文引用:一些使用方法注入的注意事项,此处懒得翻译了

  • For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.
  • Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
  • Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
  • A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.

对于前面代码片段中的CommandManager类,Spring容器动态覆盖createCommand()方法的实现。CommandManager类没有任何Spring依赖,如重做的示例所示:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // 获取 Command bean 的新实例
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // 此处倒是没有与业务代码耦合了,但是这个方法的实现去哪了?
    protected abstract Command createCommand();
}

在需要方法注入的客户端类中(本例中是CommandManager),要注入的方法需要符合以下格式:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法是抽象的,则动态生成的子类实现该方法。否则,动态生成的子类将重写在原始类中定义的具体方法。考虑下面的例子:

<!-- 一个prototype (non-singleton) bean -->
<bean id="myCommand" 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="myCommand"/>
</bean>

标识为commandManager的bean在需要myCommand bean的新实例时调用它自己的createCommand()方法。如果实际需要的话,您必须小心地将myCommand bean部署为原型。如果是单例,则每次返回myCommand bean的相同实例。

或者,在基于注解的组件模型中,您可以通过`@Lookup 注解声明一个查找方法,如下面的示例所示:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者,更惯用的做法是,您可以依赖于目标bean根据查找方法声明的返回类型进行解析:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    //自动根据返回类型Command查找方法注入【感觉不是很好。。】
    @Lookup
    protected abstract Command createCommand();
}

原文引用:

Note that you should typically declare such annotated lookup methods with a concrete stub implementation, in order for them to be compatible with Spring’s component scanning rules where abstract classes get ignored by default. This limitation does not apply to explicitly registered or explicitly imported bean classes.

注意,您通常应该用具体的存根实现来声明这种带注解的查找方法,以便它们与Spring的组件扫描规则兼容,其中抽象类在默认情况下会被忽略。这种限制不适用于显式注册或显式导入的bean类。

访问不同作用域的bean的另一种方法是 ObjectFactory/ Provider 注入点。

你可能还会发现 ServiceLocatorFactoryBean (在 org.springframework.beans.factory.config 包中)也很有用。

Arbitrary Method Replacement

原文引用:

A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. You can safely skip the rest of this section until you actually need this functionality.

与查找方法注入相比,方法注入的一种用处较小的形式是能够用另一种方法实现替换托管bean中的任意方法。您可以安全地跳过本节的其余部分,直到您真正需要这个功能为止。【那我就真的跳了喔】

With XML-based configuration metadata, you can use the replaced-method element to replace an existing method implementation with another, for a deployed bean. Consider the following class, which has a method called computeValue that we want to override:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

A class that implements the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition, as the following example shows:

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

The bean definition to deploy the original class and specify the method override would resemble the following example:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

You can use one or more <arg-type/> elements within the <replaced-method/> element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String:

java.lang.String
String
Str

Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by letting you type only the shortest string that matches an argument type.

1.5. Bean Scopes

原文引用:

When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.

【recipe个人觉得翻译成模板更好一点。】

当您创建一个bean definition 时,您创建了一个用于创建由该bean definition 定义的类的实际实例模板。bean definition 是一个模板的想法很重要,因为这意味着您可以从一个模板来创建许多对象实例。

您不仅可以控制插入到从特定bean definition 创建的对象中的各种依赖项和配置值,还可以控制从特定bean definition 创建的对象的scope 。这种方法功能强大且灵活,因为您可以通过配置选择创建的对象的scope ,而不是使用在Java类级别固定对象的scope 。可以将bean定义为部署在多个scope 中的一个。Spring框架支持6个scope ,其中4个只有在使用web环境中的ApplicationContext时才可用。当然也可以创建自定义 scope 。

支持的scope 如下表所示:

ScopeDescription
singleton(默认)每个Spring IoC容器的每一个bean definition只存在一个对象实例。
prototype一个bean definition可以有多个对象实例。
request将单个bean definition 作用于单个HTTP request 的生命周期中。也就是说,每个HTTP request 都有自己的基于单个bean definition 创建的bean实例。该 scope 只在web环境的Spring ApplicationContext中有效。
session将单个bean definition 作用于HTTPSession的生命周期。该 scope 只在web环境的Spring ApplicationContext中有效。
application将单个bean definition 作用于HTTP ServletContext 的生命周期。该 scope 只在web环境的Spring ApplicationContext中有效。
websocket将单个bean definition 作用于HTTP WebSocket 的生命周期。该 scope 只在web环境的Spring ApplicationContext中有效。

从Spring 3.0开始,thread scope 是可用的,但默认情况下没有注册。有关更多信息,请参阅 SimpleThreadScope 的文档。有关如何注册此或任何其他自定义作用域的说明,请参阅 Using a Custom Scope

1.5.1. The Singleton Scope

一个 singleton bean 只会有一个实例被管理,当 singleton bean第一次被创建之后,会被存储再内存中等待被调用,下图展示了单例作用域是如何工作的:

singleton

原文引用:

Spring’s concept of a singleton bean differs from the singleton pattern as defined in the Gang of Four (GoF) patterns book. The GoF singleton hard-codes the scope of an object such that one and only one instance of a particular class is created per ClassLoader. The scope of the Spring singleton is best described as being per-container and per-bean. This means that, if you define one bean for a particular class in a single Spring container, the Spring container creates one and only one instance of the class defined by that bean definition. The singleton scope is the default scope in Spring. To define a bean as a singleton in XML, you can define a bean as shown in the following example:

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

大致表达的就是Spring中所提到的单例和GoF所提的单例模式是不一样的,并且解释了一些区别,感觉不是很重要,这里就不做翻译了。

1.5.2. The Prototype Scope

non-singleton prototype scope 的bean在每次请求的时候才会创建一个新的实例并返回结果。按照规则,您应该对所有有状态bean使用原型作用域,对无状态bean使用单例作用域。

下图说明了Spring prototype 的范围(一般DAO层不会配置原型scope,这里只是为了重用上面的图):

prototype

下面的例子将bean定义为XML中的原型:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

与其他作用域相比,Spring并不管理原型bean的完整生命周期。容器实例化、配置或以其他方式组装一个原型对象并将其交给客户端,而不需要该原型实例的进一步记录。因此,尽管初始化生命周期回调方法在所有对象上都被调用而不考虑作用域,但在原型的情况下,配置的销毁生命周期回调不会被调用。客户端代码必须清理原型作用域的对象,并释放原型bean持有的昂贵资源。要让Spring容器释放由原型作用域bean持有的资源,请尝试使用自定义 bean post-processor ,它持有对需要清理的bean的引用。

在某些方面,Spring容器对于原型作用域bean的作用是Java new操作符的替代品。超过该点的所有生命周期管理都必须由客户机处理。

1.5.3. Singleton Beans with Prototype-bean Dependencies

当您使用具有依赖原型bean的单例作用域bean时,请注意依赖关系在实例化时被解析。因此,如果您将原型作用域的bean依赖注入到单例作用域的bean中,则会实例化一个新的原型bean,然后将依赖注入到单例bean中。原型实例是提供给单例作用域bean的唯一实例。

但是原型实例本该是在运行时每次调用都会创建新的实例,可以参考前面的 Method Injection

1.5.4. Request, Session, Application, and WebSocket Scopes

request, session, application, 和 websocket scopes 只有在使用具有web环境的Spring ApplicationContext实现(如XmlWebApplicationContext)时才可用。如果在常规Spring IoC容器(如ClassPathXmlApplicationContext)中使用这些作用域,则会抛出一个未知bean作用域异常 IllegalStateException

Initial Web Configuration

为了支持bean在 request, session, application, 和 websocket 级别(web范围的bean)的作用域,在定义bean之前需要进行一些的初始配置。(对于标准作用域:singleton和prototype,这个初始设置是不需要的。)

如何完成这个初始配置取决于特定的Servlet环境。

实际上,如果您在Spring Web MVC中访问有作用域的bean,在Spring DispatcherServlet 处理的请求中,不需要特殊的设置。DispatcherServlet 已经公开了所有相关的状态。

如果你使用 Servlet 2.5 web 的容器,请求在Spring的 DispatcherServlet 之外处理(例如,当使用JSF或Struts时),你需要注册org.springframework.web.context.request.RequestContextListener ServletRequestListener

对于Servlet 3.0+,这可以通过使用 WebApplicationInitializer 接口以编程的方式完成。

或者,对于旧的容器,将以下声明添加到您的web应用程序的 web.xml 文件中:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

另外,如果 listener 设置有问题,可以考虑使用Spring的 RequestContextFilter 。 filter 映射取决于当前的web应用程序配置,因此您必须适当地更改它。下面的列表显示了一个web应用程序的过滤器部分:

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

DispatcherServlet, RequestContextListener, 和RequestContextFilter 都在做相同的事情,即 将HTTP请求对象绑定到服务该请求的线程 。这使得 reques t和 session 作用域的bean在调用链的更下游可用。

Request scope

考虑bean definition的以下XML配置:

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

Spring容器会为每一个使用LoginAction bean definition HTTP请求创建LoginAction bean的新实例。也就是说,loginAction bean的作用域在HTTP请求级别。您可以随心所欲地更改创建的实例的内部状态,因为从相同loginAction bean definition 创建的其他实例在状态中看不到这些更改。它们是针对个别要求的。当请求完成处理时,将丢弃作用域为该请求的bean。

当使用注解驱动的组件或Java配置时,可以使用@RequestScope 注解将组件分配给 request scope。下面的例子展示了如何这样做:

@RequestScope
@Component
public class LoginAction {
    // ...
}
Session Scope

考虑以下bean定义的XML配置:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

Spring容器通过在单个HTTP Session 的生命周期中使用 UserPreferences bean definition 来创建UserPreferences bean的新实例。换句话说,userPreferences bean的作用域有效地限定在HTTP Session级别。与使用 request-scoped 的bean一样,您可以随心所欲地更改创建的实例的内部状态,因为其他HTTP Session实例也使用从相同userPreferences bean definition 创建的实例,它们不会看到这些状态变化,因为它们是特定于单个HTTP Session的。当HTTP Session最终被丢弃时,作用域为该特定HTTP Session 的bean也被丢弃。

当使用注解驱动的组件或Java配置时,可以使用@SessionScope 注解将组件分配给 session scope。下面的例子展示了如何这样做:

@SessionScope
@Component
public class UserPreferences {
    // ...
}
Application Scope

考虑以下bean定义的XML配置:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

在整个web应用程序内使用一次AppPreferences bean definition ,Spring容器创建了一个AppPreferences bean的新实例。也就是说,appPreferences bean的作用域在 ServletContext 级别,并将其存储为常规的 ServletContext 属性。这有点类似于Spring的singleton bean,但在两个重要的方面有所不同:它是每个 ServletContext 的单例,而不是每个Spring ApplicationContext (在任何给定的web应用程序中 ServletContext 可能有几个),它实际上是公开的,因此作为ServletContext属性可见。

当使用注解驱动的组件或Java配置时,可以使用@ApplicationScope 注解将组件分配给 application scope。下面的例子展示了如何这样做:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
WebSocket Scope

原文引用:

WebSocket scope is associated with the lifecycle of a WebSocket session and applies to STOMP over WebSocket applications, see WebSocket scope for more details.【与WebSocket有关的,不是太熟悉,暂时忽略】

Scoped Beans as Dependencies

Spring IoC容器不仅管理对象(bean)的实例化,而且还管理协作者(或依赖项)的连接。如果您想(例如)将一个HTTP request-scoped 的bean注入到另一个比它生命周期长的作用域的bean中,您可以选择注入一个AOP proxy 来代替该作用域的bean。也就是说,您需要注入一个代理对象,该对象公开与作用域对象相同的公共接口,但也可以从相关作用域(例如HTTP请求)检索真正的目标对象,并将方法调用委托给真正的对象。

可以在作用域都为单例的bean之间使用 <aop:scoped-proxy/> ,然后引用通过一个可序列化的中间代理,因此能够在反序列化时重新获得目标单例bean。

当原型bean声明 <aop:scope -proxy/> 时,共享代理上的每个方法调用都会导致创建一个新的目标实例,然后将调用转发到该目标实例。

而且,限定作用域的代理并不是以生命周期安全的方式从更短的作用域访问bean的唯一方法。您还可以将注入点(即构造函数或setter参数或自动装配字段)声明为 ObjectFactory<MyTargetBean> ,从而允许 getObject() 在每次需要当前实例时调用,使之能够根据需要检索当前实例而无需保留实例或单独存储它。

作为一个扩展变量,您可以声明 ObjectProvider<MyTargetBean> ,它提供了几个额外的访问变量,包括 getIfAvailablegetIfUnique

此功能的JSR-330变量称为 Provider ,并与 Provider<MyTargetBean> 声明和对应的 get() 调用一起用于每次检索尝试。请参阅这里了解更多关于JSR-330的详细信息

下面例子中的配置只有一行,但是理解它背后的“为什么”和“如何”是很重要的:

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

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> //开启了代理
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean 单例bean引用短周期bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

要创建这样的代理,您需要将子标签 <aop:scoped-proxy/> 插入到需要代理的bean definition中。为什么在 request, session 和 自定义作用域 级别定义的bean需要 <aop:scoped-proxy/> 标签?思考下面的 singleton bean definition ,并将其与您需要为前面提到的作用域定义的内容进行对比(注意,下面的userPreferences bean定义目前是不完整的):

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

原文引用:

In the preceding example, the singleton bean (userManager) is injected with a reference to the HTTP Session-scoped bean (userPreferences). The salient point here is that the userManager bean is a singleton: it is instantiated exactly once per container, and its dependencies (in this case only one, the userPreferences bean) are also injected only once. This means that the userManager bean operates only on the exact same userPreferences object (that is, the one with which it was originally injected).

This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean (for example, injecting an HTTP Session-scoped collaborating bean as a dependency into singleton bean). Rather, you need a single userManager object, and, for the lifetime of an HTTP Session, you need a userPreferences object that is specific to the HTTP Session. Thus, the container creates an object that exposes the exact same public interface as the UserPreferences class (ideally an object that is a UserPreferences instance), which can fetch the real UserPreferences object from the scoping mechanism (HTTP request, Session, and so forth). The container injects this proxy object into the userManager bean, which is unaware that this UserPreferences reference is a proxy. In this example, when a UserManager instance invokes a method on the dependency-injected UserPreferences object, it is actually invoking a method on the proxy. The proxy then fetches the real UserPreferences object from (in this case) the HTTP Session and delegates the method invocation onto the retrieved real UserPreferences object.【太长了,这里简单翻译一下】

容器在处理这种短周期bean注入到长周期bean时,会采用一个巧妙的方法:**容器将短周期bean的代理对象注入到长周期bean中,而长周期bean并不知道这个短周期bean的引用是一个代理。**在这个例子中,当 UserManager 实例调用依赖注入的 UserPreferences 对象里的一个方法时,它实际上是在调用代理上的一个方法。然后代理从HTTP 当前的 Session (在本例中)获取到真正的 UserPreferences 对象,并将方法调用委托给检索到的真实的 UserPreferences 对象然后给 UserManager 实例返回预期结果 。

【总结之后还是有点绕,总之就是,容器会将短周期bean的代理注入到长周期bean中,而长周期bean想要使用短周期bean上的方法或者属性时,则会通过短周期bean的代理转发到真正的短周期bean中进行操作。】

因此,当将requestsession 作用域的bean注入到协作对象中时,需要以下(正确且完整的)配置,如下面的示例所示:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

Choosing the Type of Proxy to Create

默认情况下,Spring容器会为标记了 <aop:scoped-proxy/> 标签的bean创建代理,这个代理是一个基于CGLIB的类代理。【注意CGLIB代理只会拦截 public 方法调用!不要在这种代理上调用非 public 方法,这种方法不会被委托给实际的作用域目标对象。】

另外,还可以配置Spring容器为这些作用域bean创建基于JDK接口的标准代理,方法是为 <aop:scoped-proxy/> 标签中的 proxy-target-class 属性指定为 false。使用基于JDK接口的代理意味着您不需要在应用程序类路径中添加其他库来影响这种代理。然而,这也意味着限定作用域bean的类必须实现至少一个接口,并且所有被注入限定作用域bean的协作者必须通过它的一个接口引用该bean。下面的示例显示了基于接口的代理:

<!-- DefaultUserPreferences 实现了 UserPreferences 接口 -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

有关选择基于类或基于接口的代理的详细信息,请参见代理机制

1.5.5. Custom Scopes

bean的作用域机制是可扩展的。您可以定义自己的作用域,甚至可以重新定义现有的作用域(除了内置的singleton和prototype作用域)。

Creating a Custom Scope

为了将您的自定义作用域集成到Spring容器中,您需要实现 org.springframework.beans.factory.config.Scope 接口,该接口将在本节中描述。要了解如何实现自定义作用域,请参阅Spring框架本身提供的作用域实现和 Scope javadoc,后者更详细地解释了需要实现的方法。

Scope 接口有四个方法用于从作用域中获取对象、从作用域中删除对象和销毁对象。

例如,session scope implementation 返回 session-scoped bean (如果它不存在,该方法将其绑定到 session 以便将来引用后返回该bean的新实例)。下面的方法从底层作用域返回对象:

Object get(String name, ObjectFactory<?> objectFactory)

例如,session scope implementation 从底层 session 中删除了 session-scoped bean 后应该返回该对象,但如果没有找到指定名称的对象,则可以返回 null 。下面的方法从底层作用域中删除对象:

Object remove(String name)

下面的方法注册一个回调,当 scope 被销毁或 scope 内的对象被销毁时调用该回调:

void registerDestructionCallback(String name, Runnable destructionCallback)

更多销毁回调的信息可以参考Javadoc或者Spring scope implementation。

原文引用:

The following method obtains the conversation identifier for the underlying scope:

可以通过下面的方法获取底层作用域的对话标识符:

String getConversationId()

每个作用域的标识符都是不同的。对于session scoped implementation,该标识符可以是 session 标识符。

Using a Custom Scope

在自定义作用域实现之后,需要通过向Spring容器中注册从而让Spring知道自定义的作用域。下面的方法是向Spring容器注册一个新的Scope的中心方法:

void registerScope(String scopeName, Scope scope);

该方法是在 ConfigurableBeanFactory 接口上声明的,该接口可以通过Spring附带的大多数具体 ApplicationContext 实现上的 BeanFactory 属性使用。

原文引用:

The first argument to the registerScope(..) method is the unique name associated with a scope. Examples of such names in the Spring container itself are singleton and prototype. The second argument to the registerScope(..) method is an actual instance of the custom Scope implementation that you wish to register and use.

参数说明,这种感觉看一眼方法体就知道的东西就不翻译了。

下一个示例使用 SimpleThreadScope ,默认情况下它在Spring中但没有注册。对于自定义Scope实现,注册指令是相同的。

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);

创建符合自定义范围的作用域规则的bean definition,如下所示:

<bean id="..." class="..." scope="thread">

使用自定义 Scope 实现,不需要局限于以编程方式注册 Scope 。还可以通过使用 CustomScopeConfigurer 类声明式注册 Scope ,如下面的例子所示:

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

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>

1.6. Customizing the Nature of a Bean

您可以使用Spring框架提供的许多接口用以自定义 bean 的性质。本节按如下方式对它们进行分组:

1.6.1. Lifecycle Callbacks

要与容器对bean生命周期的管理交互,您可以实现Spring InitializingBeanDisposableBean接口。容器对前者调用afterPropertiesSet(),对后者调用destroy(),让bean在初始化和销毁bean时执行某些操作。

JSR-250的 @PostConstruct@PreDestroy 注解通常被认为是当前Spring应用程序中接收生命周期回调的最佳实践。使用这些注解意味着您的bean没有耦合到特定于spring的接口。详情请参见使用 @PostConstruct@PreDestroy

如果您不想使用JSR-250注解,但仍然希望消除耦合,请考虑 init-methoddestroy-method bean definition 元数据。

在内部,Spring框架使用 BeanPostProcessor 的实现来处理它可以找到的任何回调接口并调用适当的方法。如果你需要定制特性或Spring默认不提供的其他生命周期行为,你可以自己实现一个 BeanPostProcessor 。 For more information, see Container Extension Points.

除了 初始化 和 销毁 回调,Spring管理的对象还可以实现 Lifecycle 接口,以便这些对象可以参与启动和关闭过程,这是由容器自己的生命周期驱动的。

本节将描述生命周期回调接口。

Initialization Callbacks

org.springframework.beans.factory.InitializingBean 接口允许bean在容器设置了bean的所有必要属性之后执行初始化工作。InitializingBean 接口指定了一个方法:

void afterPropertiesSet() throws Exception;

我们建议您不要使用 InitializingBean 接口,因为它没有必要的话就不要将代码与Spring耦合。

或者,我们建议使用 @PostConstruct 注解或指定POJO初始化方法。

对于基于xml的配置元数据,可以使用 init-method 属性指定空参的方法名。

通过Java配置,您可以使用 @BeaninitMethod 属性。

更多信息可以参考 Receiving Lifecycle Callbacks,考虑下列示例:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}

上下两个示例的效果几乎等价:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }

但是第一种方式并不会与Spring的代码耦合。

Destruction Callbacks

当一个bean实现 org.springframework.beans.factory.DisposableBean 接口后,当包含bean的容器被销毁时,bean可以获得一个回调。DisposableBean接口指定了一个方法:

void destroy() throws Exception;

我们建议您不要使用 DisposableBean 接口,因为它没有必要的话就不要将代码与Spring耦合。

或者,我们建议使用 @PreDestroy 注解或指定一个通用的方法。

对于基于xml的配置元数据,可以使用 destroy-method 属性指定空参的方法名。

通过Java配置,您可以使用 @BeandestroyMethod 属性。

更多信息可以参考 Receiving Lifecycle Callbacks,考虑下列示例:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

上下两个示例的效果几乎等价:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

但是第一种方式并不会与Spring的代码耦合。

您可以为 <bean> 标签的 destroy-method 属性分配一个特殊的(推断的)值,它指示Spring自动检测特定bean类上的公共关闭或关闭方法。(因此,任何实现 java.lang.AutoCloseablejava.io.Closeable 的类都会匹配。)您还可以在 <beans> 标签的 Default - Destroy -method 属性上设置这个特殊的(推断的)值,以便将此行为应用到整个bean集(请参阅 Default Initialization and Destroy Methods)。注意,这是Java配置的默认行为

Default Initialization and Destroy Methods

当您编写不使用spring特定的 InitializingBeanDisposableBean 回调接口的初始化和销毁方法回调时,您通常会编写具有 init()initialize()dispose() 等名称的方法。理想情况下,这种生命周期回调方法的名称在整个项目中是标准化的,以便所有开发人员使用相同的方法名称并确保一致性。

您可以配置Spring容器来查找每个bean上的命名初始化并销毁回调方法名称。这意味着,作为应用程序开发人员,您可以编写应用程序类并使用名为init()的初始化回调,而不必为每个bean定义配置init-method="init"属性。Spring IoC容器在创建bean时调用该方法(并且按照前面描述的标准生命周期回调约定)。这个特性还强制对初始化和销毁方法回调使用一致的命名约定。

假设初始化回调方法命名为init(),销毁回调方法命名为destroy()。你的类类似于下面的例子

public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

然后可以在类似如下的bean中使用该类:

<beans default-init-method="init">

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>

顶级 <beans/> 标签上的 default-init-method 属性会使得Spring IoC容器将 bean 类上名为 init 的方法识别为初始化方法回调。在创建和组装bean时,如果bean类有这样的方法,就会在适当的时候调用它。

同样也可以在顶级 <beans/> 标签上使用 default-destroy-method 属性配置销毁方法回调(即在XML中)。

当bean类中有不同于顶级<beans/> 标签的回调方法,可以通过 <bean/> 本身的 init-methoddestroy-method 属性来指定(即在XML中)方法名称来重写缺省值。

原文引用:

The Spring container guarantees that a configured initialization callback is called immediately after a bean is supplied with all dependencies. Thus, the initialization callback is called on the raw bean reference, which means that AOP interceptors and so forth are not yet applied to the bean. A target bean is fully created first and then an AOP proxy (for example) with its interceptor chain is applied. If the target bean and the proxy are defined separately, your code can even interact with the raw target bean, bypassing the proxy. Hence, it would be inconsistent to apply the interceptors to the init method, because doing so would couple the lifecycle of the target bean to its proxy or interceptors and leave strange semantics when your code interacts directly with the raw target bean.【看不太懂,但是感觉有点重要,下次回来看】

Spring容器保证在bean与所有依赖项一起提供之后立即调用已配置的初始化回调。因此,初始化回调是在原始bean引用上调用的,这意味着AOP拦截器等还没有应用到bean上。首先完全创建一个目标bean,然后应用一个AOP代理(例如)及其拦截器链。如果目标bean和代理是分开定义的,那么您的代码甚至可以绕过代理,与原始目标bean进行交互。因此,将拦截器应用于init方法是不一致的,因为这样做会将目标bean的生命周期与其代理或拦截器耦合起来,并在您的代码直接与原始目标bean交互时留下奇怪的语义。

Combining Lifecycle Mechanisms

从Spring 2.5开始,共有三个控制bean生命周期行为的选项:

您可以组合这些机制来控制给定的bean。

如果为一个bean配置了多个生命周期机制,并且每个机制都配置了不同的方法名,那么每个配置的方法都将按照本说明后面列出的顺序运行。但是,如果为这些生命周期机制中的多个配置了相同的方法名(例如,初始化方法的init()),那么该方法将运行一次,如上一节所述。

使用不同的初始化方法为同一个bean配置的多个生命周期机制如下所示:

  1. @PostConstruct 注解的方法
  2. 定义在 InitializingBean 回调接口中的 afterPropertiesSet()
  3. 自定义配置的 init() 方法

Destroy方法的调用顺序相同:

  1. @PreDestroy 注解的方法
  2. 定义在 DisposableBean 回调接口中的 destroy()
  3. 自定义配置的 destroy() 方法
Startup and Shutdown Callbacks

Lifecycle 接口定义了任何具有自己生命周期需求的对象的基本方法(比如启动和停止某个后台进程):

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}

任何spring管理的对象都可以实现 Lifecycle 接口。然后,当 ApplicationContext 本身接收到启动和停止信号时(例如,运行时的停止/重新启动场景),它将这些调用级联到该上下文中定义的所有 Lifecycle 实现。它通过委托给 LifecycleProcessor 来实现这一点,如下面的清单所示:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

注意 LifecycleProcessor 本身就是 Lifecycle 接口的扩展。它还添加了另外两个方法来响应正在刷新和关闭的上下文

注意,常规的 org.springframework.context.Lifecycle 接口是一个显式启动和停止通知的普通约定,并不意味着在上下文刷新时自动启动。对于特定bean的自动启动的细粒度控制(包括启动阶段),可以考虑实现 org.springframework.context.SmartLifecycle

此外,请注意停止通知不保证在销毁之前。在常规关闭时,所有生命周期bean在传播常规销毁回调之前首先收到一个停止通知。但是在上下文生命周期内的热刷新或停止刷新尝试时,只调用destroy方法。

启动和关闭调用的顺序可能很重要。如果任何两个对象之间存在“依赖”关系,依赖方在其依赖方之后启动,在其依赖方之前停止。然而,有时候直接依赖关系是未知的。您可能只知道某种类型的对象应该在另一种类型的对象之前开始。在这些情况下,SmartLifecycle 接口定义了另一个选项,即在其 super 接口 Phased 上定义的 getPhase() 方法。下面的清单显示了 Phased 界面的定义:

public interface Phased {

    int getPhase();
}

下面的清单显示了 SmartLifecycle 接口的定义:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}

当启动时,最低阶段的对象首先启动;当停止时,按照相反的顺序进行。因此,实现 SmartLifecycle 并且 getPhase() 方法返回 Integer.MIN_VALUE 是最先开始的,也是最后停止的。相反的, Integer.MAX_VALUE 表示应该最后启动对象,首先停止对象(可能是因为它依赖于正在运行的其他进程)。当考虑阶段值时,知道任何正常的生命周期对象没有实现 SmartLifecycle 的默认阶段是0也是很重要的。因此,任何**负相位值都表示一个对象应该在这些标准组件之前启动(并在它们之后停止)。**对于任何正相位值,反之成立。

SmartLifecycle 定义的 stop 方法接受一个回调。在实现的关闭过程完成后,任何实现都必须调用回调函数的 run() 方法。这样就可以在必要的地方实现异步关机,因为 LifecycleProcessor 接口的默认实现 DefaultLifecycleProcessor 会等待每个阶段中调用该回调的对象组的超时值。每个阶段的默认超时是30秒。您可以通过在上下文中定义一个名为 lifecycleProcessor 的bean来重写默认的生命周期处理器实例。如果您只想修改超时,定义以下内容就足够了:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

如前所述,LifecycleProcessor接口还定义了用于刷新和关闭上下文的回调方法。后者驱动关闭过程,就像显式调用了stop()一样,但它发生在上下文关闭时。另一个,'refresh’回调可以启用SmartLifecycle bean的另一个特性。当上下文被刷新时(在所有对象被实例化和初始化之后),该回调将被调用。这时,默认的生命周期处理器会检查每个 SmartLifecycle 对象的 isAutoStartup() 方法返回的布尔值。如果为true,则在该点启动该对象而不是等待上下文或它自己的 start() 方法的显式调用(与上下文刷新不同,对于标准上下文实现,上下文启动不会自动发生)。如前所述,阶段值和任何依赖关系决定启动顺序。

这些部分平时没有怎么注意过,导致现在看起文档来一脸懵,机翻过来的结果都比我看的好一些,暂时还没法接收这部分的知识,希望后续有时间能在消化掉重读这部分,相信会有更大的收获。

Shutting Down the Spring IoC Container Gracefully in Non-Web Applications

本节只适用于非web应用。Spring基于web的 ApplicationContext 实现已经有了合适的代码,可以在相关web应用程序关闭时优雅地关闭Spring IoC容器。

如果您在非web应用程序环境中使用Spring的IoC容器(例如,在客户端桌面环境中),请向JVM注册一个关闭 hook 。这样做可以确保安全关闭,并调用单例bean上的相关销毁方法,以便释放所有资源。您仍然必须正确地配置和实现这些销毁回调。

要注册一个关机 hook ,可以调用在 ConfigurableApplicationContext 接口上声明的 registerShutdownHook() 方法,如下所示:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
    }
}
1.6.2. ApplicationContextAware and BeanNameAware

ApplicationContext 创建一个实现了 org.springframework.context.ApplicationContextAware 接口的对象实例时,该实例将被提供一个对该 ApplicationContext 的引用。下面的清单显示了 ApplicationContextAware 接口的定义:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此, bean 可以通过 ApplicationContext 接口或通过将引用转换为该接口的已知子类(例如ConfigurableApplicationContext ,它公开了额外的功能),以编程方式操作创建它们的 ApplicationContext 。一种用途是对其他bean进行程序化检索,有时这种能力是有用的,但是通常情况下应该避免使用它,因为它将代码与Spring耦合在一起,并且不遵循 控制反转 风格,在这种风格中,协作者被作为属性提供给bean。ApplicationContext的其他方法提供对文件资源的访问发布应用程序事件访问MessageSource。这些附加特性在 Additional Capabilities of the ApplicationContext中进行了描述。

自动装配是另一种获取ApplicationContext引用的方法。传统的constructorbyType自动装配模式(如 Autowiring Collaborators中所述)可以分别为 构造函数参数 或 setter方法参数 提供ApplicationContext类型的依赖项。为了获得更大的灵活性,包括了 自动装配字段 和 多个参数方法 的能力,可以使用基于注解的自动装配特性。如果这样做,ApplicationContext将被自动装配到字段、构造函数参数或方法参数中,如果有问题的字段、构造函数或方法携带@Autowired注解,则该字段、构造函数或方法将期望ApplicationContext类型。有关更多信息,请参见Using @Autowired

ApplicationContext创建一个实现了org.springframework.beans.factory.BeanNameAware接口的类时,会为该类提供一个对其关联对象定义中定义的名称的引用。下面的清单显示了BeanNameAware接口的定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

回调将会在填充普通bean属性之后,初始化回调(如InitializingBean.afterPropertiesSet()或自定义初始化方法)之前完成。

1.6.3. Other Aware Interfaces

除了ApplicationContextAwareBeanNameAware之外,Spring还提供了大量的Aware回调接口,这些接口让bean向容器表明它们需要特定的基础设施依赖项。通常,名称表示依赖类型。下表总结了最重要的Aware接口:

NameInjected DependencyExplained in…
ApplicationContextAware声明 ApplicationContextApplicationContextAware and BeanNameAware
ApplicationEventPublisherAwareEvent publisher of the enclosing ApplicationContext.Additional Capabilities of the ApplicationContext
BeanClassLoaderAwareClass loader used to load the bean classes.Instantiating Beans
BeanFactoryAwareDeclaring BeanFactory.The BeanFactory API
BeanNameAwareName of the declaring bean.ApplicationContextAware and BeanNameAware
LoadTimeWeaverAwareDefined weaver for processing class definition at load time.Load-time Weaving with AspectJ in the Spring Framework
MessageSourceAwareConfigured strategy for resolving messages (with support for parametrization and internationalization).Additional Capabilities of the ApplicationContext
NotificationPublisherAwareSpring JMX notification publisher.Notifications
ResourceLoaderAwareConfigured loader for low-level access to resources.Resources
ServletConfigAwareCurrent ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext.Spring MVC
ServletContextAwareCurrent ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext.Spring MVC

注意,使用这些接口将会使得代码与Spring APId耦合,并且不遵循IoC原则。因此,在需要对容器进行编程访问的基础结构bean时才推荐使用它们。【上表的类名基本做到了见名知义,但有些部分没怎么学过导致无法完全看懂,后面看完其他篇章再回来看看】

1.7. Bean Definition Inheritance

bean definition 可以包含很多配置信息,包括构造函数参数、属性值和容器特定的信息,比如初始化方法、静态工厂方法名等。子bean definition 从父bean definition 继承配置数据。子 definition 可以根据需要重写一些值或添加其他值。使用父bean和子bean definition 可以节省大量的输入。实际上,这是模板的一种形式。

如果您以编程的方式使用 ApplicationContext 接口,子bean definition 将由 ChildBeanDefinition 类表示。大多数用户不会使用编程方式去创建bean。相反,它们在类(比如 ClassPathXmlApplicationContext )中声明性地配置bean definition。当您使用基于xml的配置元数据时,您可以通过使用父属性来指示子bean definition,并指定父bean作为该属性的值。下面的示例演示如何这样做:

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">  //注意这个 parent 属性
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>

如果没有指定,则子bean definition 使用父 definition 中的bean类,但也可以覆盖它。在后一种情况下,子bean类必须与父bean类兼容(也就是说,它必须接受父bean的属性值)。

子bean definition 从父bean继承作用域、构造函数参数值、属性值和方法重写,并可以添加新值。您指定的任何范围、初始化方法、销毁方法或静态工厂方法设置都会覆盖相应的父设置。

其余的设置总是从子 definition 中获取:depends on, autowire mode, dependency check, singleton, and lazy init。

前面的示例通过使用 abstract 属性显式地将父bean definition 标记为抽象。如果父定义没有指定类,则需要显式地将父bean definition 标记为抽象,如下面的示例所示:

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

父bean不能单独实例化,因为它是不完整的,而且它还显式地被标记为 abstract的。当一个定义是 abstract的,它只能作为一个纯粹的模板的bean definition 作为父 defintion 去定义子 definition。试图单独使用这样一个抽象的父bean,通过将其作为另一个bean的ref属性引用,或者使用父bean ID执行显式getBean()调用,将返回错误。类似地,容器内部的preInstantiateSingletons()方法忽略被定义为抽象的bean definition。

注意:ApplicationContext 默认预实例化所有的单例。因此,重要的是如果有一个(父)bean定义,您打算只将其用作模板,并且这个定义指定了一个类,那么您必须确保将抽象属性设置为true,否则应用程序上下文将实际(尝试)预实例化抽象bean。

1.8. Container Extension Points

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
2.1. 简介 2.2. 控制反转(IoC)容器 2.2.1. 新的bean作用域 2.2.2. 更简单的XML配置 2.2.3. 可扩展的XML编写 2.2.4. Annotation(注解)驱动配置 2.2.5. 在classpath中自动搜索组件 2.3. 面向切面编程(AOP) 2.3.1. 更加简单的AOP XML配置 2.3.2. 对@AspectJ 切面的支持 2.3.3. 对bean命名pointcut( bean name pointcut element)的支持 2.3.4. 对AspectJ装载时织入(AspectJ load-time weaving)的支持 2.4. 中间层 2.4.1. 在XML里更为简单的声明性事务配置 2.4.2. 对Websphere 事务管理的完整支持 2.4.3. JPA 2.4.4. 异步的JMS 2.4.5. JDBC 2.5. Web层 2.5.1. Spring MVC合理的默认值 2.5.2. Portlet 框架 2.5.3. 基于Annotation的控制器 2.5.4. Spring MVC的表单标签库 2.5.5. 对Tiles 2 支持 2.5.6. 对JSF 1.2支持 2.5.7. JAX-WS支持 2.6. 其他 2.6.1. 动态语言支持 2.6.2. 增强的测试支持 2.6.3. JMX 支持 2.6.4. 将Spring 应用程序上下文部署为JCA adapter 2.6.5. 计划任务 2.6.6. 对Java 5 (Tiger) 支持 2.7. 移植到Spring 2.5 2.7.1. 改变 2.8. 更新的样例应用 2.9. 改进的文档 I. 核心技术 3. IoC(控制反转)容器 3.1. 简介 3.2. 基本原理 - 容器和bean 3.2.1. 容器 3.2.2. 实例化容器 3.2.3. 多种bean 3.2.4. 使用容器 3.3. 依赖 3.3.1. 注入依赖 3.3.2. 依赖配置详解 3.3.3. 使用depends-on 3.3.4. 延迟初始化bean 3.3.5. 自动装配(autowire)协作者 3.3.6. 依赖检查 3.3.7. 方法注入 3.4. Bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. Singleton beans和prototype-bean的依赖 3.4.4. 其他作用域 3.4.5. 自定义作用域 3.5. 定制bean特性 3.5.1. 生命周期回调 3.5.2. 了解自己 3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. The ApplicationContext 3.8.1. BeanFactory 还是 ApplicationContext? 3.8.2. 利用MessageSource实现国际化 3.8.3. 事件 3.8.4. 底层资源的访问 3.8.5. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.10. 以J2EE RAR文件的形式部署Spring ApplicationContext 3.11. 基于注解(Annotation-based)的配置 3.11.1. @Autowired 3.11.2. 基于注解的自动连接微调 3.11.3. CustomAutowireConfigurer 3.11.4. @Resource 3.11.5. @PostConstruct 与 @PreDestroy 3.12. 对受管组件的Classpath扫描 3.12.1. @Component和更多典型化注解 3.12.2. 自动检测组件 3.12.3. 使用过滤器自定义扫描 3.12.4. 自动检测组件的命名 3.12.5. 为自动检测的组件提供一个作用域 3.12.6. 用注解提供限定符元数据 3.13. 注册一个LoadTimeWeaver 4. 资源 4.1. 简介 4.2. Resource接口 4.3. 内置 Resource 实现 4.3.1. UrlResource 4.3.2. ClassPathResource 4.3.3. FileSystemResource 4.3.4. ServletContextResource 4.3.5. InputStreamResource 4.3.6. ByteArrayResource 4.4. ResourceLoader接口 4.5. ResourceLoaderAware 接口 4.6. 把Resource作为属性来配置 4.7. Application context 和Resource 路径 4.7.1. 构造application context 4.7.2. Application context构造器中资源路径的通配符 4.7.3. FileSystemResource 说明 5. 校验,数据绑定,BeanWrapper,与属性编辑器 5.1. 简介 5.2. 使用Spring的Validator接口进行校验 5.3. 从错误代码到错误信息 5.4. Bean处理和BeanWrapper 5.4.1. 设置和获取属性值以及嵌套属性 5.4.2. 内建的PropertyEditor实现 6. 使用Spring进行面向切面编程(AOP) 6.1. 简介 6.1.1. AOP概念 6.1.2. Spring AOP的功能和目标 6.1.3. AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.4. 声明通知 6.2.5. 引入(Introduction) 6.2.6. 切面实例化模型 6.2.7. 例子 6.3. 基于Schema的AOP支持 6.3.1. 声明一个切面 6.3.2. 声明一个切入点 6.3.3. 声明通知 6.3.4. 引入 6.3.5. 切面实例化模型 6.3.6. Advisor 6.3.7. 例子 6.4. AOP声明风格的选择 6.4.1. Spring AOP还是完全用AspectJ? 6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.6.1. 理解AOP代理 6.7. 以编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 7.2.3. AspectJ切入点表达式 7.2.4. 便利的切入点实现 7.2.5. 切入点的超类 7.2.6. 自定义切入点 7.3. Spring的通知API 7.3.1. 通知的生命周期 7.3.2. Spring里的通知类型 7.4. Spring里的Advisor API 7.5. 使用ProxyFactoryBean创建AOP代理 7.5.1. 基础 7.5.2. JavaBean属性 7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”通知器 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动代理(autoproxy)”功能 7.9.1. 自动代理bean定义 7.9.2. 使用元数据驱动的自动代理 7.10. 使用TargetSource 7.10.1. 热交换目标源 7.10.2. 池化目标源 7.10.3. 原型目标源 7.10.4. ThreadLocal目标源 7.11. 定义新的Advice类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.2.1. Mock对象 8.2.2. 单元测试支持类 8.3. 集成测试 8.3.1. 概览 8.3.2. 使用哪个支持框架 8.3.3. 通用目标 8.3.4. JDBC测试支持 8.3.5. 常用注解 8.3.6. JUnit 3.8遗留支持 8.3.7. Spring TestContext Framework 8.3.8. PetClinic示例 8.4. 更多资源 II. 中间层数据访问 9. 事务管理 9.1. 简介 9.2. 动机 9.3. 关键抽象 9.4. 使用资源同步的事务 9.4.1. 高层次方案 9.4.2. 低层次方案 9.4.3. TransactionAwareDataSourceProxy 9.5. 声明式事务管理 9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知事务操作 9.5.9. 结合AspectJ使用 @Transactional 9.6. 编程式事务管理 9.6.1. 使用TransactionTemplate 9.6.2. 使用PlatformTransactionManager 9.7. 选择编程式事务管理还是声明式事务管理 9.8. 与特定应用服务器集成 9.8.1. IBM WebSphere 9.8.2. BEA WebLogic 9.8.3. Oracle OC4J 9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一种工作模式 11.1.2. Spring JDBC包结构 11.2. 利用JDBC核心类控制JDBC的基本操作和错误处理 11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口 11.2.6. 执行SQL语句 11.2.7. 执行查询 11.2.8. 更新数据库 11.2.9. 获取自动生成的主键 11.3. 控制数据库连接 11.3.1. DataSourceUtils类 11.3.2. SmartDataSource接口 11.3.3. AbstractDataSource类 11.3.4. SingleConnectionDataSource类 11.3.5. DriverManagerDataSource类 11.3.6. TransactionAwareDataSourceProxy类 11.3.7. DataSourceTransactionManager类 11.3.8. NativeJdbcExtractor 11.4. JDBC批量操作 11.4.1. 使用JdbcTemplate进行批量操作 11.4.2. 使用SimpleJdbcTemplate进行批量操作 11.5. 通过使用SimpleJdbc类简化JDBC操作 11.5.1. 使用SimpleJdbcInsert插入数据 11.5.2. 使用SimpleJdbcInsert来获取自动生成的主键 11.5.3. 指定SimpleJdbcInsert所使用的字段 11.5.4. 使用SqlParameterSource提供参数值 11.5.5. 使用SimpleJdbcCall调用存储过程 11.5.6. 声明SimpleJdbcCall使用的参数 11.5.7. 如何定义SqlParameters 11.5.8. 使用SimpleJdbcCall调用内置函数 11.5.9. 使用SimpleJdbcCall返回的ResultSet/REF Cursor 11.6. 用Java对象来表达JDBC操作 11.6.1. SqlQuery类 11.6.2. MappingSqlQuery类 11.6.3. SqlUpdate类 11.6.4. StoredProcedure类 11.6.5. SqlFunction类 11.7. 参数和数据处理的基本原则 11.7.1. 为参数设置SQL类型信息 11.7.2. 处理BLOB 和 CLOB对象 11.7.3. 在IN语句中传入一组参数值 11.7.4. 处理复杂类型的存储过程调用 12. 使用ORM工具进行数据访问 12.1. 简介 12.2. Hibernate 12.2.1. 资源管理 12.2.2. 在Spring容器中创建 SessionFactory 12.2.3. The HibernateTemplate 12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的事务划分 12.2.8. 事务管理策略 12.2.9. 容器资源 vs 本地资源 12.2.10. 在应用服务器中使用Hibernate的注意事项 12.3. JDO 12.3.1. 建立PersistenceManagerFactory 12.3.2. JdoTemplate和JdoDaoSupport 12.3.3. 基于原生的JDO API实现DAO 12.3.4. 事务管理 12.3.5. JdoDialect 12.4. Oracle TopLink 12.4.1. SessionFactory 抽象层 12.4.2. TopLinkTemplate and TopLinkDaoSupport 12.4.3. 基于原生的TopLink API的DAO实现 12.4.4. 事务管理 12.5. iBATIS SQL Maps 12.5.1. 创建SqlMapClient 12.5.2. 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 12.5.3. 基于原生的iBATIS API的DAO实现 12.6. JPA 12.6.1. 在Spring环境中建立JPA 12.6.2. JpaTemplate 和 JpaDaoSupport 12.6.3. 基于原生的JPA实现DAO 12.6.4. 异常转化 12.7. 事务管理 12.8. JpaDialect III. The Web 13. Web MVC framework Web框架 13.1. 概述 13.1.1. 与其他MVC实现框架的集成 13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 WebContentGenerator 13.3.2. 其它的简单控制器 13.3.3. MultiActionController 13.3.4. 命令控制器 13.4. 处理器映射(handler mapping) 13.4.1. BeanNameUrlHandlerMapping 13.4.2. SimpleUrlHandlerMapping 13.4.3. 拦截器(HandlerInterceptor) 13.5. 视图与视图解析 13.5.1. 视图解析器(ViewResolver) 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.6. 本地化解析器 13.6.1. AcceptHeaderLocaleResolver 13.6.2. CookieLocaleResolver 13.6.3. SessionLocaleResolver 13.6.4. LocaleChangeInterceptor 13.7. 使用主题 13.7.1. 简介 13.7.2. 如何定义主题 13.7.3. 主题解析器 13.8. Spring对分段文件上传(multipart file upload)的支持 13.8.1. 介绍 13.8.2. 使用MultipartResolver 13.8.3. 在表单中处理分段文件上传 13.9. 使用Spring的表单标签库 13.9.1. 配置 13.9.2. form标签 13.9.3. input标签 13.9.4. checkbox标签 13.9.5. checkboxes标签 13.9.6. radiobutton标签 13.9.7. radiobuttons标签 13.9.8. password标签 13.9.9. select标签 13.9.10. option标签 13.9.11. options标签 13.9.12. textarea标签 13.9.13. hidden标签 13.9.14. errors标签 13.10. 处理异常 13.11. 惯例优先原则(convention over configuration) 13.11.1. 对控制器的支持:ControllerClassNameHandlerMapping 13.11.2. 对模型的支持:ModelMap(ModelAndView) 13.11.3. 对视图的支持:RequestToViewNameTranslator 13.12. 基于注解的控制器配置 13.12.1. 建立dispatcher实现注解支持 13.12.2. 使用@Controller定义一个控制器 13.12.3. 使用@RequestMapping映射请求 13.12.4. 使用@RequestParam绑定请求参数到方法参数 13.12.5. 使用@ModelAttribute提供一个从模型到数据的链接 13.12.6. 使用@SessionAttributes指定存储在会话中的属性 13.12.7. 自定义WebDataBinder初始化 13.13. 更多资源 14. 集成视图技术 14.1. 简介 14.2. JSP和JSTL 14.2.1. 视图解析器 14.2.2. 'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL 14.2.3. 帮助简化开发的额外的标签 14.3. Tiles 14.3.1. 需要的资源 14.3.2. 如何集成Tiles 14.4. Velocity和FreeMarker 14.4.1. 需要的资源 14.4.2. Context 配置 14.4.3. 创建模板 14.4.4. 高级配置 14.4.5. 绑定支持和表单处理 14.5. XSLT 14.5.1. 写在段首 14.5.2. 小结 14.6. 文档视图(PDF/Excel) 14.6.1. 简介 14.6.2. 配置和安装 14.7. JasperReports 14.7.1. 依赖的资源 14.7.2. 配置 14.7.3. 构造ModelAndView 14.7.4. 使用子报表 14.7.5. 配置Exporter的参数 15. 集成其它Web框架 15.1. 简介 15.2. 通用配置 15.3. JavaServer Faces 15.3.1. DelegatingVariableResolver 15.3.2. FacesContextUtils 15.4. Struts 15.4.1. ContextLoaderPlugin 15.4.2. ActionSupport Classes 15.5. Tapestry 15.5.1. 注入 Spring 托管的 beans 15.6. WebWork 15.7. 更多资源 16. Portlet MVC框架 16.1. 介绍 16.1.1. 控制器 - MVC中的C 16.1.2. 视图 - MVC中的V 16.1.3. Web作用范围的Bean 16.2. DispatcherPortlet 16.3. ViewRendererServlet 16.4. 控制器 16.4.1. AbstractController 和 PortletContentGenerator 16.4.2. 其它简单的控制器 16.4.3. Command控制器 16.4.4. PortletWrappingController 16.5. 处理器映射 16.5.1. PortletModeHandlerMapping 16.5.2. ParameterHandlerMapping 16.5.3. PortletModeParameterHandlerMapping 16.5.4. 增加 HandlerInterceptors 16.5.5. HandlerInterceptorAdapter 16.5.6. ParameterMappingInterceptor 16.6. 视图和它们的解析 16.7. Multipart文件上传支持 16.7.1. 使用 PortletMultipartResolver 16.7.2. 处理表单里的文件上传 16.8. 异常处理 16.9. Portlet应用的部署 IV. 整合 17. 使用Spring进行远程访问与Web服务 17.1. 简介 17.2. 使用RMI暴露服务 17.2.1. 使用RmiServiceExporter暴露服务 17.2.2. 在客户端链接服务 17.3. 使用Hessian或者Burlap通过HTTP远程调用服务 17.3.1. 为Hessian和co.配置DispatcherServlet 17.3.2. 使用HessianServiceExporter暴露你的bean 17.3.3. 在客户端连接服务 17.3.4. 使用Burlap 17.3.5. 对通过Hessian或Burlap暴露的服务使用HTTP Basic认证 17.4. 使用HTTP调用器暴露服务 17.4.1. Exposing the service object 17.4.2. 在客户端连接服务 17.5. Web Services 17.5.1. 使用JAX-RPC暴露基于servlet的web服务 17.5.2. 使用JAX-RPC访问web服务 17.5.3. 注册JAX-RPC Bean映射 17.5.4. 注册自己的JAX-RPC 处理器 17.5.5. 使用JAX-WS暴露基于servlet的web服务 17.5.6. 使用JAX-WS暴露单独web服务 17.5.7. 使用Spring支持的JAX-WS RI来暴露服务 17.5.8. 使用JAX-WS访问web服务 17.5.9. 使用XFire来暴露Web服务 17.6. JMS 17.6.1. 服务端配置 17.6.2. 客户端配置 17.7. 对远程接口不提供自动探测实现 17.8. 在选择这些技术时的一些考虑 18. Enterprise Java Beans (EJB) 集成 18.1. 简介 18.2. 访问EJB 18.2.1. 概念 18.2.2. 访问本地的无状态Session Bean(SLSB) 18.2.3. 访问远程SLSB 18.2.4. Accessing EJB 2.x SLSBs versus EJB 3 SLSBs 18.3. 使用Spring提供的辅助类实现EJB组件 18.3.1. EJB 2.x base classes 18.3.2. EJB 3 注入拦截 19. JMS (Java Message Service) 19.1. 简介 19.2. 使用Spring JMS 19.2.1. JmsTemplate 19.2.2. 连接工厂 19.2.3. 目的地管理 19.2.4. 消息侦听容器 19.2.5. 事务管理 19.3. 发送消息 19.3.1. 使用消息转换器 19.3.2. SessionCallback 和 ProducerCallback 19.4. 接收消息 19.4.1. 同步接收 19.4.2. 异步接收 - 消息驱动的POJO 19.4.3. SessionAwareMessageListener接口 19.4.4. MessageListenerAdapter 19.4.5. 事务中的消息处理 19.5. JCA消息端点的支持 19.6. JMS命名空间支持 20. JMX 20.1. 介绍 20.2. 将Bean暴露为JMX 20.2.1. 创建MBeanServer 20.2.2. 重用原有的MBeanServer 20.2.3. 延迟初始化的MBean 20.2.4. MBean的自动注册 20.2.5. 控制注册行为 20.3. 控制Bean的管理接口 20.3.1. MBeanInfoAssembler接口 20.3.2. 使用源码级元数据 20.3.3. 使用JDK 5.0的注解 20.3.4. 源代码级的元数据类型 20.3.5. AutodetectCapableMBeanInfoAssembler接口 20.3.6. 用Java接口定义管理接口 20.3.7. 使用MethodNameBasedMBeanInfoAssembler 20.4. 控制Bean的ObjectName 20.4.1. 从Properties读取Properties 20.4.2. 使用MetadataNamingStrategy 20.4.3. 元素 20.5. JSR-160连接器 20.5.1. 服务器端连接器 20.5.2. 客户端连接器 20.5.3. 基于Burlap/Hessian/SOAP的JMX 20.6. 通过代理访问MBean 20.7. 通知 20.7.1. 为通知注册监听器 20.7.2. 发布通知 20.8. 更多资源 21. JCA CCI 21.1. 简介 21.2. 配置CCI 21.2.1. 连接器配置 21.2.2. 在Spring中配置ConnectionFactory 21.2.3. 配置CCI连接 21.2.4. 使用一个 CCI 单连接 21.3. 使用Spring的 CCI访问支持 21.3.1. 记录转换 21.3.2. CciTemplate类 21.3.3. DAO支持 21.3.4. 自动输出记录生成 21.3.5. 总结 21.3.6. 直接使用一个CCI Connection接口和Interaction接口 21.3.7. CciTemplate 使用示例 21.4. 建模CCI访问为操作对象 21.4.1. MappingRecordOperation 21.4.2. MappingCommAreaOperation 21.4.3. 自动生成输出记录 21.4.4. 总结 21.4.5. MappingRecordOperation 使用示例 21.4.6. MappingCommAreaOperation 使用示例 21.5. 事务 22. Spring邮件抽象层 22.1. 简介 22.2. 使用Spring邮件抽象 22.2.1. MailSender 和 SimpleMailMessage 的基本用法 22.2.2. 使用 JavaMailSender 和 MimeMessagePreparator 22.3. 使用MimeMessageHelper 22.3.1. 发送附件和嵌入式资源(inline resources) 22.3.2. 使用模板来创建邮件内容 23. Spring中的定时调度(Scheduling)和线程池(Thread Pooling) 23.1. 简介 23.2. 使用OpenSymphony Quartz 调度器 23.2.1. 使用JobDetailBean 23.2.2. 使用 MethodInvokingJobDetailFactoryBean 23.2.3. 使用triggers和SchedulerFactoryBean来包装任务 23.3. 使用JDK Timer支持类 23.3.1. 创建定制的timers 23.3.2. 使用 MethodInvokingTimerTaskFactoryBean类 23.3.3. 最后:使用TimerFactoryBean来设置任务 23.4. SpringTaskExecutor抽象 23.4.1. TaskExecutor接口 23.4.2. TaskExecutor类型 23.4.3. 使用TaskExecutor 24. 动态语言支持 24.1. 介绍 24.2. 第一个示例 24.3. 定义动态语言支持的bean 24.3.1. 公共概念 24.3.2. JRuby beans 24.3.3. Groovy beans 24.3.4. BeanShell beans 24.4. 场景 24.4.1. Spring MVC控制器的脚本化 24.4.2. Validator的脚本化 24.5. Bits and bobs 24.5.1. AOP - 通知脚本化bean 24.5.2. 作用域 24.6. 更多的资源 25. 注解和源代码级的元数据支持 25.1. 简介 25.2. Spring的元数据支持 25.3. 注解 25.3.1. @Required 25.3.2. Spring中的其它@Annotations 25.4. Jakarta Commons Attributes集成 25.5. 元数据和Spring AOP自动代理 25.5.1. 基本原理 25.5.2. 声明式事务管理 V. 示例程序 26. 演示案例 26.1. 介绍 26.2. 使用动态语言实现的Spring MVC控制器 26.2.1. 构建与部署 26.3. 使用SimpleJdbcTemplate和@Repository实现DAO 26.3.1. 域对象 26.3.2. Data Access Object 26.3.3. 构建 A. XML Schema-based configuration A.1. Introduction A.2. XML Schema-based configuration A.2.1. Referencing the schemas A.2.2. The util schema A.2.3. The jee schema A.2.4. The lang schema A.2.5. The jms schema A.2.6. The tx (transaction) schema A.2.7. The aop schema A.2.8. The context schema A.2.9. The tool schema A.2.10. The beans schema A.3. Setting up your IDE A.3.1. Setting up Eclipse A.3.2. Setting up IntelliJ IDEA A.3.3. Integration issues B. Extensible XML authoring B.1. Introduction B.2. Authoring the schema B.3. Coding a NamespaceHandler B.4. Coding a BeanDefinitionParser B.5. Registering the handler and the schema B.5.1. 'META-INF/spring.handlers' B.5.2. 'META-INF/spring.schemas' B.6. Using a custom extension in your Spring XML configuration B.7. Meatier examples B.7.1. Nesting custom tags within custom tags B.7.2. Custom attributes on 'normal' elements B.8. Further Resources C. spring-beans-2.0.dtd D. spring.tld D.1. Introduction D.2. The bind tag D.3. The escapeBody tag D.4. The hasBindErrors tag D.5. The htmlEscape tag D.6. The message tag D.7. The nestedPath tag D.8. The theme tag D.9. The transform tag E. spring-form.tld E.1. Introduction E.2. The checkbox tag E.3. The checkboxes tag E.4. The errors tag E.5. The form tag E.6. The hidden tag E.7. The input tag E.8. The label tag E.9. The option tag E.10. The options tag E.11. The password tag E.12. The radiobutton tag E.13. The radiobuttons tag E.14. The select tag E.15. The textarea tag F. Spring 2.5开发手册中文化项目 F.1. 声明 F.2. 致谢 F.3. 参与人员 F.4. 项目历程 表格清单 3.1. bean定义 3.2. Autowiring modes 3.3. 依赖检查方式 3.4. Bean作用域 3.5. Feature Matrix特性表 3.6. 内置事件 3.7. 过滤器类型 4.1. Resource strings 5.1. 属性示例 5.2. 内建的PropertyEditors 6.1. DefaultContextLoadTimeWeaver LoadTimeWeaversDefaultContextLoadTimeWeaver类和LoadTimeWeavers接口 6.2. aspectj-weaving属性值 9.1. 有关的设置 9.2. 设置 9.3. @Transactional 注解的属性 13.1. WebApplicationContext中特殊的bean 13.2. DispatcherServlet初始化参数 13.3. AbstractController提供的功能 13.4. 视图解析器 13.5. CookieLocaleResolver的属性 13.6. ThemeResolver的实现 14.1. 宏定义表 14.2. JasperReports View Classes 14.3. JasperReportsMultiFormatView默认Mapping Key映射 16.1. WebApplicationContext中特殊的Bean 16.2. DispatcherPortlet 的初始化参数 16.3. AbstractController提供的功能 19.1. JMS listener 元素的属性 19.2. JMS 元素的属性 19.3. JMS 元素的属性 20.1. 注册行为 20.2. 源代码级的元数据类型 20.3. 源代码级的元数据参数 21.1. Usage of Interaction execute methods 21.2. Usage of Interaction execute methods A.1. Eclipse XML editors D.1. Attributes D.2. Attributes D.3. Attributes D.4. Attributes D.5. Attributes D.6. Attributes D.7. Attributes D.8. Attributes E.1. Attributes E.2. Attributes E.3. Attributes E.4. Attributes E.5. Attributes E.6. Attributes E.7. Attributes E.8. Attributes E.9. Attributes E.10. Attributes E.11. Attributes E.12. Attributes E.13. Attributes E.14. Attributes

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值