第二章 bean的装配

target

掌握依赖注入的三种方式
掌握基于xml的bean装配
掌握基于注解的bean装配
掌握bean的自动装配
掌握注解 @Autowired 和 @Resource

1. 依赖注入

当一个bean创建出来后,是一个空对象,需要给bean进行赋值,这个bean才有意义。依赖注入就是给IoC中的bean进行赋值,一般可以分为三种。

1.1 set注入

以前的开发中,当创建完bean对象后,一般会使用set方法给对象进行赋值。

set注入,顾名思义就是用实体类的setter方法进行注入。
set注入是用的是最多的注入方式。
注入用的标签是property。

🌰新建 Java项目Spring-02,给Teacher和Course进行注入:

第一步:在src下新建实体类:

Teacher.java:

package com.lee.spring.bean;

public class Teacher {
   private int no;//工号
   private String name;//姓名

   // getter、setter、toString略
}

Course.java

package com.lee.spring.bean;

public class Course {

   private String courseName;//课程名字
   private int courseHours;//课时量
   private Teacher courseTeacher;//授课老师

   / / getter、setter、toString略

}

第二步:在src下新建配置文件 applicationContext.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">

 <!-- 将老师信息注入到IOC容器 -->
 <bean id="teacher" class="com.lee.spring.bean.Teacher">
     <property name="name" value="张三"></property>
     <property name="no" value="1001"></property>
 </bean>

 <!-- 将课程信息注入到IOC容器 -->
 <bean id="course" class="com.lee.spring.bean.Course">
     <property name="courseName" value="语文"></property>
     <property name="courseHours" value="200"></property>
     <property name="courseTeacher" ref="teacher"></property>
 </bean>

</beans>

注意:
在赋值的时候,如果是简单类型(8个基本类型 + String),用value赋值。如果是对象类型,用ref(ref是reference的简写,引用的意思)。

第三步:编写一个测试类进行测试:

package com.lee.spring.test;

public class TestDemo {

    @Test
    public void test01() {
      //获取上下文对象:Context
      ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      //获取bean对象
      Course course = (Course)context.getBean("course");
      System.out.println(course);
    }
}

输出:

Course [courseName=语文, courseHours=200, courseTeacher=Teacher [no=1001, name=张三]]

原理解释:

setter注入用到了实体类的setter方法,和getter方法、构造方法都没关系。我们可以在setter方法里面加一行输出语句验证。

set注入底层用的是反射技术,bean标签里的class是全类名,所以可以用Class.forName("全类名")拿到这个类的所有信息,然后用new Instance方法创建一个对象,getmethod()方法拿到所有的set方法,在调用setter方法将值注入。

Class clz = Class.forName("全类名");
Object obj = clz.newInstance();//创建对象
Method method = clz.getMethod("set方法名字");//获取setter方法
method.invoke(obj, value里面的值);//调用setter方法赋值

1.2 构造器注入

对象的创建,使用的是构造方法。

构造器注入用的是实体类的构造方法。
注入用的标签是constructor-arg

第一步:在src下新建实体类,并且生成构造方法

Teacher.java

package com.lee.spring.bean;

public class Teacher {
    private int no;//工号
    private String name;//姓名
    
    public Teacher(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public Teacher() {
    }

    @Override
    public String toString() {
        return "Teacher [no=" + no + ", name=" + name + "]";
    }
    
}

Course.java

package com.lee.spring.bean;

public class Course {

    private String courseName;//课程名字
      private int courseHours;//课时量
      private Teacher courseTeacher;//授课老师
    
    public Course(String courseName, int courseHours, Teacher courseTeacher) {
        this.courseName = courseName;
        this.courseHours = courseHours;
        this.courseTeacher = courseTeacher;
    }

    public Course() {
    }

    @Override
    public String toString() {
        return "Course [courseName=" + courseName + ", courseHours=" + courseHours + ", courseTeacher=" + courseTeacher
                + "]";
    }
      
}

第二步:在 src下新建配置文件applicationContext.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">

  <!-- 将老师信息注入到IOC容器 -->
  <bean id="teacher" class="com.lee.spring.bean.Teacher">
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="no" value="2002"></constructor-arg>
  </bean>

  <!-- 将课程信息注入到IOC容器 -->
  <bean id="course" class="com.lee.spring.bean.Course">
        <constructor-arg name="courseName" value="数学"></constructor-arg>
        <constructor-arg name="courseHours" value="300"></constructor-arg>
        <constructor-arg name="courseTeacher" ref="teacher"></constructor-arg>
  </bean>

</beans>

第三步:编写一个测试类,进行测试

package com.lee.spring.test;

public class TestDemo {

    @Test
    public void test02() {
         //获取上下文对象:Context
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          //获取bean对象
          Course course = (Course)context.getBean("course");
          System.out.println(course);
    }
}

输出:Course [courseName=数学, courseHours=300, courseTeacher=Teacher [no=2002, name=李四]]

补充:配置文件还可以这样写

  • 省略name:但是顺序要和构造方法中参数的顺序一致
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher">
  <constructor-arg  value="2002"></constructor-arg>
  <constructor-arg  value="李四"></constructor-arg>
</bean>

<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course">
  <constructor-arg  value="数学"></constructor-arg>
  <constructor-arg value="300"></constructor-arg>
  <constructor-arg  ref="teacher"></constructor-arg>
</bean>
  • 用index:下标要从0开始
<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.entity.Teacher">
  <constructor-arg  index="0" value="2002"></constructor-arg>
  <constructor-arg index="1" value="李四"></constructor-arg>
</bean>

<!-- 将课程信息注入到IOC容器 -->
<bean id="course" class="com.lee.entity.Course">
  <constructor-arg  index="0" value="数学"></constructor-arg>
  <constructor-arg  index="1" value="300"></constructor-arg>
  <constructor-arg  index="2" ref="teacher"></constructor-arg>
</bean>

原理解释:

构造器注入用到了实体类的构造方法。我们可以在构造方法里面加一行输出语句验证。

构造器注入底层用的也是反射技术,bean标签里的class是全类名,所以可以用Class.forName("全类名")拿到这个类的所有信息,然后用getConstructor()方法获取构造器,调用newInstance()方法就可以创造出对象。

Class clz = Class.forName("全类名");
Constructor constructor = clz.getConstructor(int.class,String.class);
Object instance = constructor.newInstance("value里面的值");

1.3 p命名空间注入

首先需要引入p命名空间,在namespaces里将p勾上。然后在bean标签里面写就行了,注意中间要加上空格。此时直接编写配置文件是没有提示的,当在实体类中加上setter方法,就有提示了,所以p命名空间本质上也是用的setter注入

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

    <!-- 将老师信息注入到IOC容器 -->
    <bean id="teacher" class="com.lee.entity.Teacher" p:name="王五" p:no="3003"></bean>

    <!-- 将课程信息注入到IOC容器 -->
    <bean id="course" class="com.lee.entity.Course" p:courseName="hadoop" p:courseHours="280" p:courseTeacher-ref="teacher"></bean>
</beans>

注意:

没有-ref就是简单注入,p:courseName="hadoop"
-ref就是对象注入,p:courseTeacher-ref="teacher"
多个属性赋值时,中间要加空格
三种注入方式,我们绝大多数用set注入。

2. bean装配概述

bean的装配其实做的只有两件事:为bean赋值、组合bean之间的关系。

在 Spring 中提供了 3 种方法进行装配:

  • 基于xml配置装配bean

  • 基于注解装配bean

  • 自动装配

在现实的工作中,这 3 种方式都会被用到,并且在学习和工作之中常常混合使用,所以这里给出一些关于这 3 种优先级的建议:

① 最优先:自动装配

  • 基于约定优于配置的原则,这种方式应该是最优先的

  • 好处:减少程序开发者的决定权,简单又不失灵活。

② 其次:基于注解装配bean

  • 在没有办法使用自动装配原则的情况下应该优先考虑此类方法

  • 好处:避免 XML 配置的泛滥,也更为容易。

  • 典型场景:一个父类有多个子类,比如学生类有两个子类,一个男学生类和女学生类,通过 IoC 容器初始化一个学生类,容器将无法知道使用哪个子类去初始化,这个时候可以使用 Java 的注解配置去指定。

③ 最后:XML 方式配置

  • 在上述方法都无法使用的情况下,那么也只能选择 XML 配置的方式。

  • 好处:简单易懂

通俗来讲,现在开发中,优先考虑 注解装配 + 自动装配。老项目的维护,可以考虑使用 xml方式 装配。

3. 基于xml的装配

前面所讲到的装配都是基于xml方式的装配,因为基于xml的装配更容易理解。

3.1 简单装配

简单装配就是给一个8个基本数据类型+String的属性赋值,比如:

<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" value="张三"></property>
  <property name="no" value="1001"></property>
</bean>

3.2 集合属性装配

对于一些集合属性的赋值,比如list集合、set集合、map集合、数组、Properties等,我们可以这样来赋值:

第一步:新建实体类

public class CollectionInject {

  private List<String> listElement;
  private Set<String> setElement;
  private String[] strElement;
  private Map<String,String> mapElement;
  private Properties propElement;

  //getter、setter省略

  @Override
  public String toString() {
    return "listElement=" + listElement + "\nsetElement=" + setElement + "\nstrElement="
      + Arrays.toString(strElement) + "\nmapElement=" + mapElement + "\npropElement=" + propElement ;
  }

}

第二步:在 src新建配置文件 applicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 将集合注入到IOC容器 -->
    <bean id="coll" class="com.lee.spring.bean.CollectionInject">
    
        <!-- list集合注入 -->
        <property name="listElement" >
            <list>
                <value>足球</value>
                <value>篮球</value>
                <value>乒乓球</value>
            </list>
        </property>
        
        <!-- set集合注入 -->
        <property name="setElement">
            <set>
                <value>语文</value>
                <value>数学</value>
                <value>英语</value>
            </set>
        </property>
        
        <!-- 数组集合注入 -->
        <property name="strElement">
            <array>
                <value>手机</value>
                <value>电脑</value>
                <value>平板</value>
            </array>
        </property>
        
        <!-- map集合注入 -->
        <property name="mapElement">
            <map>
                <entry>
                    <key>
                        <value>shoes</value>
                    </key>
                    <value>鞋子</value>
                </entry>
                <entry>
                    <key>
                        <value>cap</value>
                    </key>
                    <value>帽子</value>
                </entry>
                <entry>
                    <key>
                        <value>coat</value>
                    </key>
                    <value>外套</value>
                </entry>
            </map>
        </property>
        
        <!-- property注入 -->
        <property name="propElement">
            <props>
                <prop key="bread">面包</prop>
                <prop key="chocolate">巧克力</prop>
                <prop key="milk">牛奶</prop>
            </props>
        </property>
    </bean>

</beans>

记忆技巧:除了properties,所有的集合赋值都在value标签里进行。

第三步:在测试类中测试

@Test
public void test04() {
  //获取上下文对象:Context
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //获取bean对象
  CollectionInject coll = (CollectionInject)context.getBean("coll");
  System.out.println(coll);
}

输出:

listElement=[足球, 篮球, 乒乓球]
setElement=[语文, 数学, 英语]
strElement=[手机, 电脑, 平板]
mapElement={shoes=鞋子, cap=帽子, coat=外套}
propElement={bread=面包, chocolate=巧克力, milk=牛奶}

注意:

  • 其中list、set、array可以混合使用

  • 也可以用构造器赋值,和set注入一样,就是换个标签而已

<constructor-arg name="listElement" >
  <list>
    <value>足球</value>
    <value>篮球</value>
    <value>乒乓球</value>
  </list>
</constructor-arg>

3.3 特殊值装配

如果我们想注入一些特殊值:>、<、&等,可以考虑<value>标签。

value和<value>注入的区别:

<value>标签 value属性
参数位置 写在首尾标签(<value></value>)的中间,不加双引号 写在value的属性值中,必须加双引号
参数值包含特殊符号 方式一:使用<![CDATA[]]>标记
方式二:使用xml预定义的实体引用
使用xml预定义的实体引用
参数值带双引号输出 可以 不可以

其中,xml预定义实体引用:

实体引用 符号
> &gt;
< &lt;
& &amp;

注意有分号。

① 字符串

配置文件:

<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" value="王五"></property>
  <property name="no" value="5005"></property>
</bean>

测试:

@Test
public void test05() {
  //获取上下文对象:Context
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //获取bean对象
  Teacher teacher = (Teacher)context.getBean("teacher");
  System.out.println(teacher);
}

输出:[no=5005, name=王五]

还可以使用<value>写:

<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" ><value>王五</value></property>
  <property name="no" ><value>5005</value></property>
</bean>

我们如果想输出:[no=5005, name="王五"],就必须用<value>标签:

<!-- 将老师信息注入到IOC容器 -->
<bean id="teacher2" class="com.lee.spring.bean.Teacher">
  <property name="name" ><value>"王五"</value></property>
  <property name="no" ><value>5005</value></property>
</bean>

② 特殊字符

a. 使用<value>标签

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name">
    <value><![CDATA[<&>]]></value>
  </property>
  <property name="no" value="1001"></property>
</bean>

或者

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name">
    <value>&gt;&lt;&amp;</value>
  </property>
  <property name="no" value="1001"></property>
</bean>

b. 使用value属性

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" value="&gt;&lt;&amp;"></property>
  <property name="no" value="1001"></property>
</bean>

开发中绝大多数使用value属性的方式注入

③ 赋空值

a. 空字符串""

装配空字符串有两种方式:

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" value=""></property>
</bean>

或者

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" ><value></value></property>
</bean>

b. null

null表示该对象是空的,只能这样做:用<null>标签

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name"> <null/></property>
</bean>

注意:不能用下面方法做

<bean id="teacher" class="com.lee.spring.bean.Teacher">
  <property name="name" value="null"></property>
</bean>

这样表示这个人的名字叫null,而不是空。

4. 基于注解的装配

基于注解的装配就是不使用xml配置文件,使用注解来进行装配。

🌰用三层结构中的Service层来模拟一下注解装配。

用配置文件的方法需要这样做:

StudentService.java

package com.lee.spring.service;

public class StudentService {
    public void addStudent() {
        System.out.println("增加学生。。。");
    }
}

applicationContext.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: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="studentService" class="com.lee.spring.service.StudentService"></bean>
</beans>

此时,StudentService类就已经纳入到IOC容器中了,可以通过eclipse左边StudentService类旁边的S来确定是不是纳入成功。

4.1 用注解装配步骤

首先需要在配置文件中打开包扫描功能:

<?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:p="http://www.springframework.org/schema/p"
    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-4.3.xsd">
    
  <!--开启包扫描-->
    <context:component-scan base-package="com.lee.spring.service"></context:component-scan>
  
</beans>

然后在StudentService类头上加相关注解即可:

package com.lee.spring.service;

import org.springframework.stereotype.Component;

@Component("studentService")
public class StudentService {
    public void addStudent() {
        System.out.println("增加学生。。。");
    }
}

说明

第一点:

@Component("studentService")
就等价于<bean id="studentService" class="com.lee.spring.service.StudentService"></bean>
注解括号里面的值就是bean的id值。

第二点:

在Spring启动的时候,会根据<context:component-scan base-package="com.lee.spring.service"></context:component-scan>
中的base-package去扫描这个包下的所有类,如果发现该包下有哪个类存在组件相关的注解,就将他纳入到IOC容器中。

4.2 其他注解

开发中会用到下面四个注解:

@Component:可以在三层中的任何一层中使用,还有一些功能性的bean使用。

@Controller:控制器层使用。

@Service:service层使用。

@Repository:dao层使用。

下面演示一个用@Component注解给普通的bean赋值:

第一步:直接在实体类上标注 @Component("p") 注解,p是bean的名字。

第二步:用@Value("小花")注解直接在属性上赋值,不需要getter、setter方法。

  • 实体类 Person.java:
@Component("p")
public class Person {

    @Value("1001")
    private int id;
    @Value("小花")
    private String name;
    
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + "]";
    }
}
  • 配置文件 applicationContext.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:p="http://www.springframework.org/schema/p"
    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-4.3.xsd">
    
  <!--开启包扫描-->
    <context:component-scan base-package="com.lee.spring.bean"></context:component-scan>
  
</beans>
  • 测试
@Test
public void test08() {
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  Person p = (Person)context.getBean("p");
  System.out.println(p);
}

输出:Person [id=1001, name=小花]

5. 自动装配

开发中有一个原则:约定优于配置。所以能够按照约定自动去装配的尽量自动装配。

但是需要注意:自动装配仅适用于对象类型,对于简单类型不适用

自动装配分为两种,一种是基于xml方式的自动装配,一种是基于注解方式的自动装配。基于注解方式的自动装配是比较常用的。

5.1 基于xml的自动装配

自动装配分为三种,分别是 通过名字装配、通过类型装配、使用构造器装配。

① byName

  • 新建实体类:

Teacher.java

public class Teacher {

  private int no;
  private String teaName;

  //getter、setter省略
  @Override
  public String toString() {
    return "Teacher [no=" + no + ", teaName=" + teaName + "]";
  }
}

Course.java

public class Course {

    private String courseName;//课程名字
    private int courseHours;//课时量
    private Teacher courseTeacher;//授课老师
    
    //getter、setter省略
    @Override
    public String toString() {
        return "[课程名字:" + courseName + ", 课时量:" + courseHours + ", 老师信息:" + courseTeacher + "]";
    }
    
}
  • 编写配置文件applicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 将老师信息注入到IOC容器 -->
    <bean id="teacher" class="com.lee.entity.Teacher">
        <property name="teaName" value="张三"></property>
        <property name="no" value="1001"></property>
    </bean>

    <!-- 将课程信息注入到IOC容器 -->
    <bean id="course" class="com.lee.entity.Course">
        <property name="courseName" value="语文"></property>
        <property name="courseHours" value="200"></property>
        <property name="courseTeacher" ref="teacher"></property>
    </bean>

</beans>

以上的装配是手工装配的。

自动装配就是我不写<property name="courseTeacher" ref="teacher"></property>这行代码,想办法让程序自动在IOC容器中去寻找teacher这个bean,答案是可以的。删掉这行代码,然后在course的标签中加一句 autowire="byName" 。

运行程序,发现老师信息是null,说明没有程序没有找到对应的teacher的bean。既然是按照名字自动装配,所以得保证属性的名字和bean的名字一致

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 将老师信息注入到IOC容器 -->
    <bean id="courseTeacher" class="com.lee.entity.Teacher">
        <property name="teaName" value="张三"></property>
        <property name="no" value="1001"></property>
    </bean>

    <!-- 将课程信息注入到IOC容器 -->
    <bean id="course" class="com.lee.entity.Course" autowire="byName">
        <property name="courseName" value="语文"></property>
        <property name="courseHours" value="200"></property>
    </bean>

</beans>
  • 测试
@Test
public void testAutowire() {
  //获取上下文对象:Context
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //获取bean对象
  Course course = (Course)context.getBean("course");
  System.out.println(course);

}

按照名字装配,必须保证属性名和被装配bean的id名一致。

byName装配

② byType

byType自然就是按照类型装配。

在被装配的bean中加入autowire="byType"

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

   <!-- 将老师信息注入到IOC容器 -->
   <bean  class="com.lee.entity.Teacher">
       <property name="teaName" value="张三"></property>
       <property name="no" value="1001"></property>
   </bean>

   <!-- 将课程信息注入到IOC容器 -->
   <bean id="course" class="com.lee.entity.Course" autowire="byType">
       <property name="courseName" value="语文"></property>
       <property name="courseHours" value="200"></property>
   </bean>

</beans>

如果IOC容器中有一个bean的类型恰好是该类的引用类型,则可以按照类型自动装配。

byType装配

弊端:

如果IOC容器中有两个类型一样的bean,则程序不知道该装配哪一个,就会装配失败。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 将老师信息注入到IOC容器 -->
    <bean  class="com.lee.entity.Teacher">
        <property name="teaName" value="张三"></property>
        <property name="no" value="1001"></property>
    </bean>

    <!-- 将老师2信息注入到IOC容器 -->
    <bean  class="com.lee.entity.Teacher">
        <property name="teaName" value="李四"></property>
        <property name="no" value="2002"></property>
    </bean>
    
    <!-- 将课程信息注入到IOC容器 -->
    <bean id="course" class="com.lee.entity.Course" autowire="byType">
        <property name="courseName" value="语文"></property>
        <property name="courseHours" value="200"></property>
    </bean>

</beans>

会抛这样一个异常:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lee.entity.Teacher' available: expected single matching bean but found 2: com.lee.entity.Teacher#0,com.lee.entity.Teacher#1

③ constructor

根据构造器去自动装配。所以实体类要有构造方法。
配置文件: autowire="constructor"

<?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="teacher" class="com.lee.spring.bean.Teacher">
      <constructor-arg name="no" value="1001"></constructor-arg>
      <constructor-arg name="name" value="张晓三"></constructor-arg>
  </bean>
  
  <bean id="course" class="com.lee.spring.bean.Course" autowire="constructor">
      <constructor-arg name="courseName" value="Spring"></constructor-arg>
      <constructor-arg name="courseHours" value="100"></constructor-arg>
  </bean>
</beans>

注意:同样IOC容器中不能有两个teacher类型的bean,否则装配失败,并且抛异常。

全局自动装配

全局自动装配使用:default-autowire

可以给每一个类进行自动装配,也能给全局bean都设置自动装配。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-autowire="byName"
    >

    <!-- 将老师信息注入到IOC容器 -->
    <bean  id ="courseTeacher" class="com.lee.entity.Teacher">
        <property name="teaName" value="张三"></property>
        <property name="no" value="1001"></property>
    </bean>
    
    <!-- 将课程信息注入到IOC容器 -->
    <bean id="course" class="com.lee.entity.Course" >
        <property name="courseName" value="语文"></property>
        <property name="courseHours" value="200"></property>
    </bean>

</beans>

注意:

局部bean的自动装配可以覆盖全局bean自动装配。

5.2 基于注解的自动装配

基于注解的自动装配有两种,分别是 通过类型自动装配 和 通过名字自动装配。

基于注解的装配在开发中是最常用的,一般三层架构的装配都是使用注解自动装配。

① @Autowired

@Autowired默认根据类型进行装配。和 byType是一样的。

用配置文件方式模拟一个开发中的三层架构:

Dao层:

package com.lee.spring.dao;

public interface StudentDao {
    void addStudent();
}
package com.lee.spring.dao.impl;

public class StudentDaoImpl implements StudentDao {

    @Override
    public void addStudent() {
        System.out.println("添加学生...");
    }
}

Service层:

package com.lee.spring.service;

public interface StudentService {
    void addStudent();
}
package com.lee.spring.service.impl;

public class StudentServiceImpl implements StudentService{
  
  StudentDao studentDao;
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }
  
    @Override
    public void addStudent() {
        studentDao.addStudent();
    }
}

Controller层:

package com.lee.spring.controller;

public class AddStudentController {

     public void setStudentService(StudentService studentService) {
            this.studentService = studentService;
          }

          StudentService studentService;

          public void addStudent() {
            studentService.addStudent();
          }
}

配置文件:

<?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:p="http://www.springframework.org/schema/p"
    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-4.3.xsd">
    
        <!-- 将控制器纳入到IOC容器 -->
    <bean id="addStudentController" class="com.lee.spring.controller.AddStudentController">
        <property name="studentService" ref="studentService"></property>
    </bean> 
    
    <!-- 将studentService纳入到IOC容器 -->
    <bean id="studentService" class="com.lee.spring.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"></property>
    </bean>
    
    <!-- 将studentDao纳入到IOC容器 -->
    <bean id="studentDao" class="com.lee.spring.dao.impl.StudentDaoImpl"></bean>

</beans>

测试:

@Test
public void test08() {
  //获取上下文对象:Context
  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  //获取bean对象
  AddStudentController addStudentController = (AddStudentController)context.getBean("addStudentController");
  addStudentController.addStudent();
}

用注解方式实现:

Dao层:

package com.lee.spring.dao;

public interface StudentDao {
    void addStudent();
}
package com.lee.spring.dao.impl;

@Repository
public class StudentDaoImpl implements StudentDao  {
    @Override
    public void addStudent() {
        System.out.println("添加学生...");
    }
}

Service层:

package com.lee.spring.service;

public interface StudentService {
    void addStudent();
}
package com.lee.spring.service.impl;

@Service
public class StudentServiceImpl implements StudentService{
    
    @Autowired
    StudentDao studentDao;
  
    @Override
    public void addStudent() {
        studentDao.addStudent();
    }
}

Controller层:

package com.lee.spring.controller;

@Controller
public class AddStudentController {

    @Autowired
    StudentService studentService;

    public void addStudent() {
        studentService.addStudent();
    }
}

配置文件:

<?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:p="http://www.springframework.org/schema/p"
    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-4.3.xsd">
    
    <context:component-scan base-package="com.lee"></context:component-scan>

</beans>

如果IOC容器没有StudentDao这个bean,StudentService层源码是这样:

package com.lee.spring.service.impl;

@Service
public class StudentServiceImpl implements StudentService{
    
    @Autowired
    StudentDao studentDao;
  
    @Override
    public void addStudent() {
        studentDao.addStudent();
    }
}

当容器获取stuService组件时,程序会报错,因为stuService依赖StudentDao,但是IOC容器还没有StudentDao,所以报错。怎么让程序不依赖StudentDao:在@AutoWired注解后加:required = false

package com.lee.spring.service.impl;

@Service
public class StudentServiceImpl implements StudentService{
    
    @Autowired(required = false)
    StudentDao studentDao;
  
    @Override
    public void addStudent() {
        studentDao.addStudent();
    }
}

② @Qualifier

当一个接口有多个实现类时,需要用@Autowired + @Qualifier组合来区分到底调用哪个类的实现。

@Controller("addStudentController")
public class AddStudentController {

    @Autowired
    @Qualifier("stuService")
    StudentService studentService;
    public void addStudent() {
        studentService.addStudent();
    }
}

其中@Qualifier("stuService")括号中的stuService就是我们之前定义@Service注解的名称。

注意:@Qualifier必须和@Autowired一起使用才有效。

③ @Resource

@Resource注解是通过byName注入的。

@Controller("addStudentController")
public class AddStudentController {

    @Resource(name = "stuService")
    StudentService studentService;
  
    public void addStudent() {
        studentService.addStudent();
    }
}

④ @Resource和@Autowired区别

① @Autowired为Spring提供的注解。@Resource并不是Spring的注解,它的包是javax.annotation.Resource

② @Autowired按照byType注入,@Resource默认按照ByName自动注入。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容