Spring框架学习

Spring框架学习

Spring Framework Overview

Spring是开发java application的通用框架,分为多个模块(modules),核心是core container,包括configuration model(配置模型)和dependency injection(依赖注入)Spring还可以为多种应用架构(application architecture)提供支持,包括messaging,transaction data, persistence(持久化),web。Spring也提供Servlet-based Spring MVC web framework和Spring WebFlux reactive web framework。

History

Spring最早在2003年,由于J2EE过于复杂而被开发出来的。有人认为Spring和Java EE是竞争关系,但Spring更像是对Java EE的补充。Spring整合了一些EE的标准:

  • Servlet API
  • WebSocket API
  • Concurrency Utilities(并发性)
  • JSON Binding API 简介
  • Bean Validation(数据校验) 简介
  • JPA
  • JMS
  • Dependency Injection and Common Annotations

Java EE在app开发中的角色在随时间变化。早期的时候,javaEE和Spring开发的应用是部署在application server上的,今天,在Spring Boot的帮助下开发变得友好且更加云端化(devops and cloud-friendly),嵌入Servelet容器,非常容易改变。在Spring Framework5中,一个webflux应用甚至不需要Servlet API并可以运行在不含Servlet容器的server上。

Spring projects目前在逐渐丰富,建立在Spring Framework上的projects有Spring Boot,Spring Security,Spring Data,Spring Cloud,Spring Batch…

Spring的design philosophy

  • Provide choice at every level 尽可能允许不改动code的情况下变更design
  • Accommodate diverse perspectives 允许设计的灵活性
  • Maintain strong backward compatibility 对JDK和第三方库的高兼容性
  • Care about API design API被设计地简单易用
  • Set high standards for code quality 注意代码的整洁

IoC Container

Introduction

IoC: Inversion of Control(控制反转) 也可称为dependency injection(依赖注入), 定义: it is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. 容器随后在创建bean时将依赖注入(把需要的对象传入)。这个过程事实上是对bean自身控制实例化或依赖定位(通过直接初始化类或类似Service Locator Pattern机制)的inverse,这就是为什么叫Inversion of Control。

org.springframework.beansorg.springframework.context两个包是Spring Framework的IoC容器的基础。BeanFactory接口提供了能管理任何类型对象的高级配置机制。简单来说BeanFactory提供了框架配置和基本的功能。ApplicationContextBeanFactory的子接口,它添加了更多特性企业级应用的功能。在Spring中,塑造应用骨架并被IoC容器管理的对象称为bean。一个bean是一个被IoC容器实例化(instantiated),组装(assembled),管理(managed)的对象,bean只是应用中众多对象中的一个。bean和它周围的依赖都会被容器的配置影响。

Container Overview

org.springframework.context.ApplicationContext接口说明了IoC容器,容器负责实例化,配置,集成beans。容器通过阅读配置文件(configuration metadata)知道实例化(或配置,集成)哪些beans。configuration metadata可以是XML,Java annotations(注释)或java code。IoC可以传递对象以使应用更简练并丰富对象间的依赖。

Spring也提供ApplicationContext接口的一些实现,在实践中经常会创建ClassPathXmlApplicationContextFileSystemXmlApplicationContext的实例。XML是传统的定义configuration metadata的方法。你可以通过一些XML配置(修改配置为支持额外的格式)来让容器使用注释或代码作为metadata。(原文较难理解:you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats)

在大部分application scenario中IoC容器实例化多个实例并不需要详尽的用户代码,比如说网页应用中创建一个boilerplate只需要几行代码的descriptor。如果使用Spring Tool Suite就甚至只需要鼠标点几下或者键盘敲几下。

how Spring works(图)
Your application classes are combined with configuration metadata so that, after the ApplicationContext is created and initialized, you have a fully configured and executable system or application.
Spring IoC

Configuration Metadata

传统上Configuration metadata是简单易理解的XML(xml-based configuration),除此外还有annotation-based configuration和Java-based configuration(后面有详细的)。XML中一般用…把具体的配置括进去。java-based config中一般用@Configuration注释类,用@Bean注释方法。

These bean definitions correspond to the actual objects that make up your application.一般来说有service layer objects, data access objects (DAOs), presentation objects such as Struts Action instances, infrastructure objects such as Hibernate SessionFactories, JMS Queues, and so forth。通常我们不会在容器中配置一个非常详细的(fine-grained)domain objects,因为根据设计原则应该是由DAO和business logic来创建和调用domain objects。(然而Spring也可以集成AspectJ来配置IoC容器外创建的对象)
xml-based configuration file结构:

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

Instantiating a Container

初始化container时要给出metadata的路径(resource strings that let container load configuration metadata from a variety of external resources)(resources含义丰富,除了local path外还可以是读取inputstream等)。xml名称前不需要加slash。可以,但不推荐使用相对地址(…/),可以使用绝对地址(file:C;/config/services.xml或classpath:/config/services.xml),但这样就只有特定的地址。推荐使用类似’${…}’:的占位符,再替换地址,这样就可以在任何JVM上使用。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

一个例子,services.xml的config file(关于service layer objects):

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

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

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

</beans>

daos.xml:(for data access objects)

<?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="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>

很多情况下我们让一个XML对应一个逻辑层或者一个module。

Groovy文件也可以作为config metadata,基本等效于xml。

Using the container

ApplicationContext 接口的多态对象可以作为registry保存不同的beans和他们的依赖。例子:

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

事实上完全可以不用getbean方法,这样就不会产生对spring API的依赖。Spring和web框架的integration可以对web框架的组件注射依赖,因此只需要在metadata中声明依赖就可以了。

Bean Overview

Beans通过metadata定义。在容器内bean被表示为BeanDefinition对象,其包括了如下的metadata:

  1. 含包class name(package-qualified class name): typically是bean的actual implementation class
  2. Bean行为配置的元素(bean behavioral configuration elements),表明bean在容器内的行为(scope, lifecycle callbacks…)
  3. 对其他beans的引用,也被称作collaborators或dependencies
  4. 其他创建对象时的config setting,比如pool的size limit,bean内管理pool的connections的使用次数等
    metadata会设置这些properties:
    The bean definition

除了通过bean definitions,ApplicationContext实现类也可以允许用户在容器外创建的对象进行注册。需要用getBeanFactory()方法来访问ApplicationContext的BeanFactory,会返回DefaultListableBeanFactory实现。DefaultListableBeanFactory支持通过registerSingleon(..)registerBeanDefinition(..)方法注册但是typical applications只能和通过metadata定义的beans一起工作(需要时再查

Bean metadata和manually supplied singleton instances需要尽可能早注册,以便容器在autowiring和其他instropection steps中可以properly reason about(does not have unintended side effects)。运行时注册新的beans是not officially supported的,可能会导致concurrent access exceptions和inconsistent state in the bean container。

Naming Beans

每个bean可以有一个或多个identifiers(一般只有一个,多个的话可以被认为是aliases),identifiers必须在host container中唯一。在XML中可以用idname来标识identifiers。id只有一个,name可以有多个,用逗号或分号或空格隔开。也可以不指定identifiers,容器可以自动产生。如果我们指定了名称,就可以通过ref或lookup来查找和引用bean。一般inner beans和autowiring collaborators不必提供名称。

java beans的命名习惯:小写开头camel-cased。Spring为未命名组件创建的名字也是同样规则,一般就是把类名的第一个字母小写。但如果有超过一个前面字母大写(unusual)就保留这个名字不变。java beans可以命名别名,为何有这个需要呢?比如说一个application有很多components,每一个components可能有不同的命名习惯,如果某一通用的bean要被众多组件引用,那么它可以起一个component-specific的名字(为了美观方便阅读云云)。别名也可以在definition以外的地方被创建(好处是显而易见的,在需要的时候命名,不会每次都改前面定义bean的metadata,尤其是产品规模很大的时候)在xml-based config metadata中可以这样写:

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

Instantiating Beans

(很棒的定义) bean本质上是创建一个或多个对象的食谱(recipe) 容器看着bean的食谱(封装在config metadata中的bean定义)创建对象。

在xml-based file中,object类型定义在<bean/>element中的classattribute, 大部分情况下这是必须要有的定义(For exceptions, see Instantiation by Using an Instance Factory Methodand Bean Definition Inheritance.) 两种方法使用Class property:

  1. 容器创建bean时直接调用用类的构造函数(为了确定bean的类型)

  2. 为了确认是哪个类含有 static factory method(创建对象时被调用) ,还有一个更少见的情况是调用该方法创建bean。该静态方法返回的对象类型可能是另一个类

inner class names
有时候如果bean定义是在一个static nested class(内部静态类)时,内部类要使用binary name
比如说如果有一个类SomeThingcom.example package中,Something 类又有一个内部静态类OtherThing,那么bean定义中class attribute的value就是com.example.SomeThing$OtherThing

Instantiation with a Constructor
通过构造函数创建的bean不需要任何特殊的接口或多余代码,只要确认bean class就够了。Spring IoC可以管理任何类(只要你愿意),不限于真正的JavaBeans。大部分Spring的使用者倾向于在容器中使用只有default contructor,有getter和setter的java bean。

instantiation with a static factory method
通过static factory method实例化,需要class来确认factory method自身的name(因为要调用这个方法)。你应该调用这个方法并返回一个对象,整个过程看起来好像是调用了构造函数创造的,但并不是(这就是为什么需要class,不然不知道去哪里调用这个方法)。这个方法的本质目的是把实例的创建过程封装起来。

Instantiation by Using an Instance Factory Method
跟上面类似,区别是调用了非静态方法。过程是一个已经存在的bean调用非静态方法创建了这个bean。使用这种mechanism,class要leave empty(上面说的极少数情况), 在Factory-bean中确定当前(或father,ancestor)的容器中包括了实例方法的bean的name(哪个bean创建的这个bean),以及factory-method(方法名称)

一个factory class也可以有多个factory method
例子:
factory类:

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

XML:

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

这个方法显示了factory bean自身可以通过DI被管理和配置。factory bean在Spring中指的是容器中配置并可以通过实例方法或静态方法创造对象的bean。

Dependencies

Dependency Injection依赖注射

Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. 依赖注射是指对象决定其依赖的过程,这个过程只通过构造函数参数工厂方法参数在被工厂方法构造或返回的对象实例上的成员 (三种方法)。

DI的好处是让代码更加clean,并且在对象提供他们的依赖时耦合(decoupling)会更加有效。对象不会查看自身的依赖,也不知道依赖的位置和类。这样类会更容易测试,尤其是当依赖在接口或抽象基类上,这样允许单元测试中使用stub或mock。

DI有两种主要的方法:Constructor-based dependency injectionSetter-based dependency injection

Constructor-based dependency injection**

实现方法是容器调用有若干参数的constructor,每一个参数都代表一个依赖。调用静态工厂方法构造bean也几乎是等价的。
例子(这个代码没有任何特别的地方,因为通过构造函数来创造依赖是非常普通的java语法,仅仅是一个POJO,没有任何对接口,基类,注释的依赖):

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private 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 Arguments Resolution 构造函数参数解决方案
如果你把某对象交给容器管理,xml里面会定义如何注射依赖。如果通过构造函数注射依赖,那么会涉及到arguments匹配的问题。有很多种方法在xml里定义,假如是两个不涉及继承关系且没有ambiguity的类,那么不需要index和types,只需要写在<constructor - arg/> element中即可(ref =... )。因为bean的类型是已知的。如果参数类型是简单类型(如int,string等),那么容器无法确定参数类型。这时有几种办法:1. type = ... value = ... 2. index = ... value = ... 3. name = ... value = ... 需要注意的是编译的时候必须使"debug flag enabled",只有这样Spring才能从构造函数查看参数的name。如果没有这么做的话,可以使用注释@ConstructorProperties告诉Spring参数名称

Setter-based Dependency Injection

容器调用beans的setter方法(在调用无参构造函数或午餐静态工厂方法实例化bean后)
例子*(一个POJO,无特别之处,通过setter方法注入依赖):

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管理beans依赖的方法。依赖的配置原理上可以通过BeanDefinition配置(使用PropertyEditor的实例convert properties),但比较方便的实践是用几个xml bean definitions, annotated components(that is, classes annoted with @Component, @Controller…), or @Bean methods in Java-based @Configuration classes. 这些来源随后会转化成``BeanDefinition`的实例并用来载入完整的IoC容器实例。

使用Constructor-based还是setter-based DI?
最赞的实践是对必要的依赖(mandatory)用构造函数(保证required property不会是null),对可选的依赖(optional)用setter方法(也可以inject later)。Note下@Required注释用在setter方法上can be used to make the property be a required dependency. 有一些第三方的类并没有提供setter方法(同时也没有源代码),这时可能只能用构造函数。

Dependency Resolution Process 依赖决定的过程

容器依照以下过程为bean提供依赖

  1. ApplicationContext创建实例并根据Configuration metadata(XML, Java code, annotations)进行初始化。
  2. 对每一个bean来说,其依赖表现在properties, constructor arguments, 或arguments to the static-factory method(用来代替normal constructor)
  3. 每个property或constructor arguments都决定了set什么value,引用什么bean。
  4. property或constructor arguments需要type转换,by default Spring可以吧string转化成所有的built-in types(int, long, string, boolean…)

bean的配置在容器创建后就决定好了。bean properties自身是在bean被创建时被set的。pre-instantiated的singleton在容器创建时被创建。Beans要么有Bean scope,要么只在需要的时候被创建。一个bean的创建会潜在的导致a graph of beans(与其相联系的)beans被创建和指配。注意这可能会随后导致随后的依赖之间的mismatch(在创建被影响的bean时,意思是需要的依赖在使用依赖的bean之后创建,导致这个bean和被注入的bean实际上是两个)

Circular dependencies 循环依赖
如果你主要使用constructor injection,就可能导致不可解的循环依赖。举个例子,A类constructor injection需要B类实例,B类constructor injection需要A类实例,就会陷入死循环。IoC容器可以检测到这种循环引用并抛出BeanCurrentlyInCreationException异常。
这种情况需要改用用setters。这种情况事实上是先有鸡还是先有蛋的问题,需要从逻辑上排个先后顺序。

一般我们可以信任Spring来do the right thing(比如在load container时检测引用不存在的beans或循环引用)Spring会尽可能晚的解决依赖(在bean被创建的时候),也就是说即使container被正确创建了,还是可能在随后抛出异常,比如说一个bean在创建的时候因为缺失或不正确的properties而抛出异常。这会使一些不正确的配置延迟被发现。这就是为什么ApplicationContext的implementations默认会pre-instantiate singleton beans.这样以一些早期的time和memory的代价就可以在ApplicationContext创建的时候发现configuration issues。但你也可以override default behavior来lazily initialize the singleton beans。

如果没有循环依赖的情况,当有collaborating beans被注射到一个bean中时,collaborating beans会被优先配置。

DI的例子:

通过setter注射

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <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;
    }
}

通过constructor注射

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <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;
    }
}

通过static factory method(注意xml部分和constructor-based完全一样,因为区别也仅仅是封装了构造的部分。注意静态工厂方法返回的对象的类型不一定就是包括它的那个类,可以是任何类(只要合法)):

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

instance factory method(非静态)的使用在非常容易辨识(用factory-beanattribute而不是class)。暂时没有更多细节。

Dependencies and Configuration in Detail

<property/> element

Stright Values
<property/>的value attribute可以把string转化为property或arguement的实际类型。

<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="masterkaoli"/>
</bean>

更简洁的方法是使用p-namespace:

<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
    http://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="masterkaoli"/>

</beans>

还有一种方法是配置一个java.uil.Properties实例。Spring容器会把内的text转化为一个java.util.Properties`的实例。

<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

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

idref element只是把另一个bean的id传进来(只是一个string值,并不是引用)。例子:

<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是否存在。目的是为了error-proof。而且不会出现typos的问题。ProxyFactoryBean中的AOP interceptor配置中常见这种用法(防止拼写错)

引用其他的beans(collaborators)
<ref>/, collaborators在需要的时候被初始化(除了singleton)。

  1. Specifying target bean可以通过bean attribute, 只要在同一个container或父类container中的bean都可以引用,不管在不在同一个xml中。bean的value可以是target bean的idname等。
<ref bean="someBean"/>
  1. parent attribute,引用parent container中的bean(用于重名的parent bean,例子如下)
<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<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
inner bean不需要ID或者名字,容器创建它的时候会忽略scope flag,原因是inner bean只会在outer bean创建的时候创建,不会有任何情况下单独访问inner bean,他们也不能被注射到别的地方。大部分情况下inner bean的lifecycle和包括他们的bean是一样的(除非调用destruction callbacks)

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

Collections 集合
<list/> , set/, map/, props/例子:

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

注意<value/> element中的值可以是任何形式,容器会把这部分内容转化为任何需要的主要数据类型。

Collection Merging
Spring容器也支持merging collections。意思是假设有一个parent的list,那么我可以定义一个child的list,继承了parent的内容,然后在这个child 的list中可以覆盖parent的内容。但是merging仅能发生在同类型的集合间。例子:

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

Strongly-typed collection强制类型的集合
如果bean类定义了集合的元素类型,xml中会做相应的强制类型转换。例子:

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>
Null and Empry String Values

空的arguements相当于空字符,<null/>元素代表null的值,两者略有不同

XML Shortcut with the p-namespace

XML比较简洁的给properties的方法,直接把p当类一样用. -ref表示’this is not a straight value but rather a reference to another 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
        http://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"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>
XML Shortcut with the c-namespace

和p类似,p代替property,c代替constructor arguments

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

    <bean id="thingTwo" class="x.y.ThingTwo"/>
    <bean id="thingThree" class="x.y.ThingThree"/>

    <!-- traditional declaration -->
    <bean id="thingOne" class="x.y.ThingOne">
        <constructor-arg ref="thingTwo"/>
        <constructor-arg ref="thingThree"/>
        <constructor-arg value="something@somewhere.com"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="thingOne" class="x.y.ThingOne" c:thingTwo-ref="thingTwo" c:thingThree-ref="thingThree" c:email="something@somewhere.com"/>

</beans>
Compound Property Names

适用于嵌套的property

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

Using depends-on

可以用depends-on attribute表达这个bean依赖哪些beans。这样在这个bean实例化之前,容器会强迫依赖的beans初始化。(用于哪些不是properties的beans,但也有依赖。比如一个需要条件触发的静态的initizlier)。除了初始化顺序,depends-on也可以控制destruction的顺序。depends-on的beans会被先于定义的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" />

Lazy-initilized Beans

通常在默认情况下singletons会被很早的创建(在容器初始化的时候)。通常我们希望如此因为配置和环境错误会在早期被发现。如果不希望如此可以标记singletons为lazy-initilized。

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

Autowiring Collaborators

指在Spring中容器可以autowire relationships between collaborating beans,意思是Spring通过检查BeanFactory的内容自动提供依赖关系。autowiring功能有五种modes:

  1. No(default):没有autowiring,bean的引用必须通过ref
  2. byName:通过identifiers
  3. byType: 如果该type只有一种bean,可以通过类型。超过一种会报错
  4. constructor:类似byType,提供构造函数的arguments
  5. autodetect:通过检查bean class来决定byType还是constructor
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值