第二章、IOC容器和Bean的配置

一、IOC和DI

1.1、IOC(Inversion of Control):反转控制

  在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。

  反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。

1.2、DI(Dependency Injection):依赖注入

  IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。

  IOC 描述的是一种思想,而DI 是对IOC思想的具体实现。

1.3、IOC容器的实现

  在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。

  Spring提供了IOC容器的两种实现方式

    • BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
    • ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。

  

1.4、ApplicationContext的主要实现类

  1. ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件。
  2. FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件。
  3. 在IOC容器初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。

1.5、ConfigurableApplicationContext

  1. 是ApplicationContext的子接口,包含一些扩展方法。
  2. refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文(容器)的能力。

二、获取IOC容器中的Bean

  1、从IOC容器中获取bean时,除了通过id值获取,还可以通过bean的类型获取。但如果同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。

Student stu = context.getBean(Student. class);

  2、或者可以使用另外一个重载的方法,同时指定bean的id值和类型

//推荐使用
Student stu = cxt.getBean(“student”,Student. class);

三、给Bean的属性赋值

3.1、依赖注入的方式

(1)、通过bean的setXxx()方法赋值

<bean id="stu" class="com.jdy.spring2020.bean.Student">
        <property name="age" value="18"/>
        <property name="name" value="jdy"/>
        <property name="studentId" value="001"/>
</bean>

(2)、通过bean的构造器赋值

  • Spring自动匹配合适的构造器
    <bean id="student" class="com.jdy.spring2020.bean.Student">
        <constructor-arg name="studentId" value="0001"/>
        <constructor-arg name="name" value="jdy"/>
        <constructor-arg name="age" value="18"/>
    </bean>
  • 通过索引值指定参数位置
<!--通过索引值指定参数位置 索引从0开始-->
    <bean id="student" class="com.jdy.spring2020.bean.Student">
        <constructor-arg index="0" value="0001"/>
        <constructor-arg index="1" value="jdy"/>
        <constructor-arg index="2" value="18"/>
    </bean>
  • 通过类型区分重载的构造器
<bean id="student" class="com.jdy.spring2020.bean.Student">
        <constructor-arg index="0" value="0001" type="java.lang.String"/>
        <constructor-arg index="1" value="jdy" type="java.lang.String"/>
        <constructor-arg index="2" value="18" type="java.lang.String"/>
</bean>

3.2、p名称空间

  为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。Spring从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置Bean

  IDEA导入命名空间方法: https://www.cnblogs.com/jdy1022/p/13650099.html 

<!--p命名空间-->
<bean id="student" class="com.jdy.spring2020.bean.Student" p:studentId="0001" p:name="jdy" p:age="18"/>

3.3、可以使用的值

(1)、字面量

  若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来

<bean id="student" class="com.jdy.spring2020.bean.Student">
        <property name="name">
            <value> <![CDATA[<?><@!#^&%*!]]></value>
        </property>
</bean>

(2)、外部已声明的Bean

<bean id="student" class="com.jdy.spring2020.bean.Student">
   <property name="book" ref="book"/>
</bean>
<bean id="book" class="com.jdy.spring2020.bean.Book">
   <property name="name" value="历史"/>
   <property name="price" value="10.0"/>
</bean>
<property> 的ref属性指明引用外部Bean的id。

(3)、内部bean

  当Bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean。内部bean声明直接包含在<property><constructor-arg>元素里,不需要设置任何id或name属性 内部bean不能使用在任何其他地方

<!--外部改成内部-->
<bean id="student" class="com.jdy.spring2020.bean.Student">
        <property name="book">
            <bean class="com.jdy.spring2020.bean.Book">
                <property name="name" value="历史"/>
                <property name="price" value="10.0"/>
            </bean>
        </property>
</bean>

四、Bean的集合属性

  在Spring中可以通过一组内置的XML标签来配置集合属性,例如:<list><set><map>

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private String studentId;
    private String name;
    private String age;
    private Book book;
    private List<Book> books;
    private Map<String,Object> hobby;
   private List<String> phoneNos; }
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Book {private String book_name;
    private String price;
}

4.1、数组和List

  配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素。这些标签 可以通过<value>指定简单的常量值,通过<ref>指定对其他Bean的引用。通过<bean> 指定内置bean定义。通过<null/>指定空元素。甚至可以内嵌其他集合。

    <!--数组和List-->
    <bean id="stu" class="com.jdy.spring2020.bean.Student">
        <property name="books">
            <list>
                <ref bean="englist_book"/>
                <ref bean="java_book"/>
            </list>
        </property>
    </bean>

    <bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/>
    <bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" p:price="20.0"/>

  数组的定义和List一样,都使用<list>元素。 配置java.util.Set需要使用<set>标签,定义的方法与List一样。

4.2、Map

  • Java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签。每个条目包含一个键和一个值。
  • 必须在<key>标签里定义键。
  • 因为键和值的类型没有限制,所以可以自由地为它们指定<value><ref><bean><null/>元素。
  • 可以将Map的键和值作为<entry>的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。
<bean id="stu" class="com.jdy.spring2020.bean.Student">
        <property name="hobby">
            <map>
                <entry>
                    <key>
                        <value>
                            爱好
                        </value>
                    </key>
                    <value></value>
                </entry>
                <entry>
                    <key>
                        <value>
                           英语书
                        </value>
                    </key>
                     <ref bean="englist_book"/>
                </entry>
            </map>
        </property>
    </bean>
    
    <bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/>

4.3、集合类型的Bean

  如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean重复引用。

  配置集合类型的bean需要引入util名称空间。

  首先在配置文件 中 xsi:schemaLocation 后面补上下面约束

http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
<bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/>
<bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" p:price="20.0"/>

    <util:list id="books">
        <ref bean="englist_book"/>
        <ref bean="java_book"/>
    </util:list>

    <util:list id="phoneNos">
        <value>10086</value>
        <value>10001</value>
        <value>10000</value>
    </util:list>

    <util:map id="hobby">
        <entry key="爱好" value="吃" />
        <entry key="book" value-ref="java_book" />
    </util:map>

    <bean id="stu" class="com.jdy.spring2020.bean.Student">
        <property name="hobby" ref="hobby"/>
        <property name="books" ref="books"/>
        <property name="phoneNos" ref="phoneNos"/>
    </bean>

五、FactoryBean

  Spring中有两种类型的bean,一种是普通bean,另一种是工厂Bean,即FactoryBean 。

    • 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,工厂Bean返回的是该工厂bean的getObject方法所返回的对象
    • 工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
public class MyFactoryBean implements FactoryBean<Book> {
    @Override
    public Book getObject() throws Exception {
        return new Book("数学","20.0");
    }

    /**
     * 返回具体bean对象类型
     * @return
     */
    @Override
    public Class<Book> getObjectType() {
        return Book.class;
    }

    /**
     * 是否是单利
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}
<bean id="myFactoryBean" class="com.jdy.spring2020.factorybean.MyFactoryBean"> </bean>
@Test
    public void test_method02() {

        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

        Book book = (Book) context.getBean("myFactoryBean");
        System.out.println("book = " + book);
    }

   通过debug模式可知,在MyFactoryBean中3个方法的执行顺序依次是isSingleton、getObjectType、getObject。

六、 Bean的高级配置

6.1 配置信息的继承

  book0和book1的price属性值一样,可以提出来,继承base bean ,book0和book1继承base。

<bean id="baseBook" class="com.jdy.spring2020.bean.Book" >
      <property name="price" value="10"/>
</bean>

<bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" parent="baseBook"/>
<bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" parent="baseBook"/>
  • 父bean可以作为配置模板,也可以作为bean实例。若只想把父bean作为模板,可以设置<bean>的abstract 属性为true,这样Spring将不会实例化这个bean

  • 如果一个bean的class属性没有指定,则必须是抽象bean

  • 并不是<bean>元素里的所有属性都会被继承。比如:autowire,abstract等。

  • 也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。 但此时abstract必须设为true。

6.2 bean之间的依赖

  有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Student对象的时候必须创建Book。这里需要注意的是依赖关系不等于引用关系,Student即使依赖Book也可以不引用它。

public class A {
    public A() {
        System.out.println("A");
    }
}
public class B {
    public B() {
        System.out.println("B");
    }
}
<!--<bean id="a" class="com.jdy.spring2020.bean.A"></bean>-->
<bean id="a" class="com.jdy.spring2020.bean.A" depends-on="b" ></bean>
<bean id="b" class="com.jdy.spring2020.bean.B" scope="prototype"></bean>
 @Test
public void test_method04() {
     ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
 }

   我们知道,java类在被IOC初始化时会调用构造器,单利Bean在IOC容器初始化时,就会被初始化,多实例Bean是在调用时被初始化,基于这些条件 A的<bean>中没有depends-on 时IOC只会加载A,否则A B都会加载。

 七、Bean的作用域

  在Spring中,可以在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。

  默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一 一个实例(默然单例Bean),整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。

  

类别 说明
singleton 在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在。
prototype 每次调用getBean()时都会返回一个新的实例
request 每次HTTP请求都会创建一个新的Bean,该作用域适用于WebApplicationContxet环境
session 同一个HTTP Seesion共享一个Bean不用的 HTTP Seesion使用不同的Bean,该作用域适用于WebApplicationContxet环境

 

  • 当xml文件中bean的作用域为单例时,Spring会在IOC容器对象创建时就创建bean的对象实例。

  • 当bean的作用域为prototype时,IOC容器在获取bean的实例时创建bean的实例对象。

八、Bean的生命周期

8.1、Bean的初始化和销毁方法

  1. Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。

  2. Spring IOC容器对bean的生命周期进行管理的过程:

    1. 通过构造器或工厂方法创建bean实例

    2. 为bean的属性设置值和对其他bean的引用

    3. 调用bean的初始化方法

    4. bean可以使用了

    5. 当容器关闭时,调用bean的销毁方法

@ToString
public class Car {

    private String brand;
    private String price;
    public void inti_method(){
        System.out.println("Car-inti_method....");
    }
    public void destory_method(){
        System.out.println("Car-destory_method....");
    }
    public Car() {
        System.out.println("Car-无参构造....");
    }
    public void setBrand(String brand) {
        System.out.println("Car-setBrand方法....");
        this.brand = brand;
    }
} 
<!--在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法-->
    <bean id="car" class="com.jdy.spring2020.bean.Car" p:brand="五菱" init-method="inti_method" destroy-method="destory_method"/> 
public class Test_02 {
    ClassPathXmlApplicationContext context = null;
    {
        context = new ClassPathXmlApplicationContext("application_02.xml");
    }

    @Test
    public void test() {
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(context.getBean(beanDefinitionName));
        }
    }
} 

8.2、Bean的后置处理器

  1. bean后置处理器允许在调用初始化方法前后对bean进行额外的处理

  2. bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改bean的属性

  3. bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:

    • postProcessBeforeInitialization(Object, String)

    • postProcessAfterInitialization(Object, String)

    <bean id="myProcessor" class="com.jdy.spring2020.Processor.MyProcessor"/>
/**
 * bean的购置处理器:对IOC容器中所有的bean都起作用
 */
public class MyProcessor implements BeanPostProcessor {

    public static final String TITLE="Bean后置处理器";
    /**
     *在bean的生命周期的初始化方法之前执行
     * @param bean  正在被创建的bean对象
     * @param beanName  bean对象的id属性值
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(TITLE+"-Before方法......");
        return bean;
    }

    /**
     * bean的生命周期的初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(TITLE+"-After方法......");
        return bean;
    }
}

  添加bean后置处理器后bean的生命周期

    • 通过构造器或工厂方法创建bean实例

    • 为bean的属性设置值和对其他bean的引用

    • 将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法

    • 调用bean的初始化方法

    • 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法

    • bean可以使用了

    • 当容器关闭时调用bean的销毁方法

九、引用外部属性文件

  当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。

9.1 直接配置

<!-- 直接配置 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="jdbcUrl" value="jdbc:mysql:///db"/>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    </bean>

9.2 使用外部的属性文件

 <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.2</version>
</dependency>
  • 指定properties属性文件的位置

!-- classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
  • 从properties属性文件中引入属性值
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${prop.userName}"/>
    <property name="password" value="${prop.password}"/>
    <property name="jdbcUrl" value="${prop.url}"/>
    <property name="driverClass" value="${prop.driverClass}"/>
</bean>
  • 创建properties属性文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
  • 测试
public class Test_02 {
    ClassPathXmlApplicationContext context = null;
    {
        context = new ClassPathXmlApplicationContext("application_02.xml");
    }

    @Test
    public void test() {
        ComboPooledDataSource dataSource = context.getBean("dataSource",ComboPooledDataSource.class);
        System.out.println(dataSource.getUser());
        System.out.println(dataSource.getPassword());
        System.out.println(dataSource.getJdbcUrl());
        System.out.println(dataSource.getDriverClass());
    }
}

十、自动装配

10.1 自动装配的概念

  • 手动装配:以value或ref的方式明确指定属性值都是手动装配。

  • 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

10.2 装配模式

  1. 根据类型自动装配:将类型匹配的bean作为属性注入到另一个bean中。若IOC容器中有多个与目标bean类型一致的bean,Spring将无法判定哪个bean最合适该属性,所以不能执行自动装配

  2. 根据名称自动装配:必须将目标bean的名称和属性名设置的完全相同

  3. 通过构造器自动装配:当bean中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。

@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
    private Dog dog;
}
@ToString
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {

    private String name;
    private String age;
}

  自动装配:

  • byName:用Person中dog属性的setDog()后面的Dog-->将Dog转成dog-->在容器中找到id为dog的bean-->装配

  • byType: 使用bean的属性的类型与IOC容器中<bean>的class进行匹配,如果唯一匹配则装配成功, 如果匹配到多个兼容类型的bean,则抛出异常。

<bean id="person" class="com.jdy.spring2020.bean.Person" autowire="byName">
     <property name="name" value="小明"/>
</bean>
<bean id="dog"  class="com.jdy.spring2020.bean.Dog">
    <property name="name" value="阿黄"/>
    <property name="age" value="10"/>
</bean>

十一、通过注解配置Bean

  相对于XML方式而言,通过注解的方式配置bean更加简洁和优雅,而且和MVC组件化开发的理念十分契合,是开发中常用的使用方式。

11.1、使用注解标识组件

  1. 普通组件:@Component 标识一个受Spring IOC容器管理的组件

  2. 持久化层组件:@Repository 标识一个受Spring IOC容器管理的持久化层组件

  3. 业务逻辑层组件:@Service 标识一个受Spring IOC容器管理的业务逻辑层组件

  4. 表述层控制器组件:@Controller 标识一个受Spring IOC容器管理的表述层控制器组件

  5. 组件命名规则 ①默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id ②使用组件注解的value属性指定bean的id

  注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。

11.2、扫描组件

  组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。

(1)、指定被扫描的package

<context:component-scan base-package="com.jdy.spring2020"/>
  • base-packag:属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。

  • 当需要扫描多个包时可以使用逗号分隔。

(2)、包扫描-包含与排除

  • <context:include-filter> 子节点表示要包含的目标类 ,扫描指定的组件时,要关闭默认扫描 use-default-filters=false。注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。

  • <context:exclude-filter>子节点表示要排除在外的目标类

  • component-scan下可以拥有若干个include-filter和exclude-filter子节点

(3)、扫描规则

  下面演示的是按照规则扫描指定的组件,排除组件,用exclude-filter ,type 和expression的用法一样。

    • type:指定扫描规则,常见的扫描规则:annotationassignableregexcustom
    • expression:表达式。

  1、annotation :按照注解类型进行扫描排除 主要有@Controller @Service @Repository

    如下面:只将含有@Service注解的组件注册到 IOC。

<context:component-scan base-package="com" >
  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

   2、assignable:根据目标组件是否是指定类型的子类的方式进行过滤。

    如下:将MyService和MyService的子类添加到 IOC。

<context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false">
   <context:include-filter type="assignable" expression="com.jdy.spring2020.scan.service.MyService"/>
</context:component-scan>

   3、regex:按照正则表达式

    如下:将所有已Service结尾的java类添加到IOC容器,不管这个java类上有没有注解

<context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false">
        <!--type是给正则表达式,类名以Service结尾的会被扫描进Spring容器-->
        <context:include-filter type="regex" expression="com.jdy.spring2020.scan.*Service"/>
</context:component-scan>

   4、custom:通过编码的方式自定义过滤

  • 编写自定义的排除规则 实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {


    /**
     *
     * @param metadataReader:读取到当前正在扫描的类的信息
     * @param metadataReaderFactory :获取其他类(如超类和接口)的元数据读取器的工厂
     * @return  match方法返回值true:表示规则匹配成功 false:表示规则匹配失败
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取类名
        String className = classMetadata.getClassName();
        //获取当前类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前类型资源信息(路径信息)
        Resource resource = metadataReader.getResource();
        //
        if(className.contains("Service")){
            return true;
        }
        return false;
    }
}
  •  编写xml
    <context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false">
        <context:include-filter type="custom" expression="com.jdy.spring2020.MyTypeFilter"/>
    </context:component-scan>

11.3 组件装配

  1. 需求

    Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。

  2. 实现依据

    在指定要扫描的包时,<context:component-scan> 元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。

  3. @Autowired注解

    1. 根据类型实现自动装配。首先使用byType的方法进行自动装配,如果能唯一匹配,则装配成功,如果匹配到多个兼容性的bean,还会尝试使用byName的方法进行唯一性确定如果你能唯一确定,装配成功

    2. 构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解。

    3. 默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。

    4. 若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false

    5. 默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。

    6. @Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。

    7. @Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。

    8. @Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。

  4. @Resource

    @Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。

  5. @Inject @Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired。

 

posted @ 2020-09-16 11:10  jingdy  阅读(142)  评论(0编辑  收藏  举报