Spring Framework5.1.5|核心技术(一)——IOC容器和Bean

6 篇文章 0 订阅

参考官方文档

概述

支持Groocy和Kotlin,从Spring Framework 5.0开始,Spring需要JDK 8+(Java SE 8+),并且已经为JDK 9提供了开箱即用的支持。

springframe为不同的应用程序体系结构提供了基础支持:消息传递、事务数据和持久性以及Web,基于Servlet的Spring WebMVC,以及Spring WebFlux响应式框架。

Spring WebMVCSpring WebFlux
阻塞式IO非阻塞式IO

1 IOC容器

1.1 Spring IOC容器、Bean简介

org.springframework.beans和org.springframework.context包是Spring框架的IOC容器的基础。该 BeanFactory (工厂方法模式)接口提供了一种能够管理任何类型对象的高级配置机制。ApplicationContext接口继承了BeanFactory接口:

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

简而言之,它BeanFactory提供了配置框架和基本功能,ApplicationContext添加了更多特定于企业的功能。ApplicationContext是完整的超集。
在Spring中,构成应用程序主干并由Spring IoC 容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装、管理的对象。否则,bean只是应用程序中许多对象之一Bean及其之间的依赖关系反映在容器使用的配置元数据中

1.2 Spring IOC容器概述

spring工作高级视图如下,有三个步骤:

  1. 配置元数据配置
  2. 配置POJOs
  3. 然后生产全部配置的环境等待使用
    IOC容器

1.2.1 IOC容器之配置元数据

有3中配置方式:基于XML配置基于注解配置基于Java配置

传统上,基于XML配置元数据:

<bean>配置为<beans>元素的子元素:

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

<bean>的id属性:标识单个bean
class属性:bean的完全限定类名(包名+类名)

1.2.2 IOC容器之实例化容器

提供给ApplicationContext构造函数的位置路径实际上是资源字符串,允许容器从各种外部资源(如本地文件系统,Java等)加载配置元数据CLASSPATH。

实例化容器如下:

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

配置元数据:服务层对象配置文件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
        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>

配置元数据:数据访问对象(DAO)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
        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>

bean可以定义在多个XML文件。通常,每个单独的XML配置文件都代表架构中的逻辑层或模块。
加载XML配置文件有2种方式

  • 可以用 应用程序上下文构造函数(如:ClassPathXmlApplicationContext()) 从所有这些XML文件加载bean定义。此构造函数采用多个Resource位置,如:
ApplicationContext context = 
				new ClassPathXmlApplicationContext("services.xml", "daos.xml");
  • 可以用一个或多个 <import> 元素来从另一个或多个XML文件加载bean定义。例如:
<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
    
    <bean id="bean1" class="..."/>
</beans>

对于资源路径推荐使用:完全限定的资源位置而不是相对路径。如flie:C:/config/services.xml或者classpath:/config/services.xml

命名空间本身提供了import指令功能。spring提供了一系列XML命名空间除了bean定义配置功能,还提供了其他配置功能。如:context、util命名空间。

1.2.3 IOC容器之使用容器

ApplicationContext是高级工厂的接口,能够维护不同bean及其依赖项的注册表。使用其
T getBean(String name, Class requiredType) 方法您可以检索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();

在应用程序种,不应该调用getBean()方法。因为Spring和Web的集成框架可以给各种框架组件(如:控制器)进行依赖注入,允许通过元数据声明对指定bean的依赖,从而得到框架组件、指定bean的实例。

1.3 Bean概述

一个IOC容器管理一个或多个bean。这些bean根据提供给容器的元数据创建。在容器本身内,这些bean定义表示为BeanDefinition 对象,其中包含(以及其他信息)以下元数据:

  • 包限定的类名(class属性):通常是正在定义的bean的实际实现类。
  • Bean行为配置元素(如:scope属性),说明bean在容器中的行为方式(范围,生命周期回调等)。
  • 引用bean执行所需的其他bean,这些引用也称为协作者或依赖项。
  • 在新创建的对象中设置的其他配置设置。例如,在管理连接池的Bean中使用的连接数,或池的大小限制。

bean属性定义:

属性解释
class实例化bean
name命名bean
scopebean范围
constructor arguments依赖注入
properties依赖注入
autowiring mode自动装配模式
lazy-initialization mode懒初始化模式
initialization method初始化方法回调
destruction method销毁方法回调

ApplicationContext(应用程序上下文)的实现类 :包含创建指定bean定义信息, 也运允许由用户在容器外面创建的对象进行注册。
通过ApplicationContext的getBeanFactory()方法返回DefaultListableBeanFactory实例,然后通过DefaultListableBeanFactory的registerSingleton(…)、registerBeanDefinition(…)来进行注册
通常,bean还是通过bean定义元数据来进行注册。

要尽早注册“bean元数据、手动提供的单例实例”,为了容器在自动装配和其他内省步骤期间正确推理他们
虽然某些情况下,支持覆盖现有元数据、单例实例,但是在运行时注册新的bean并没有得到官方支持,且可能导致并发访问异常、容器中bean状态不一致等问题。

1.3.1 命名bean

每个bean可以由一个或多个标识符。这些标识符在IOC容器中必须是唯一的,额外的当作别名。
在XML中,可以使用id属性、mane属性或者同时使用这两个属性指定bean标识符。若不显式提供id、name,则容器会给bean生成唯一的名称。

Bean命名约定:
bean名称以小写字母开头,驼峰式命名。

通过类路径classpath中的组件扫描,Spring按Bean命名约定给没有命名的Bean生成唯一名称。

在Bean定义外面,定义bean别名

可以在bean定义中,给bean提供多个名称。方法是:通过id属性和任意数量的name属性。

实际应用中,bean定义的别名不是足够的。有时要给别处定义的bean提供别名。在大型系统中通常是这种情况。在XML配置中,定义别名如下:

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

在Java配置中,使用@Bean注解提供别名。

1.3.2 实例化bean

若使用基于XML配置的元数据实例化bean,则要指定<bean />元素的class属性。使用class属性实例化bean的方式有以下两种:

  • 在容器中反向调用class指定类的构造函数实例化bean,类似于new实例
  • 调用为实例化bean提供的静态工厂方法实例化bean
使用构造函数实例化

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

使用静态工厂实例化

定义使用静态工厂方法创建的bean时,可以使用该class 属性指定包含static工厂方法的类,并使用factory-method属性指定静态工厂方法名。您应该能够调用此方法(使用后面描述的可选参数)并返回一个活动对象,随后将其视为通过构造函数创建的对象。这种bean定义的一个用途是static在遗留代码中调用工厂。如下:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    
    private ClientService() {}
    // 静态工厂方法
    public static ClientService createInstance() {
        return clientService;
    }
}
使用实例工厂实例化

与静态工厂方法实例化类似,使用实例工厂方法进行实例化会从容器调用现有bean的非静态方法来创建新bean。要使用此机制,请将该class属性保留为空,并在factory-bean属性中指定当前(或父/祖先)容器中bean的名称,该容器包含要调用以创建对象的实例方法。使用factory-method属性设置工厂方法本身的名称。如下:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
    private static ClientService clientService = new ClientServiceImpl();
    // 实例工厂方法
    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

此方法表明,工厂bean(指Spring容器中配置,并通过实例工厂方法或者静态工厂方法实例化的bean)可以通过依赖注入进行管理和配置

1.4 依赖

1.4.1 依赖注入(DI)

依赖注入是一个过程。这个过程中,对象只能通过构造函数参数、工厂方法参数、对象实例化后对象实例设置的属性来定义它们的依赖关系。
从工厂方法返回创建的对象,然后容器创建bean时注入这些依赖项(创建的对象)。这个过程基本是bean本身的反向,因此叫控制反转,它使用类的直接构造或者服务定位器模式来控制依赖项的实例化或者位置。

DI有两个主要变体:

  • 基于构造函数的DI
  • 基于setter的DI
1)基于构造函数的依赖注入

容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项,与通过调用静态工厂方法来构造bean类似。如下是一个只能通过构造函数依赖注入的类:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
构造函数参数-解析:

通过使用参数类型进行构造函数解析匹配。若bean定义的构造函数参数中无二义性,则bean构造函数中参数的顺序就是实例化bean时,将这些参数提供给适当的构造函数的顺序。
bean类:

package x.y;

public class ThingOne {

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

设ThingTwo和ThingThree没有继承关系——不存在二义性。以下配置正常执行:

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

当引用另一个类时,类型已知,则可以按类型匹配。当使用基本数据类型时,如<value>true<value/>,spring无法确定值的类型,则无法在没有帮助情况下按类型匹配。如:

package examples;

public class ExampleBean {

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

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

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
(1)构造函数参数-类型匹配:

使用type属性显式指定构造函数参数的类型,则容器可以使用与简单类型的类型匹配。如下:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
(2)构造函数参数-索引:

可以使用该index属性显式指定构造函数参数的索引。如下:

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

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

(3)构造函数参数-名称:

可以使用构造函数参数名称进行值消歧。如下:

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

为了使这项工作开箱即用,必须在启用调试标志的情况下编译代码,以便Spring可以从构造函数中查找参数名称。如果您不能或不想使用debug标志编译代码,则可以使用 @ConstructorProperties JDK批注显式命名构造函数参数。如下:

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
2)基于Setter的依赖注入

容器调用无参构造函数后,通过bean上的容器调用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;
    }
}

用基于构造函数DI还是用基于setter 的DI?
可以选择混合使用。将构造函数DI用于强制依赖项,setter的DI用于可选依赖项
可以在setter方法上用@Required注解标注使属性成为必需依赖项,但是最好选择用构造函数注入。
有时第三方类没有公开的setter方法,只能选择用构造函数DI。

“依赖性解决过程”

容器执行bean依赖性解析,如下:

  • 使用ApplicationContext描述所有bean的配置元数据创建和初始化。可以通过XML,Java代码或注释指定配置元数据。
  • 对于每个bean,如果使用的是依赖于普通构造函数的,那么它的依赖关系将以属性,构造函数参数或static-factory方法的参数的形式表示。实际创建 bean 时,会将这些依赖项提供给bean 。
  • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
  • 作为值的每个属性或构造函数参数都从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring能够转换成字符串格式提供给所有的基本数据类型,比如数值int, long,String,boolean,等。

Spring容器在创建容器时验证每个bean的配置。但是,在实际创建 bean之前,不会设置bean属性本身。创建容器时会创建单例作用域并设置为预先实例化(默认值)的Bean。范围在Bean范围中定义。否则,仅在请求时才创建bean。创建bean可能会导致创建bean的视图,因为bean的依赖关系及其依赖关系(依此类推)被创建和分配。请注意,这些依赖项之间的解决方案不匹配可能会显示较晚,即首次创建受影响的bean时。

如果您主要使用构造函数注入,则可能创建无法解析的循环依赖关系场景。

ApplicationContext实现类默认情况下,预先实例化单例bean。单例bean也可以设置懒惰初始化。

若不存在循环依赖关系,当一个或者多个协作bean被注入依赖bean时,每个协作bean(若不是预先实例化bean)则在注入之前已经实例化了。

依赖代码实例
基于XML配置元数据的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;
    }
}
基于XML配置元数据的构造函数依赖注入:
<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;
    }
}

1.4.2 依赖关系和配置

直值(基元、字符串等)

在<property />元素的value属性赋值字符转。配置如下:

<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命名空间更简洁配置:

<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>
合作者

<constructor />和<property />元素的ref属性。如:

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

<bean />内嵌套<bean />,如:

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

<list />,<set />,<map />,<props />元素对应java的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>
空字符串值

<null /> 元素处理null值,如:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

等同以下java代码:

exampleBean.setEmail(null);
p命名空间的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
        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>

p命名空间不如标准XML灵活。

c命名空间的XML快捷方式

spring3.1引入此功能。
以下与基于构造函数的依赖注入操作相同:

<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="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <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 declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>
复合属性名称
<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>

该bean具有一个fred属性具有bob属性,该bob属性具有sammy 属性,并且最终sammy属性的值设置为123。为了使其工作,在构造bean之后,fred属性和bob属性不得为null。否则,NullPointerException抛出一个。

1.4.3 使用依赖depends-on

如果bean是另一个bean的依赖项,通常意味着一个bean为另一个bean的属性。
通常,您可以使用基于XML的配置元数据中的 元素来完成此操作。但是,有时bean之间的依赖关系不那么直接; 例如,需要触发类的静态初始化程序,例如数据库驱动程序注册。depends-on在初始化使用此元素的bean之前,该属性可以显式强制初始化一个或多个bean。
要表示对多个bean的依赖关系,请提供bean名称列表作为depends-on属性的值,使用逗号、空格、分号作为有效分隔符:

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

depends-onbean定义中的属性既可以指定初始化时依赖关系,也可以指定仅限单例 bean的相应销毁时依赖关系。depends-on 在给定的bean本身被销毁之前,首先销毁定义与给定bean 的关系的从属bean 。因此depends-on也可以控制关​​机顺序。

1.4.4 懒惰初始化bean

默认情况下,ApplicationContext实现会急切地创建、配置所有 单例 bean,作为初始化过程的一部分。通常,这种预先实例化是可取的,因为配置或周围环境中的错误可以立即发现。可以通过将bean定义标记为延迟初始化来阻止单例bean的预实例化。延迟初始化的bean告诉IoC容器在第一次请求时创建bean实例,而不是在启动时。

在XML中,此行为由元素<bean />的lazy-init属性控制; 例如:

// 容器第一次请求时初始化
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>

//容器启动时初始化
<bean name="not.lazy" class="com.foo.AnotherBean"/>

lazy-init的bean是no lazy-init单例bean的依赖项时 ,ApplicationContext会在启动时创建延迟初始化的bean,因为它必须满足单例的依赖关系(即此时懒惰初始化设置是无用的)。

您还可以控制容器级别的延迟初始化

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

1.4.5 自动装配协作者

Spring容器可以自动连接协作bean之间的关系。您可以通过检查bean的内容,允许Spring自动为您的bean解析协作者(其他bean)。

自动装配有以下优点:

  • 可以显著减少指定成员变脸、构造函数参数
  • 可以随对象变化更新配置

XML中可以使用<bean />元素的autowired属性指定自动装配模式。

自动装配的4种模式:

模式解释
no(默认值)无自动装配。必须通过ref元素定义Bean引用。不建议对较大的部署更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。
byName按属性名称自动装配。Spring查找与需要自动装配的属性同名的bean。例如,如果bean定义按名称设置为autowire,并且它包含master属性(即,它具有 setMaster(…)方法),则Spring会查找名为的bean定义master,并使用它来设置属性。
byType如果容器中只存在一个属性类型的bean,则允许自动装配属性。如果存在多个,则抛出致命异常,这表示您不能对该bean 使用byType自动装配。如果没有匹配的bean,则没有任何反应; 该物业未设定。
constructor类似于byType,但适用于构造函数参数。如果容器中没有构造函数参数类型的一个bean,则会引发致命错误。

1.4.6 方法注入

大多数应用场景中,容器中多数bean是单例的。

查找方法注入

Lookup方法注入是容器覆盖容器托管bean上的方法的能力 ,以返回容器中另一个命名bean的查找结果。查找通常涉及原型bean,如上一节中描述的场景。Spring Framework通过使用CGLIB库中的字节码生成来实现此方法注入,以动态生成覆盖该方法的子类。

任意方法更换

与查找方法注入相比,一种不太有用的方法注入形式是能够使用另一个方法实现替换托管bean中的任意方法。

1.5 bean范围

Scope描述
singleton(默认值)将每个Spring IoC容器的单个bean定义范围限定为单个对象实例。
prototype将单个bean定义范围限定为任意数量的对象实例。
request将单个bean定义范围限定为单个HTTP请求的生命周期; 也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。仅在具有Web感知功能的Spring环境中有效ApplicationContext。
session将单个bean定义范围限定为HTTP会话的生命周期。仅在Web感知Spring ApplicationContext的上下文中有效
application将单个bean定义范围限定为a的生命周期ServletContext。仅在具有Web感知功能的Spring环境中有效ApplicationContext。
websocket将单个bean定义范围限定为a的生命周期WebSocket。仅在具有Web感知功能的Spring环境中有效ApplicationContext。

从Spring 3.0开始,线程范围可用,但默认情况下未注册。

1.6 自定义bean的本质

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

1.7 Bean定义继承

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

1.8 容器AOP

1.9 基于注解的容器配置

spring2.5引入此功能。

spring“使用注解配置”和“使用xml配置”哪个更好?
使用注解:编码简单,配置更少;产生大量上下文不易于查看。
使用xml:可以在不接触源码或重新编译情况下连接组件;配置繁多。

注解注入XML注入先执行。因此,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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
     <!--启动注解配置-->
    <context:annotation-config/>
</beans>

<context:annotation-config/>只会查找定义在同一个应用程序上下文的bean上面的注解。即:给DispatcherServlet在一个WebApplicationContext中定义一个 <context:annotation-config/>,容器只会扫描控制器里@Autowired标注的bean类,不会扫描服务。

@Required:

标注bean类成员变量的setter方法,表示类的这个成员变量必选的。spring framework5.1正式弃用这个注解,“推荐使用构造函数注入”。如下:

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // ...
}

此注释仅表示受影响的bean属性必须在配置时填充,通过bean定义中的显式属性值或通过自动装配填充。如果尚未填充受影响的bean属性,容器将引发异常。

@Autowired:

可以放在构造函数、setter方法、多参数的任意名方法、类属性。

可以使用 JSR330 的@Inject注解替代spring的@AutoWired注解。

用法1:@Autowired注解用于构造函数

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

从spring framework4.3开始,若bean类只定义了一个构造函数,则可以不用@Autowired注解标注构造函数;若有多个构造函数,则至少要标注一个构造函数,告诉IOC容器使用哪一个构造函数。

用法2:@Autowired注解用于setter方法

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

用法3:@Autowired注解用于具有任意名字的、多个参数的方法

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

用法4:@Autowired可以用于类的成员变量,也可以和用法1——用于构造函数混用

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}

确保目标组件——bean类——始终按照

@Primary:

微调基于注释的自动装配。

1.10 类路径扫描和组件托管

TODO

1.11 使用JSR330的标准注解

TODO

1.12 基于Java的容器配置

目前,基于Java配置元数据,更多。spring3.0引入此功能。

Java配置通常使用:@Bean、@Configuration、@Import、@DependsOn注解标注类

1.13 环境对象

TODO

1.14 注册LoadTimeWeaver

TODO

1.15 ApplicationContext(应用程序上下文)附加功能

TODO

1.16 BeanFactory

TODO


TODO:

2.2 事件

2.3 资源

2.4 I18N

2.5 验证

2.6 数据绑定

2.7 类型转换

2.8 SpEL

2.9 AOP

3 测试

4 数据访问

5 Web Servlet

6 Web Reactive

7 整合

8 语言

8.1 Kotlin

8.2 Groovy

8.3 动态语言

ERROR 5436 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/back/comment_list.html]")] with root cause org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'list' cannot be found on null at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:213) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:51) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:406) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:109) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:328) ~[spring-expression-5.1.5.RELEASE.jar:5.1.5.RELEASE] at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:263) ~[thymeleaf-spring5-3.0.11.RELEASE.jar:3.0.11.RELEASE]
最新发布
06-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值