Bootstrap

set注入之专题(spring)

依赖注入的本质

依赖注入是为了让类(对象)获得它所需要的其他类(对象)。这些依赖关系由Spring容器来处理,实现对象之间的解耦。

依赖注入 ≠ 简单的赋值

虽然依赖注入最终的效果是给对象的属性赋值,但注入不仅仅是简单的赋值,而是一种设计模式。更重要的是,它让对象之间产生关系,并且这种关系由Spring容器来管理。开发者只需声明依赖,不需要手动创建或管理依赖对象。其背后的核心思想是 控制反转(IoC, Inversion of Control),即把控制对象依赖关系的责任交给框架,而不是在代码内部自行创建对象。

外部注入和内部注入

在 Spring 框架中,set 注入用于通过 JavaBean 的 setter 方法注入依赖。Spring 提供了两种类型的 Bean 注入:内部 Bean外部 Bean。它们的区别在于它们如何在 XML 配置文件中定义和使用。

1. 内部 Bean(Inner Bean)

内部 Bean 是在另一个 Bean 的定义中内联定义的 Bean,它只能用于当前的父 Bean,无法在其他地方引用。

特点:
  • 只能在包含它的外部 Bean 中使用。其实就是两个嵌套的Bean。
  • 由于内部 Bean 没有 idname,不能在其他地方引用它
    <bean id="person" class="com.example.Person">
        <property name="address">
            <!-- 内部 Bean 定义 -->
            <bean class="com.example.Address">
                <property name="city" value="New York"/>
                <property name="zipcode" value="10001"/>
            </bean>
        </property>
    </bean>
    

2. 外部 Bean(Outer Bean)

外部 Bean 是在 Spring 容器中全局定义的 Bean,可以在多个地方引用。

特点:
  • 定义在 Spring 配置文件的顶层。
  • 通过 idname 属性,可以在多个 Bean 中共享或引用
    <bean id="address" class="com.example.Address">
        <property name="city" value="New York"/>
        <property name="zipcode" value="10001"/>
    </bean>
    
    <bean id="person" class="com.example.Person">
        <property name="address" ref="address"/>
    </bean>
    

    <property name="address" ref="address"/> 表示 Person 类中的 address 属性将与前面定义的 address Bean 连接(注入)在一起。注意address是一个类。

    具体来说,这个 XML 配置的意思是:

    • property 标签中的 name="address" 指的是 Person 类中的 address 属性(假设该类中存在一个 setAddress(Address address) 的 setter 方法,Spring 会调用这个方法来完成注入)。
    • ref="address" 表示引用前面定义的名为 address 的 Bean,并将其注入到 Person 类中的 address 属性中。

 简单类型的注入:

1. 使用 <property> 标签注入简单类型

使用 Spring 的 XML 配置文件,你可以通过 <property> 标签为 Bean 的属性注入简单类型的值。

示例:注入基本数据类型
<bean id="person" class="com.example.Person">
    <property name="name" value="John Doe"/>
    <property name="age" value="30"/>
    <property name="height" value="180.5"/>
    <property name="isEmployed" value="true"/>
</bean>

对应的 Java 类:

public class Person {
    private String name;
    private int age;
    private double height;
    private boolean isEmployed;

    // setter 和 getter 方法
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public void setIsEmployed(boolean isEmployed) {
        this.isEmployed = isEmployed;
    }

    // getter 方法...
}

在这个示例中:

  • nameString 类型,通过 value="John Doe" 进行注入。
  • ageint 类型,通过 value="30" 进行注入。
  • heightdouble 类型,通过 value="180.5" 进行注入。
  • isEmployedboolean 类型,通过 value="true" 进行注入。

总结

注入简单类型是通过 <property> 标签的 value 属性来完成的,Spring 会自动进行类型转换。你可以注入原始类型(如 intboolean)、包装类(如 IntegerBoolean)、字符串和枚举类型。

简单类型有:

8个基本类型以及对应的包装类型,String类型,数组类型,枚举类型,集合类型(List,set,Map),日期类型(但是在开发中会把他当成非简单类型,因为需要遵循一定的格式,很麻烦),数字(如 IntegerDouble)、URI、URL、Locale 等类型

简单类型注入的经典应用 

运用在数据库连接的配置,方便通过 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    让spring来管理我们的数据源-->
    <bean id="myDataSource" class="spring6.jdbc.myDataSource" >
        <property name="driver" value="om.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/Test"/>
        <property name="username" value="root"/>
        <property name="password" value="gege5211314"/>
    </bean>
</beans>

java示例代码

package spring6.jdbc;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 所有的数据源都要实现java规范:javax.sql.DataSource
 * 什么是数据源:能够给你提供Connection对象的,都是数据源
 */
// 可以将数据源交给spring管理
public class myDataSource implements DataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "myDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

非简单类型

引用其他 Bean(使用 ref

如果要注入的属性是一个对象(而不是简单的字符串、整数等),你可以使用 ref 引用其他已经定义的 Bean。

<bean id="myAddress" class="com.example.Address">
    <property name="city" value="New York"/>
    <property name="zipcode" value="10001"/>
</bean>

<bean id="myPerson" class="com.example.Person">
    <!-- 注入复杂类型的属性 -->
    <property name="address" ref="myAddress"/>
</bean>
说明:
  • 这里 myPerson Bean 的 address 属性是一个 Address 类型,而不是简单的类型。通过 ref 来引用另一个已经定义的 myAddress Bean。
  • ref="myAddress" 表示 Person 对象中的 address 属性使用的是 myAddress 这个对象。

数组类型的引入

java代码:

package spring6.bean;

import java.util.Arrays;

public class LiPing {
    private String[] habits;

    // 多个女性朋友
    private Woman[] women;

    public Woman[] getWomen() {
        return women;
    }

    public void setWomen(Woman[] women) {
        this.women = women;
    }

    public String[] getHabits() {
        return habits;
    }

    public void setHabits(String[] habits) {
        this.habits = habits;
    }

    @Override
    public String toString() {
        return "LiPing{" +
                "habits=" + Arrays.toString(habits) +
                ", women=" + Arrays.toString(women) +
                '}';
    }
}

Women:

package spring6.bean;

public class Woman {
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Woman{" +
                "name='" + name + '\'' +
                '}';
    }
}

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="w1" class="spring6.bean.Woman">
    <property name="name" value="小花"/>
</bean>
    <bean id="w2" class="spring6.bean.Woman">
    <property name="name" value="小明"/>
    </bean>
    <bean id="w3" class="spring6.bean.Woman">
    <property name="name" value="小格"/>
    </bean>
    <bean id="w4" class="spring6.bean.Woman">
    <property name="name" value="小亮"/>
    </bean>

    <bean id="hobby" class="spring6.bean.LiPing">
<!--    数组的元素属性类型都是String简单类型-->
 <property name="habits">
     <array>
         <value>养猫</value>
         <value>给猫做饭</value>
         <value>和猫说话</value>
     </array>
 </property>

<!--数组的类型不是简单类型了-->
<property name="women">
    <array>
        <ref bean="w1"/>
        <ref bean="w2"/>
        <ref bean="w3"/>
        <ref bean="w4"/>
    </array>
</property>
</bean>
</beans>

代码解析:

1. 简单类型的数组注入

在配置 LiPing Bean 时,habits 属性是一个字符串数组,包含了几个爱好。通过 <array> 标签来定义数组,并使用 <value> 标签为数组的每个元素赋值。

<property name="habits">
    <array>
        <value>养猫</value>
        <value>给猫做饭</value>
        <value>和猫说话</value>
    </array>
</property>
2. 复杂类型的数组注入

在同一个 LiPing Bean 中,women 属性是一个 Woman 对象的数组这里使用了 <ref> 标签来引用其他已经定义的 Woman Bean。

<property name="women">
    <array>
        <ref bean="w1"/>
        <ref bean="w2"/>
        <ref bean="w3"/>
        <ref bean="w4"/>
    </array>
</property>
3. Bean 定义

多个 Woman Bean 被定义并设置了 name 属性:

<bean id="w1" class="spring6.bean.Woman">
    <property name="name" value="小花"/>
</bean>
<bean id="w2" class="spring6.bean.Woman">
    <property name="name" value="小明"/>
</bean>
<bean id="w3" class="spring6.bean.Woman">
    <property name="name" value="小格"/>
</bean>
<bean id="w4" class="spring6.bean.Woman">
    <property name="name" value="小亮"/>
</bean>

同样对于以下的集合注入规则也是这样!!

集合

java代码:

package spring6.bean;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class Person {
    private List<String> names;
    private Set<String> address;
    private Map<Integer,String > phones;
    // 注入属性类对象
    //Properties本质上也是一个Map集合
//   Properties的父类Hashtable,Hashtable实现Map接口
//   虽然这个也是一个Map集合,和Map的注入方式有点像,但是不同。
    // properties的key和value只能是String类型
    private Properties properties;

    public void setPhones(Map<Integer, String> phones) {
        this.phones = phones;
    }

    @Override
    public String toString() {
        return "Person{" +
                "address=" + address +
                ", names=" + names +
                ", phones=" + phones +
                ", properties=" + properties +
                '}';
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

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

    public void setNames(List<String> names) {
        this.names = names;
    }

}

1. 注入 List 集合:

<property name="names">
    <list>
        <value>张三</value>
        <value>李四</value>
        <value>王五</value>
        <value>张三</value>
    </list>
</property>
  • List 集合:使用 <list> 标签定义一个有序且可重复的集合。
  • 这里 names 属性是一个 List 类型,包含 4 个值,其中 "张三" 出现了两次,表明 List 允许重复元素,并保持插入的顺序。

2. 注入 Set 集合

<property name="address">
    <set>
        <value>湖南</value>
        <value>广东</value>
        <value>重庆</value>
        <value>北京</value>
    </set>
</property>
  • Set 集合:使用 <set> 标签定义一个无序且不可重复的集合。
  • 这里 address 属性是一个 Set 类型,包含 4 个值。Set 不允许重复元素,并且它是无序的(元素的顺序不保证)。

3. 注入 Map 集合

<property name="phones">
    <map>
        <entry key="1" value="110"/>
        <entry key="2" value="120"/>
        <entry key="3" value="130"/>
        <entry key="4" value="140"/>
    </map>
</property>
  • Map 集合使用 <map> 标签注入键值对,<entry> 标签用于指定键和值。
  • 这里 phones 属性是一个 Map,每个 <entry> 定义一个键值对。键和值都是简单类型(String),分别对应电话的编号和电话号码。
  • 注释提到如果键值对不是简单类型时,使用 key-refvalue-ref 来引用其他 Bean。

4. 注入 Properties 集合 

<property name="properties">
    <props>
        <prop key="driver">spring6.jdbc.myDataSource</prop>
        <prop key="url">om.mysql.cj.jdbc.Driver</prop>
        <prop key="username">root</prop>
        <prop key="password">gege5211314</prop>
    </props>
</property>
  • Properties 集合使用 <props> 标签来定义键值对形式的配置属性,<prop> 标签用于定义键和值。
  • 这里 properties 属性是一个 Properties 类型,包含数据库的连接信息,如驱动、URL、用户名和密码。这是一种常见的用于配置外部属性的方式。

总结:

  1. List 集合:使用 <list> 标签注入有序且可重复的集合,如 names 属性。
  2. Set 集合:使用 <set> 标签注入无序且不可重复的集合,如 address 属性。
  3. Map 集合:使用 <map> 标签注入键值对集合,如 phones 属性,可以通过 keyvalue 来设置简单类型的键值对,或通过 key-refvalue-ref 引用其他 Bean。
  4. Properties 集合:使用 <props> 标签注入键值对形式的属性,如 properties 属性,常用于注入配置数据。

注入 null

Spring 提供了 null 元素,用于将 null 值注入到 Bean 的属性中。

<bean id="personBean" class="spring6.bean.Person">
    <property name="name">
        <null/>
    </property>
</bean>
解释:
  • 通过 <null/> 标签,Spring 会将 null 值注入到 name 属性中。

注意这样有时候会报错:空指针异常。

2. 注入空字符串

注入空字符串时,直接将 value 设为空即可。

示例:
<bean id="personBean" class="spring6.bean.Person">
    <property name="name" value=""/>
</bean>
解释:
  • <property name="name" value=""/> 表示将一个空字符串注入到 name 属性中。这是处理空字符串的方式。

3. 注入特殊字符

有时候需要注入特殊字符,如 XML 中的转义字符或者特殊格式的字符串。在 XML 中,需要使用转义字符来表示一些特殊符号。

常见特殊字符及其转义方式:
  • < (小于号) - &lt;
  • > (大于号) - &gt;
  • & (和符号) - &amp;
  • " (双引号) - &quot;
  • ' (单引号) - &apos;

示例:注入带有特殊字符的字符串

<bean id="personBean" class="spring6.bean.Person">
    <property name="description" value="This is a &quot;special&quot; string with &lt;special&gt; characters like &amp; and quotes."/>
</bean>
 使用 <![CDATA[]]> 注入特殊符号:
<bean id="personBean" class="spring6.bean.Person">
    <property name="description">
        <![CDATA[This is a "special" string with <special> characters like & and quotes.]]>
    </property>
</bean>
解释:
  • <![CDATA[]]> 是 XML 的一种语法,用于将其中的内容视为未解析的字符数据(Character Data)。XML 解析器不会解析 CDATA 块内的任何内容,所有字符都会原样注入到属性中。
  • 在这个示例中,description 属性中包含了双引号(")、小于号(<)、大于号(>)、和符号(&)等特殊字符。使用 CDATA 包裹后,这些字符无需转义,Spring 会将它们按原样注入。

什么时候使用 <![CDATA[]]>

  • 当你需要注入包含特殊字符的长文本时,如 HTML、XML 片段、SQL 查询语句等。
  • 当注入的内容包含许多需要转义的字符,使用 CDATA 可以避免过多的手动转义,减少出错的可能性。

示例:注入包含 HTML 代码的字符串 

<bean id="htmlBean" class="spring6.bean.HtmlContent">
    <property name="content">
        <![CDATA[
            <html>
                <body>
                    <h1>This is a heading</h1>
                    <p>This is a paragraph with <b>bold</b> text.</p>
                </body>
            </html>
        ]]>
    </property>
</bean>

在这个示例中,content 属性包含了一个 HTML 片段。如果不用 CDATA,这些 HTML 标签会被解析器认为是 XML 的一部分,可能导致解析错误。而使用 <![CDATA[]]> 可以让 XML 解析器忽略这些内容并将其作为纯文本处理。

 p命名空间:

什么是 P 命名空间注入?

在 Spring 中,P命名空间(P-namespace)是一种简化 XML 配置文件的方法,用于方便地为 bean 的属性进行赋值,提供了一种比传统 <property> 标签更简洁的注入方式。主要是为了简化set注入。

P命名空间注入的工作原理

通常,Spring 中的属性注入使用 <property> 标签来设置 bean 的属性,例如:

<bean id="myBean" class="com.example.MyBean">
    <property name="name" value="Spring" />
    <property name="age" value="30" />
</bean>

如果使用 P 命名空间,配置会变得更加简洁:

但是首先引入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 配置 -->
</beans>

添加以下代码: 

xmlns:p="http://www.springframework.org/schema/p" 

例如,假设你有一个类 Person,它有两个属性:nameage。通过传统的 XML 配置,你的注入方式可能是这样的:

<bean id="person" class="com.example.Person">
    <property name="name" value="John Doe" />
    <property name="age" value="30" />
</bean>

如果启用了 P 命名空间,配置会变得简洁:

<bean id="person" class="com.example.Person" p:name="John Doe" p:age="30" />

C 命名空间

C 命名空间的工作原理

主要是为了简化构造函数注入。

通常,Spring 中的构造函数注入需要在 XML 配置文件中使用 <constructor-arg> 标签来指定参数。例如:

<bean id="person" class="com.example.Person">
    <constructor-arg value="John Doe" />
    <constructor-arg value="30" />
</bean>

这种方式虽然清晰,但对于参数较多的构造函数来说,配置会显得冗长。而通过引入 C 命名空间,可以用更简洁的方式来进行构造函数注入:

<bean id="person" class="com.example.Person" c:_0="John Doe" c:_1="30" />

C 命名空间的另一种方式——具名参数

如果构造函数有多个参数,并且需要按名称区分参数,还可以使用具名的方式,例如:

<bean id="person" class="com.example.Person" c:name="John Doe" c:age="30" />

如何启用 C 命名空间

要使用 C 命名空间,和 P 命名空间一样,你需要在 XML 文件中引入它:

<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 配置 -->
</beans>

util命名空间

在 Spring 框架中,util 命名空间 提供了一些辅助工具类,用于在 XML 配置中更方便地处理集合、属性文件等数据结构。这使得我们能够在 XML 配置中定义集合类型(如列表、集合、映射、属性等),并通过 util 命名空间注入到 Spring 的 bean 中。

util 命名空间的用途

util 命名空间主要用于处理以下场景:

  1. 定义集合(List、Set、Map)
  2. 定义属性文件Properties 对象)
  3. 引用外部集合
  4. 提供简化的集合和属性文件的注入

如何启用 util 命名空间

首先,你需要在 XML 配置文件中引入 util 命名空间:把所有的beans换成util 

<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">
    <!-- Bean 配置 -->
</beans>

1. 使用 util:list 注入 List

你可以使用 util:list 来定义一个 List 类型的集合,并将其注入到 bean 中:

// 对于简单类型
<util:list id="myList">
    <value>Element1</value>
    <value>Element2</value>
    <value>Element3</value>
</util:list>

// 对于非简单类型
<bean id="myBean" class="com.example.MyBean">
    <property name="list" ref="myList" />
</bean>

在此例中,myList 是一个 List 集合,它可以通过 ref 被注入到 myBeanlist 属性中。

2. 使用 util:set 注入 Set

类似于 List,你可以使用 util:set 来定义一个 Set 集合:

<util:set id="mySet">
    <value>Element1</value>
    <value>Element2</value>
</util:set>

<bean id="myBean" class="com.example.MyBean">
    <property name="set" ref="mySet" />
</bean>

3. 使用 util:map 注入 Map

util:map 用于定义 Map 类型的键值对集合

<util:map id="myMap">
    <entry key="key1" value="value1" />
    <entry key="key2" value="value2" />
</util:map>

<bean id="myBean" class="com.example.MyBean">
    <property name="map" ref="myMap" />
</bean>

基于XML的自动装配

 根据名称自动装配(byName):

工作原理:

byName 自动装配会根据属性名称来匹配 Spring 容器中的 bean。如果 bean 的属性名和容器中的某个 bean 的 ID 相同,那么 Spring 会将该 bean 自动注入到这个属性中。

配置方法:

在 XML 配置中,使用 <bean> 元素的 autowire 属性将其设置为 byName

示例:

假设我们有两个类 PersonAddressPerson 类依赖于 Address

public class Person {
    private Address address;

    // setter method
    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    // getter and setter
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

XML 配置文件如下:

<bean id="address" class="com.example.Address">
    <constructor-arg value="New York" />
</bean>

<bean id="person" class="com.example.Person" autowire="byName" />

在这个配置中,Spring 会根据 Person 类中属性 address 的名称,自动找到容器中 ID 为 address 的 bean,并注入到 personaddress 属性中。

优点:
  • 自动根据属性名称匹配 bean,配置较为简单。
注意事项:
  • 如果 Spring 容器中没有与属性名称匹配的 bean,属性将不会被注入,且不会抛出异常(默认行为是属性值保持为 null)。
  • 如果属性名称和 bean ID 不匹配,需要显式使用 <property> 标签手动注入。

2. byType 自动装配

工作原理:

byType 自动装配会根据属性的类型来匹配 Spring 容器中的 bean。如果容器中存在与属性类型匹配的 bean,则 Spring 会将该 bean 自动注入到该属性中。

配置方法:

在 XML 配置中,使用 <bean> 元素的 autowire 属性将其设置为 byType

示例:

假设我们有相同的 PersonAddress

XML 配置文件如下:

<bean id="homeAddress" class="com.example.Address">
    <constructor-arg value="New York" />
</bean>

<bean id="person" class="com.example.Person" autowire="byType" />

在此配置中,Spring 会根据 Person 类中属性 address 的类型 Address,自动找到容器中类型为 Address 的 bean,并将其注入到 personaddress 属性中。

优点:
  • 根据类型自动匹配 bean,避免了硬编码的 ID 匹配。
注意事项:
  • 如果容器中存在多个相同类型的 bean,Spring 将抛出异常,因为它不知道该注入哪一个 bean。为避免这个问题,可以使用 @Primary 注解或 qualifier 来指明注入的 bean。
  • 如果没有匹配的类型,Spring 将不会注入该属性,并可能导致空指针异常。

byNamebyType 的对比

特性byNamebyType
匹配方式根据属性名称和 bean ID 匹配根据属性的类型和 bean 类型匹配
适用场景当容器中存在多个相同类型但名称不同的 bean 时使用当容器中只有一个特定类型的 bean 时效果更好
配置简洁性需要 bean 的 ID 与属性名相同不依赖名称,只依赖类型
缺点名称匹配失败时,不会注入属性存在多个相同类型的 bean 时会抛出异常

Spring引入外部属性配置文件

我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver url username password等信息。这些信息可以单独写到一个属性配置文件中吗,这样用户修改起来会更加的方便。当然可以。 第一步:写一个数据源类,提供相关属性。

package spring6.jdbc;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 所有的数据源都要实现java规范:javax.sql.DataSource
 * 什么是数据源:能够给你提供Connection对象的,都是数据源
 */
// 可以将数据源交给spring管理
public class myDataSource implements DataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "myDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

第二步:在类路径下新建jdbc.properties文件,并配置信息。

driverClass=om.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root // 自己的数据库密码

第三步:在spring配置文件中引入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/>

第四步:在spring中配置使用jdbc.properties文件。

<?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">
<!--引入外部的properties文件-->
<!--    第一步:引入Context命名空间
        第二步:使用标签context:property-placeholder的location属性配置文件的路径
                    location默认从类的根路径开始加载资源
-->
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="ds" class="spring6.jdbc.myDataSource" >
    <property name="driver" value="${driverClass}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
</beans>

;