likes
comments
collection
share

Spring Bean属性注入的 N 种方式

作者站长头像
站长
· 阅读数 43

日积月累,水滴石穿 😄

前言

Bean 属性注入,就是将属性注入到 Bean 中的过程,而这属性可以普通属性,也可以是一个对象(Bean)。

Spring 实现属性注入的方式有两种:

  • 构造函数注入
  • set方法注入

但是具体的操作方式也分为两种:

  • 基于 xml 配置文件方式实现
  • 基于注解方式实现

我们先来使用 xml 配置文件操作 set方法注入、构造函数注入属性,

加入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.17</version>
</dependency>

<!--用于测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>RELEASE</version>
    <scope>test</scope>
</dependency>

加入 context 依赖,一个依赖就包含了 aop、beans、core、expression。

Spring Bean属性注入的 N 种方式

xml 配置文件

构造函数注入

1、 创建一个名为 User2 的类,定义属性和对应的有参构造方法,代码如下。

public class User2 {

//普通属性
    private String name;

    private String address;

//对象属性
    private Card card;
    
    public User2(String name, String address, Card card) {
        this.name = name;
        this.address = address;
        this.card = card;
    }

    @Override
    public String toString() {
        return "User2{" +
                "name='" + name + ''' +
                ", address='" + address + ''' +
                ", card=" + card +
                '}';
    }
}

  • 2、再创建一个名为 Card 类的对象,定义属性和对应的有参构造方法,代码如下。
public class Card {

    private String cardId;

    private String cardName;

    public Card(String cardId, String cardName) {
        this.cardId = cardId;
        this.cardName = cardName;
    }

@Override
public String toString() {
    return "Card{" +
            "cardId='" + cardId + ''' +
            ", cardName='" + cardName + ''' +
            '}';
}

}

  • 3、在 resources 目录下创建 Spring 配置文件 IOC02.xml,配置如下。
<bean id="user02" class="com.cxyxj.dto.User2">
    <constructor-arg name="name" value="cxyxj"></constructor-arg>
    <constructor-arg name="address" value="shs"></constructor-arg>
    <constructor-arg name="card" ref="card"></constructor-arg>
</bean>

<bean id="card" class="com.cxyxj.dto.Card">
    <constructor-arg index="0" value="1222"></constructor-arg>
    <constructor-arg index="1" value="zsyh"></constructor-arg>
</bean>

上面使用 constructor-arg 标签完成了属性的注入。 name:类的属性名称, value:需要向属性注入的值,index:参数所属下标,ref:创建 card 对象 bean 标签的 id 值。Bean 的构造函数内有多少参数,就需要使用多少个 元素。

  • 测试。
@Test
public void test2(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC02.xml");
    // 获取配置创建的对象
    User2 user = context.getBean("user02", User2.class);
    System.out.println(user);
 
}
结果如下:
User2{name='cxyxj', address='shs', card=Card{cardId='1222', cardName='zsyh'}}

set方法注入

在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用无参构造方法,实例化 Java 对象,然后通过 反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。

  • 1、修改 User2 类的代码,代码如下:
public class User2 {

    private String name;

    private String address;

    private Card card;

    public User2() {
        System.out.println("User2 构造方法执行");
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setCard(Card card) {
        this.card = card;
    }

    @Override
    public String toString() {
        return "User2{" +
                "name='" + name + ''' +
                ", address='" + address + ''' +
                ", card=" + card +
                '}';
    }
}

  • 2、修改 Card 类的代码,代码如下:
public class Card {

    private String cardId;

    private String cardName;

    public Card() {
        System.out.println("Card 构造方法执行");
    }


public void setCardId(String cardId) {
    this.cardId = cardId;
}

public void setCardName(String cardName) {
    this.cardName = cardName;
}

    @Override
    public String toString() {
        return "Card{" +
                "cardId='" + cardId + ''' +
                ", cardName='" + cardName + ''' +
                '}';
    }
}

  • 修改 spring 配置文件,代码如下:
<bean id="user02" class="com.cxyxj.dto.User2">
     <property name="name" value="cxyxj"></property>
     <property name="address" value="shs"></property>
    <property name="card" ref="card"></property>
 </bean>

 <bean id="card" class="com.cxyxj.dto.Card">
     <property name="cardId" value="2222"></property>
     <property name="cardName" value="zsyh"></property>
 </bean>

上面使用 property 标签完成了属性的注入。 name:类的属性名称, value:需要向属性注入的值,ref:创建 card 对象 bean 标签的 id 值。

  • 测试。
@Test
public void test2(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC02.xml");
    // 获取配置创建的对象
    User2 user = context.getBean("user02", User2.class);
    System.out.println(user);
    user.test2();
}
结果如下:
User2 构造方法执行
Card 构造方法执行
User2{name='cxyxj', address='shs', card=Card{cardId='2222', cardName='zsyh'}}

短命名空间注入

我们在通过构造函数或 set 方法进行属性注入时,通常是在 元素中嵌套 和 元素来实现的。这种方式虽然结构清晰,但书写较繁琐。

Spring 框架提供了 2 种短命名空间,可以简化基于 xml 配置方式,如下。

p 命名空间注入

p 命名空间注入,可以简化 set 方法方式属性注入。

  • 1、添加 p 命名空间XML 约束
xmlns:p="http://www.springframework.org/schema/p"
  • 2、导入之后,进行属性注入,在 bean 标签里面进行操作。
<bean id="user02" class="com.cxyxj.dto.User2" p:name="cxyxj" p:address="shs" p:card-ref="card">
</bean>

<bean id="card" class="com.cxyxj.dto.Card" p:cardId="3333" p:cardName="zsyh">
</bean>

p:普通属性="普通属性值",p:对象属性-ref="对象的引用"。

  • 测试
@Test
public void test2(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC02.xml");
    // 获取配置创建的对象
    User2 user = context.getBean("user02", User2.class);
    System.out.println(user);
    user.test2();
}
结果如下:
User2 构造方法执行
Card 构造方法执行
User2{name='cxyxj', address='shs', card=Card{cardId='3333', cardName='zsyh'}}

c 命名空间注入

c 命名空间注入,可以简化构造方法方式属性注入。

将代码还原成构造函数注入时的代码,其实就是需要构造方法。

  • 1、添加 c 命名空间XML 约束
xmlns:c="http://www.springframework.org/schema/c"
  • 2、导入之后,进行属性注入,在 bean 标签里面进行操作。
<bean id="user02" class="com.cxyxj.dto.User2" c:name="cxyxj" c:address="shs" c:card-ref="card">
</bean>

<bean id="card" class="com.cxyxj.dto.Card" c:_0="4444" c:_1="zsyh">

c:普通属性="普通属性值",c:对象属性-ref="对象的引用",c:_下标值="普通属性值", c:_下标值-ref="对象的引用"

定义内部 Bean

上述例子创建的Bean是外部Bean,那如何创建内部Bean呢?我们将定义在 元素的 或 元素内部的 Bean,称为内部 Bean。这里就拿 标签举例。

  • 1、对象复用之前的,修改配置文件内容,如下
<bean id="user02" class="com.cxyxj.dto.User2">
    <property name="name" value="cxyxj"></property>
    <property name="address" value="shs"></property>
    <property name="card">
        <bean class="com.cxyxj.dto.Card">
            <property name="cardId" value="5555"></property>
            <property name="cardName" value="内部Bean"></property>
        </bean>
    </property>
</bean>

注意:内部 Bean 是匿名的,不需要指定 id,即使指定也不会被Spring 容器作为Bean的唯一标识。内部Bean随着外部 Bean 创建而创建,内部 Bean 无法被注入到它所在的 Bean 以外的其他 Bean 中。

  • 测试
@Test
public void test2(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC02.xml");
    // 获取配置创建的对象
    User2 user = context.getBean("user02", User2.class);
    System.out.println(user);
    user.test2();
}
结果如下:
User2 构造方法执行
Card 构造方法执行
User2{name='cxyxj', address='shs', card=Card{cardId='5555', cardName='内部Bean'}}

其他类型的注入

上面演示了普通类型和对象类型的注入,但是还有其他类型也是常用的。比如数组、List、Map、Set。

  • 1、创建名为 Demo 的类,定义数组、list、map、set 类型属性并生成对应 的set 方法。
public class Demo {

    // 数组
    private String[] courses;
    //list 集合
    private List<String> list;
    // map 集合
    private Map<String, String> maps;
    // set 集合
    private Set<String> sets;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    @Override
    public String toString() {
        return "Demo{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", maps=" + maps +
                ", sets=" + sets +
                '}';
    }
}
  • 2、创建 IOC03.xml 配置文件并进行配置
<bean id="demo" class="com.cxyxj.dto.Demo">
    <!--数组类型属性注入-->
    <property name="courses">
       <array>
           <value>courses-1</value>
           <value>courses-2</value>
       </array>
    </property>
    <property name="list">
        <list>
            <ref bean="card"></ref>
            <ref bean="card"></ref>
        </list>
    </property>
    <property name="maps">
        <map>
            <entry key="maps-1" value-ref="card"></entry>
            <entry key="maps-2" value-ref="card"></entry>
        </map>
    </property>
    <property name="sets">
        <set>
            <value>sets-1</value>
            <value>sets-2</value>
        </set>
    </property>
</bean>
<bean id="card" class="com.cxyxj.dto.Card">
    <property name="cardId" value="2222"></property>
    <property name="cardName" value="zsyh"></property>
</bean>
  • 3、测试
@Test
public void test3(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC03.xml");
    // 获取配置创建的对象
    Demo info = context.getBean("demo", Demo.class);
    System.out.println(info);
}
结果如下:
Card 构造方法执行
Demo{courses=[courses-1, courses-2], list=[Card{cardId='2222', cardName='zsyh'}, Card{cardId='2222', cardName='zsyh'}], maps={maps-1=Card{cardId='2222', cardName='zsyh'}, maps-2=Card{cardId='2222', cardName='zsyh'}}, sets=[sets-1, sets-2]}

写法并没有很大的出入,对应的类型会有对应的标签。

公用配置抽取

  • 1、引入命名空间 util
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd">
  • 2、拿 list 举例,对其进行抽取
<util:list id="list">
    <ref bean="card"></ref>
    <ref bean="card"></ref>
</util:list>
  • 3、进行引用,使用 ref 指向 util:list 标签的id。
<property name="list" ref="list"></property>
  • 测试
@Test
public void test3(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC03.xml");
    // 获取配置创建的对象
    Demo info = context.getBean("demo", Demo.class);
    System.out.println(info);
}
结果如下:
Card 构造方法执行
Demo{courses=[courses-1, courses-2], list=[Card{cardId='2222', cardName='zsyh'}, Card{cardId='2222', cardName='zsyh'}], maps={maps-1=Card{cardId='2222', cardName='zsyh'}, maps-2=Card{cardId='2222', cardName='zsyh'}}, sets=[sets-1, sets-2]}

xml自动装配

前面在进行Bean 与 Bean之间属性注入的时候,我们需要在配置文件中使用 property 标签或者 constructor-arg 中的 ref 属性手动维护依赖关系才能正确的进行属性注入。随着容器中 Bean 的增多,我们的XML 配置也越来越复杂,越与难以维护。为了解决这一问题,Spring 框架为我们提供了自动装配功能。

什么是自动装配

根据指定的装配规则(一共有五种),Spring 将自动匹配,然后将属性值进行注入。

自动装配规则

属性值说明
byName按名称自动装配。 Spring 会根据的 Java 类中对象属性的名称,在整个IoC 容器中查找。如果某个 Bean 的 id 或 name 属性值与这个对象属性的名称相同,则获取这个 Bean,赋值给当前的 Java 类中对象属性。
byType按类型自动装配。 Spring 会根据 Java 类中的对象属性的类型,在整个IoC 容器中查找。若某个 Bean 的 class 属性值与这个对象属性的类型相匹配,则获取这个 Bean,赋值给当前的 Java 类中对象属性。
constructor类似于构造函数参数的“byType”,如果在容器中没有找到与构造器参数类型一致的 Bean,那么将抛出异常。
default表示默认采用上一级元素 设置的自动装配规则(default-autowire)进行装配。
no默认值,表示不使用自动装配,Bean 引用必须通过 <constructor-arg><property> 元素的 ref 属性来定义。

接下来分别介绍每种装配规则的使用方式。

使用

准备工作

  • 1、创建名为 Order 的类,定义属性并生成对应的 set 方法、构造方法。
public class Order {

    private String name;

    private String address;

    private Product product;

    public Order(String name, String address, Product product) {
        System.out.println("Order 有参构造方法执行");
        this.name = name;
        this.address = address;
        this.product = product;
    }

    public Order() {
        System.out.println("Order 无参构造方法执行");
    }

    public void setName(String name) {
        System.out.println("setName 方法执行");
        this.name = name;
    }

    public void setAddress(String address) {
        System.out.println("setAddress 方法执行");
        this.address = address;
    }

    public void setProduct(Product product) {
        System.out.println("setProduct 方法执行");
        this.product = product;
    }

    @Override
    public String toString() {
        return "Order{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", product=" + product +
                '}';
    }
}
  • 2、创建名为 Product 的类,定义属性并生成对应的 set 方法、构造方法。
public class Product {

    private String productId;

    private String productName;

    public Product() {
        System.out.println("Product 无参构造方法执行");
    }

    public Product(String productId, String productName) {
        System.out.println("Product 有参构造方法执行 ===》");
        this.productId = productId;
        this.productName = productName;
    }

    public void setProductId(String productId) {
        System.out.println("setProductId 方法执行 ===》");
        this.productId = productId;
    }

    public void setProductName(String productName) {
        System.out.println("setProductName 方法执行 ===》");
        this.productName = productName;
    }

    @Override
    public String toString() {
        return "Product{" +
                "productId='" + productId + '\'' +
                ", productName='" + productName + '\'' +
                '}';
    }
}
  • 3、编写测试方法逻辑
@Test
public void test4(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC04.xml");
    // 获取配置创建的对象
    Order info = context.getBean("order", Order.class);
    System.out.println(info);
}

no规则使用

默认值,表示不使用自动装配。配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="no">
    <property name="name" value="生发水订单"></property>
    <property name="address" value="shs"></property>
    <property name="product" ref="product"></property>
</bean>

<bean id="product" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

执行 test4() 方法,控制台输出如下:

Order 无参构造方法执行
Product 无参构造方法执行
setProductId 方法执行 ===》
setProductName 方法执行 ===》
setName 方法执行
setAddress 方法执行
setProduct 方法执行
Order{name='生发水订单', address='shs', product=Product{productId='123456', productName='生发水'}}

byName

根据属性名称自动注入,XML 文件中 Bean 的 id 或 name 必须与类中的属性名称相同。配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="byName">
    <property name="name" value="生发水订单"></property>
    <property name="address" value="shs"></property>
  <!--  <property name="product" ref="product"></property>-->
</bean>

<bean id="product" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

执行 test4() 方法,控制台输出如下:

Order 无参构造方法执行
Product 无参构造方法执行
setProductId 方法执行 ===》
setProductName 方法执行 ===》
setName 方法执行
setAddress 方法执行
setProduct 方法执行
Order{name='生发水订单', address='shs', product=Product{productId='123456', productName='生发水'}}

测试一下 Bean的id与类中的属性名称不一致的情况,将 product 修改为 product1,配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="byName">
    <property name="name" value="生发水订单"></property>
    <property name="address" value="shs"></property>
  <!--  <property name="product" ref="product"></property>-->
</bean>

<bean id="product1" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

执行 test4() 方法,控制台输出如下:

Order 无参构造方法执行
setName 方法执行
setAddress 方法执行
Product 无参构造方法执行
setProductId 方法执行 ===》
setProductName 方法执行 ===》
Order{name='生发水订单', address='shs', product=null}

byType

据属性类型注入,即使 XML 文件中 Bean 的 id 或 name 与类中的属性名不同,只要 Bean 的 class 属性值与类中的对象属性的类型相同,就可以完成自动装配。配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="byType">
    <property name="name" value="生发水订单"></property>
    <property name="address" value="shs"></property>
  <!--  <property name="product" ref="product"></property>-->
</bean>

<bean id="product1" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

执行 test4() 方法,控制台输出如下:

Order 无参构造方法执行
Product 无参构造方法执行
setProductId 方法执行 ===》
setProductName 方法执行 ===》
setName 方法执行
setAddress 方法执行
setProduct 方法执行
Order{name='生发水订单', address='shs', product=Product{productId='123456', productName='生发水'}}

但如果同时存在多个相同类型的 Bean,则会自动注入失败,并且抛出异常。配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="byType">
    <property name="name" value="生发水订单"></property>
    <property name="address" value="shs"></property>
  <!--  <property name="product" ref="product"></property>-->
</bean>

<bean id="product1" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

<bean id="product2" class="com.cxyxj.dto.autowire.Product">
    <property name="productId" value="123456"></property>
    <property name="productName" value="生发水"></property>
</bean>

执行 test4() 方法,控制台输出如下:

Spring Bean属性注入的 N 种方式

org.springframework.beans.factory.UnsatisfiedDependencyException: Error 
creating bean with name 'order' defined in class path resource 
[IOC04.xml]: Unsatisfied dependency expressed through bean property 
'product'; nested exception is 
org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type 'com.cxyxj.dto.autowire.Product' available: 
expected single matching bean but found 2: product1,product2

根据类型找到了多个Bean,分别是 product1、product2。

constructor

根据构造函数进行自动装配。property 标签 修改为 constructor-arg 标签。配置文件内容如下:

<bean id="order" class="com.cxyxj.dto.autowire.Order" autowire="constructor">
    <constructor-arg name="name" value="生发水订单"></constructor-arg>
    <constructor-arg name="address" value="shs"></constructor-arg>
</bean>

<bean id="product1" class="com.cxyxj.dto.autowire.Product">
    <constructor-arg name="productId" value="123456"></constructor-arg>
    <constructor-arg name="productName" value="生发水"></constructor-arg>
</bean>

执行 test4() 方法,控制台输出如下:

Product 有参构造方法执行 ===》
Order 有参构造方法执行
Order{name='生发水订单', address='shs', product=Product{productId='123456', productName='生发水'}}

default

采用上一级标签 设置的装配规则(default-autowire)进行装配,配置文件内容如下:

<?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"
default-autowire="constructor">


    <bean id="order" class="com.cxyxj.dto.autowire.Order">
        <constructor-arg name="name" value="生发水订单"></constructor-arg>
        <constructor-arg name="address" value="shs"></constructor-arg>
    </bean>

    <bean id="product1" class="com.cxyxj.dto.autowire.Product">
        <constructor-arg name="productId" value="123456"></constructor-arg>
        <constructor-arg name="productName" value="生发水"></constructor-arg>
    </bean>
</beans>

执行 test4() 方法,控制台输出如下:

Product 有参构造方法执行 ===》
Order 有参构造方法执行
Order{name='生发水订单', address='shs', product=Product{productId='123456', productName='生发水'}}

注解方式

我们可以使用注解来实现自动装配,再次简化 Spring 的 XML 配置。

定义 Bean 的注解

Spring 对定义 Bean 提供了四个注解,如下:

注解说明
@Component该注解可以作用在应用的任何层次,例如 Service 层、Dao 层等。将标注的类注册为 Spring 中的 Bean。
@Repository该注解用于数据访问层(Dao 层),将标注的类注册为 Spring 中的 Bean。
@Service该注解用于业务层(Service 层),将标注的类注册为 Spring 中的 Bean。
@Controller该注解用在于控制层(web层),将标注的类注册为 Spring 中的 Bean。

上面四个注解功能是一样的,都可以直接标注在 Java 类上,使它们成为 Spring Bean 实例

实现属性注入的注解

可以通过以下注解将定义好 Bean 装配到其它的 Bean 中。

注解说明
@Autowired可以在 Bean 的属性变量、setter 方法、非 setter 方法及构造函数上使用,默认按照 Bean 的类型进行装配,如果匹配到多个类型则名称再次匹配。如果想指定名称(byName)来装配,可以结合 @Qualifier 注解一起使用。
@Qualifier与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
@Resource是javax 的注解,作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 的名称进行装配。 @Resource 中有两个重要属性:name 和 type。Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配;如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。

引入依赖

我们导入的依赖中已经包含了注解的运行环境(spring-aop),所以不需要再次进行导入。

开启组件扫描

Spring 默认是不使用注解进行装配Bean的,因此我们需要在 Spring 的 XML 配置文件中,通过 <context:component-scan> 标签开启 Spring 的组件扫描功能。开启后,Spring 会自动扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

  • 1、加入 context 命名空间
<?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">

</beans>
  • 2、开启组件扫描
<!--开启组件扫描-->
<context:component-scan base-package="com.cxyxj"></context:component-scan>

如果需要扫描多个包,多个包使用逗号隔开

注入方式

@Autowired

  • 3、在项目中创建新包 dao,在其中创建名为 UserService 的类。
package com.cxyxj.dao;

public interface UserDao {

    void insertUser();
}

创建名为 UserDaoImpl 的子类,如下:

package com.cxyxj.dao;

import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

@Repository(value = "userDaoImpl")
public class UserDaoImpl implements UserDao {

    @Override
    public void insertUser() {
        System.out.println("UserDaoImpl insertUser 执行 ====》");
    }
}

UserDaoImpl 类上被 @Repository 标注。相当于 xml 配置 <bean id="userDaoImpl"class="com.cxyxj.dao.UserDaoImpl">。 注解里面 value 属性值可以省略不写,默认值是类名称首字母小写。

  • 4、在项目中创建新包 service,在其中创建名为 UserService 的类。
package com.cxyxj.service;

import com.cxyxj.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    @Autowired
    private UserDao userDao;
    
    public void testInsert(){
        userDao.insertUser();
    }
}

UserService 类上被 @Service 标注,其中 UserDao 属性被 @Autowired 标注。

  • 5、编写测试方法逻辑并执行
@Test
public void test5(){
    // 加载 spring 配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("IOC05.xml");
    // 获取配置创建的对象
    UserService info = context.getBean("userService", UserService.class);
    info.testInsert();
}
结果如下:
UserDaoImpl insertUser 执行 ====》

多个类型注入

给 UserDao 新增一个子类 UserDaoImpl2。

package com.cxyxj.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl2 implements UserDao{
    @Override
    public void insertUser() {
        System.out.println("UserDaoImpl2 insertUser 执行 ====》");
    }
}

运行test5方法,会抛出异常。根据类型找到了多个Bean,分别是 userDaoImpl,userDaoImpl2。上面说过如果匹配到多个类型则名称再次匹配,所以我们修改代码如下:

@Service
public class UserService {

    @Autowired
    private UserDao userDaoImpl2;

    public void testInsert(){
        userDaoImpl2.insertUser();
    }
}

执行 test5() 方法,控制台输出如下:

UserDaoImpl2 insertUser 执行 ====》

@Qualifier

也可以由 Qualifier 注解指定 bean 的名称进行注入。代码如下:

@Service
public class UserService {

    @Autowired
    @Qualifier(value = "userDaoImpl")
    private UserDao userDao;

    public void testInsert(){
        userDao.insertUser();
    }
}

执行 test5() 方法,控制台输出如下:

UserDaoImpl insertUser 执行 ====》

@Resource

@Service
public class UserService {

    @Resource(name = "userDaoImpl2")
    private UserDao userDao;

    public void testInsert(){
        userDao.insertUser();
    }
}

执行 test5() 方法,控制台输出如下:

UserDaoImpl2 insertUser 执行 ====》

纯注解开发

  • 1、创建类,替代 xml 配置文件
@ComponentScan(basePackages = "com.cxyxj")
public class SpringMain {
}
  • 2、使用 AnnotationConfigApplicationContext 容器加载
@Test
public void test6(){
    // 加载 spring 配置文件
    AnnotationConfigApplicationContext context = 
        new AnnotationConfigApplicationContext(SpringMain.class);
    // 获取配置创建的对象
    UserService info = context.getBean("userService", UserService.class);
    info.testInsert();
}
结果如下:
UserDaoImpl2 insertUser 执行 ====》

  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。
转载自:https://juejin.cn/post/7087819601651695653
评论
请登录