Skip to content

04 Spring对IoC的实现

4.1 IoC 控制反转

  • 控制反转是一种思想。
  • 控制反转是为了降低程序耦合度,提高程序扩展力,达到 OCP 原则,达到 DIP 原则
  • 控制反转,反转的是什么?
    • 将对象的创建权利交出去,交给第三方容器负责。
    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
  • 控制反转这种思想如何实现呢?
    • DI(Dependency Injection):依赖注入

4.2 依赖注入

依赖注入实现了控制反转的思想。
- Spring 通过依赖注入的方式来完成 Bean 管理的。 - Bean 管理说的是:Bean 对象的创建,以及 Bean 对象中属性的赋值(或者叫做 Bean 对象之间关系的维护)。

依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。

依赖注入常见的实现方式包括两种:
- 第一种:set 注入
- 第二种:构造注入

新建模块:spring 6-002-dependency-injection

4.2.1 set 注入

set 注入,基于 set 方法实现的,底层会通过反射机制调用属性对应的 set 方法然后给属性赋值。这种方式要求属性必须对外提供 set 方法。

```XML title:pom.xml

4.0.0 com.powernode spring6-002-dependency-injection 1.0-SNAPSHOT jar

<repositories>
    <repository>
        <id>repository.spring.milestone</id>
        <name>Spring Milestone Repository</name>
        <url>https://repo.spring.io/milestone</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.0-M2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

```Java title:UserDao.java
package com.powernode.spring6.dao;

/**
 * @author 动力节点
 * @version 1.0
 * @className UserDao
 * @since 1.0
 **/
public class UserDao {

    public void insert(){
        System.out.println("正在保存用户数据。");
    }
}

```Java title:UserService.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

/** * @author 动力节点 * @version 1.0 * @className UserService * @since 1.0 **/ public class UserService {

private UserDao userDao;

// 使用set方式注入,必须提供set方法。
// 反射机制要调用这个方法给属性赋值的。
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

public void save(){
    userDao.insert();
}

}

```XML title:Spring.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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
        <property name="userDao" ref="userDaoBean"/>
    </bean>

</beans>
package com.powernode.spring6.test;

import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 动力节点
 * @version 1.0
 * @className DITest
 * @since 1.0
 **/
public class DITest {

    @Test
    public void testSetDI(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
        userService.save();
    }
}

运行结果: ![[04.02-1.webp]]

重点内容是,什么原理: ```XML title:spring.xml

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
    <property name="userDao" ref="userDaoBean"/>
</bean>

实现原理:  
1. 通过 property 标签获取到属性名:userDao  
2. 通过属性名推断出 set 方法名:setUserDao  
3. 通过反射机制调用 setUserDao () 方法给属性赋值  
4. property 标签的 name 是属性名。  
5. property 标签的 ref 是要注入的 bean 对象的 id。(通过 ref 属性来完成 bean 的装配,这是 bean 最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)  
可以把 set 方法注释掉,再测试一下:

![[04.02-2.webp]]

经过测试,底层实际上调用了 `setUserDao()` 方法。所以需要确保这个方法的存在。

现在把属性名修改一下,但方法名还是 `setUserDao()`,我们来测试一下:

```java title:UserService.java
package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className UserService
 * @since 1.0
 **/
public class UserService {

    private UserDao aaa;

    // 使用set方式注入,必须提供set方法。
    // 反射机制要调用这个方法给属性赋值的。
    public void setUserDao(UserDao userDao) {
        this.aaa = userDao;
    }

    public void save(){
        aaa.insert();
    }
}

![[04.02-3.webp]]

测试结果表明,程序仍然可以正常执行,说明 property 标签的 name 属性是由 setUserDao() 方法名演变得到的。演变的规律是:

  • setUsername() 演变为 username
  • setPassword() 演变为 password
  • setUserDao() 演变为 userDao
  • setUserService() 演变为 userService

另外,对于 property 标签来说,ref 属性也可以采用标签的方式,但使用 ref 属性是多数的:

```xml title:spring.xml

**总结:setter 注入的核心实现原理:通过反射机制调用 set 方法来给属性赋值,让两个对象之间产生关系。**

### 4.2.2 构造注入

核心原理:通过调用构造方法来给属性赋值。

```java title:OrderDao.java
package com.powernode.spring6.dao;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderDao
 * @since 1.0
 **/
public class OrderDao {
    public void deleteById(){
        System.out.println("正在删除订单。。。");
    }
}

```java title:OrderService.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.OrderDao;

/** * @author 动力节点 * @version 1.0 * @className OrderService * @since 1.0 **/ public class OrderService { private OrderDao orderDao;

// 通过反射机制调用构造方法给属性赋值
public OrderService(OrderDao orderDao) {
    this.orderDao = orderDao;
}

public void delete(){
    orderDao.deleteById();
}

} xml title:spring.xml ```

```java title:测试程序 import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.powernode.spring6.service.OrderService;

public class MyTest { @Test public void testConstructorDI(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class); orderServiceBean.delete(); } }

运行结果如下:

![[04.02-4.webp]]

 如果构造方法有两个参数:

```java title:OrderService
package com.powernode.spring6.service;

import com.powernode.spring6.dao.OrderDao;
import com.powernode.spring6.dao.UserDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderService
 * @since 1.0
 **/
public class OrderService {
    private OrderDao orderDao;
    private UserDao userDao;

    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao, UserDao userDao) {
        this.orderDao = orderDao;
        this.userDao = userDao;
    }

    public void delete(){
        orderDao.deleteById();
        userDao.insert();
    }
}

spring 配置文件:

```xml title:spring.xml

执行测试程序:
![[04.02-5.webp]]

不使用参数下标,使用参数的名字可以吗?
```xml title:spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--这里使用了构造方法上参数的名字-->
  <constructor-arg name="orderDao" ref="orderDaoBean"/>
  <constructor-arg name="userDao" ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序: ![[04.02-6.webp]]

不指定参数下标,不指定参数名字,可以吗? ```XML title:spring.xml

执行测试程序:
![[04.02-7.webp]]

配置文件中构造方法参数的类型顺序和构造方法参数的类型顺序不一致呢?
```XML title:spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--顺序已经和构造方法的参数顺序不同了-->
  <constructor-arg ref="userDaoBean"/>
  <constructor-arg ref="orderDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序: ![[04.02-8.webp]]

通过测试得知,通过构造方法注入的时候:

  • 可以通过下标
  • 可以通过参数名
  • 也可以不指定下标和参数名,可以类型自动推断。

Spring 在装配方面做的还是比较健壮的。


4.3 set 注入专题

4.3.1 注入外部 Bean

在之前 4.2.1 中使用的案例就是注入外部 Bean 的方式。 ```XML title:spring.xml

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
    <property name="userDao" ref="userDaoBean"/>
</bean>

外部 Bean 的特点:bean 定义到外面,在 property 标签中使用 ref 属性进行注入。通常这种方式是常用。

### 4.3.2 注入内部 Bean

内部 Bean 的方式:在 bean 标签中嵌套 bean 标签。
```XML title:spring-inner-bean.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="userServiceBean" class="com.powernode.spring6.service.UserService">
        <property name="userDao">
            <bean class="com.powernode.spring6.dao.UserDao"/>
        </property>
    </bean>

</beans>

@Test
public void testInnerBean(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml");
    UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
    userService.save();
}

执行测试程序: ![[04.03-1.webp]] 这种方式作为了解。

4.3.3 注入简单类型

我们之前在进行注入的时候,对象的属性是另一个对象。 ```Java title:对象的属性是另一个对象 public class UserService{

private UserDao userDao;

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}

}

那如果对象的属性是 int 类型呢?
```Java title:对象的属性是int类型
public class User{

    private int age;

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

}

可以通过 set 注入的方式给该属性赋值吗? * 当然可以。因为只要能够调用 set 方法就可以给属性赋值。

编写程序给一个 User 对象的 age 属性赋值 20:

第一步:定义 User 类,提供 age 属性,提供 age 属性的 setter 方法。 ```Java title:User.java package com.powernode.spring6.beans;

/** * @author 动力节点 * @version 1.0 * @className User * @since 1.0 **/ public class User { private int age;

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

@Override
public String toString() {
    return "User{" +
            "age=" + age +
            '}';
}

}

第二步:编写 spring 配置文件:spring-simple-type. xml
```XML title:spring-simple-type.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="userBean" class="com.powernode.spring6.beans.User">
        <!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref-->
        <!--<property name="age" value="20"/>-->
        <property name="age">
            <value>20</value>
        </property>
    </bean>
</beans>

第三步:编写测试程序 ```Java title:DITestSimple.java @Test public void testSimpleType(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-simple-type.xml"); User user = applicationContext.getBean("userBean", User.class); System.out.println(user); }

第四步:运行测试程序
![[04.03-2.webp]]

需要特别注意:如果给简单类型赋值,使用 value 属性或 value 标签。而不是 ref。

简单类型包括哪些呢?可以通过 Spring 的源码来分析一下:BeanUtils 类
```Java title:BeanUtils.java
public class BeanUtils{

    //.......

    /**
     * Check if the given type represents a "simple" property: a simple value
     * type or an array of simple value types.
     * <p>See {@link #isSimpleValueType(Class)} for the definition of <em>simple
     * value type</em>.
     * <p>Used to determine properties to check for a "simple" dependency-check.
     * @param type the type to check
     * @return whether the given type represents a "simple" property
     * @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
     * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
     * @see #isSimpleValueType(Class)
     */
    public static boolean isSimpleProperty(Class<?> type) {
        Assert.notNull(type, "'type' must not be null");
        return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
    }

    /**
     * Check if the given type represents a "simple" value type: a primitive or
     * primitive wrapper, an enum, a String or other CharSequence, a Number, a
     * Date, a Temporal, a URI, a URL, a Locale, or a Class.
     * <p>{@code Void} and {@code void} are not considered simple value types.
     * @param type the type to check
     * @return whether the given type represents a "simple" value type
     * @see #isSimpleProperty(Class)
     */
    public static boolean isSimpleValueType(Class<?> type) {
        return (Void.class != type && void.class != type &&
                (ClassUtils.isPrimitiveOrWrapper(type) ||
                Enum.class.isAssignableFrom(type) ||
                CharSequence.class.isAssignableFrom(type) ||
                Number.class.isAssignableFrom(type) ||
                Date.class.isAssignableFrom(type) ||
                Temporal.class.isAssignableFrom(type) ||
                URI.class == type ||
                URL.class == type ||
                Locale.class == type ||
                Class.class == type));
    }

    //........
}

通过源码分析得知,简单类型包括: * 基本数据类型 * 基本数据类型对应的包装类 * String 或其他的 CharSequence 子类 * Number 子类 * Date 子类 * Enum 子类 * URI * URL * Temporal 子类 * Locale * Class * 另外还包括以上简单值类型对应的数组类型。

经典案例:给数据源的属性注入值:

假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现 javax. sql. DataSource 接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password 等。

```java title:MyDataSource package com.powernode.spring6.beans;

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;

/** * @author 动力节点 * @version 1.0 * @className MyDataSource * @since 1.0 **/ public class MyDataSource implements DataSource { private String driver; private String url; private String username; private String password;

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

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

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

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

@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;
}

} 我们给 driver、url、username、password 四个属性分别提供了 setter 方法,我们可以使用 spring 的依赖注入完成数据源对象的创建和属性的赋值吗?看配置文件xml title: spring-datasource.xml ```

测试程序: ```Java title:DITest.testDataSource @Test public void testDataSource(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml"); MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class); System.out.println(dataSource); }

执行测试程序:
![[04.03-3.webp]]
你学会了吗?  

接下来,我们编写一个程序,把所有的简单类型全部测试一遍:  
编写一个类 A:

```Java title:A.java
package com.powernode.spring6.beans;

import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.Locale;

/**
 * @author 动力节点
 * @version 1.0
 * @className A
 * @since 1.0
 **/
public class A {
    private byte b;
    private short s;
    private int i;
    private long l;
    private float f;
    private double d;
    private boolean flag;
    private char c;

    private Byte b1;
    private Short s1;
    private Integer i1;
    private Long l1;
    private Float f1;
    private Double d1;
    private Boolean flag1;
    private Character c1;

    private String str;

    private Date date;

    private Season season;

    private URI uri;

    private URL url;

    private LocalDate localDate;

    private Locale locale;

    private Class clazz;

    // 生成setter方法
    // 生成toString方法
}

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

```XML title:spring-all-simple-type.xml

<bean id="a" class="com.powernode.spring6.beans.A">
    <property name="b" value="1"/>
    <property name="s" value="1"/>
    <property name="i" value="1"/>
    <property name="l" value="1"/>
    <property name="f" value="1"/>
    <property name="d" value="1"/>
    <property name="flag" value="false"/>

    <property name="c" value="a"/>
    <property name="b1" value="2"/>
    <property name="s1" value="2"/>
    <property name="i1" value="2"/>
    <property name="l1" value="2"/>
    <property name="f1" value="2"/>
    <property name="d1" value="2"/>
    <property name="flag1" value="true"/>
    <property name="c1" value="a"/>

    <property name="str" value="zhangsan"/>
    <!--注意:value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果。-->
    <!--如果想使用其他格式的日期字符串,就需要进行特殊处理了。具体怎么处理,可以看后面的课程!!!!-->
    <property name="date" value="Fri Sep 30 15:26:38 CST 2022"/>
    <property name="season" value="WINTER"/>
    <property name="uri" value="/save.do"/>
    <!--spring6之后,会自动检查url是否有效,如果无效会报错。-->
    <property name="url" value="http://www.baidu.com"/>
    <property name="localDate" value="EPOCH"/>
    <!--java.util.Locale 主要在软件的本地化时使用。它本身没有什么功能,更多的是作为一个参数辅助其他方法完成输出的本地化。-->
    <property name="locale" value="CHINESE"/>
    <property name="clazz" value="java.lang.String"/>
</bean>

编写测试程序:Java title:DITest.testAllSimpleType() @Test public void testAllSimpleType(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml"); A a = applicationContext.getBean("a", A.class); System.out.println(a); } ```

执行结果如下:

![[04.03-4.webp]]

需要注意的是:
- 如果把 Date 当做简单类型的话,日期字符串格式不能随便写。格式必须符合 Date 的 toString () 方法格式。显然这就比较鸡肋了。如果我们提供一个这样的日期字符串:2010-10-11,在这里是无法赋值给 Date 类型的属性的。
- spring 6 之后,当注入的是 URL,那么这个 url 字符串是会进行有效性检测的。如果是一个存在的 url,那就没问题。如果不存在则报错。

好的,以下是你提供的所有内容已严格按照原文进行格式化的 Markdown 版本,没有新增或删除任何文字,只做排版、代码和层次结构的整理,代码块已按要求保留 title,无“复制代码”等内容:

4.3.4 级联属性赋值(了解)

```java title:Clazz.java package com.powernode.spring6.beans;

/** * @author 动力节点 * @version 1.0 * @className Clazz * @since 1.0 **/ public class Clazz { private String name;

public Clazz() {
}

public Clazz(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

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

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

} java title:Student.java package com.powernode.spring6.beans; /** * @author 动力节点 * @version 1.0 * @className Student * @since 1.0 **/ public class Student { private String name; private Clazz clazz; public Student() { } public Student(String name, Clazz clazz) { this.name = name; this.clazz = clazz; } public void setName(String name) { this.name = name; } public void setClazz(Clazz clazz) { this.clazz = clazz; } public Clazz getClazz() { return clazz; } @Override public String toString() { return "Student{" + "name='" + name + '\''; ```

```xml title:spring-cascade.xml 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">

```java title:测试方法
@Test
public void testCascade(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-cascade.xml");
    Student student = applicationContext.getBean("student", Student.class);
    System.out.println(student);
}

运行结果:

![[04.03-5.webp]]

要点:

  • 在 spring 配置文件中,如上,注意顺序。
  • 在 spring 配置文件中,clazz 属性必须提供 getter 方法。

4.3.5 注入数组

当数组中的元素是简单类型:

```java title:Person.java package com.powernode.spring6.beans;

import java.util.Arrays;

public class Person { private String[] favariteFoods;

public void setFavariteFoods(String[] favariteFoods) {
    this.favariteFoods = favariteFoods;
}

@Override
public String toString() {
    return "Person{" +
            "favariteFoods=" + Arrays.toString(favariteFoods) +
            '}';
}

} xml title:spring-array-simple.xml 鸡排 汉堡 鹅肝 ```

```java title:测试方法 @Test public void testArraySimple(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array-simple.xml"); Person person = applicationContext.getBean("person", Person.class); System.out.println(person); }

当数组中的元素是非简单类型:一个订单中包含多个商品。

```java title:Goods.java
package com.powernode.spring6.beans;

/**
 * @author 动力节点
 * @version 1.0
 * @className Goods
 * @since 1.0
 **/
public class Goods {
    private String name;

    public Goods() {
    }

    public Goods(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

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

```java title:Order.java package com.powernode.spring6.beans;

import java.util.Arrays;

/** * @author 动力节点 * @version 1.0 * @className Order * @since 1.0 **/ public class Order { // 一个订单中有多个商品 private Goods[] goods;

public Order() {
}

public Order(Goods[] goods) {
    this.goods = goods;
}

public void setGoods(Goods[] goods) {
    this.goods = goods;
}

@Override
public String toString() {
    return "Order{" +
            "goods=" + Arrays.toString(goods) +
            '}';
}

} xml title:spring-array.xml 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"> ```

```java title:DITest.testArray() @Test public void testArray(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml"); Order order = applicationContext.getBean("order", Order.class); System.out.println(order); }

执行结果:

![[04.03-6.webp]]

要点:
- 如果数组中是简单类型,使用 `value` 标签。
- 如果数组中是非简单类型,使用 `ref` 标签。

### 4.3.6 注入 List

**List**:有序可重复

```java title:People.java
package com.powernode.spring6.beans;

import java.util.List;

/**
 * @author 动力节点
 * @version 1.0
 * @className People
 * @since 1.0
 **/
public class People {
    // 一个人有多个名字
    private List names;

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

    @Override
    public String toString() {
        return "People{" +
                "names=" + names +
                '}';
    }
}

```xml title:spring-collection.xml

<bean id="peopleBean" class="com.powernode.spring6.beans.People">
    <property name="names">
        <list>
            <value>铁锤</value>
            <value>张三</value>
            <value>张三</value>
            <value>张三</value>
            <value>狼</value>
        </list>
    </property>
</bean>

java title:测试方法 @Test public void testCollection(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml"); People peopleBean = applicationContext.getBean("peopleBean", People.class); System.out.println(peopleBean); } ```

执行结果:

![[04.03-7.webp]]

注意:注入 List 的时候使用 <list> 标签,如果 List 中是简单类型使用 value 标签,反之使用 ref 标签。

4.3.7 注入 Set

Set:无序不可重复

```java title:People.java package com.powernode.spring6.beans;

import java.util.List; import java.util.Set;

/** * @author 动力节点 * @version 1.0 * @className People * @since 1.0 **/ public class People { // 一个人有多个电话 private Set phones;

public void setPhones(Set phones) {
    this.phones = phones;
}

//......

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

} xml title:spring-set.xml 110 110 120 120 119 119 ```

执行结果: ![[04.03-8.webp]] 要点: - 使用 <set> 标签 - Set 中元素是简单类型的使用 value 标签,反之使用 ref 标签

4.3.8 注入 Map

```java title:People.java package com.powernode.spring6.beans;

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

/** * @author 动力节点 * @version 1.0 * @className People * @since 1.0 **/ public class People { // 一个人有多个住址 private Map addrs;

public void setAddrs(Map addrs) {
    this.addrs = addrs;
}

//......

@Override
public String toString() {
    return "People{" +
            "addrs=" + addrs +
            ", phones=" + phones +
            ", names=" + names +
            '}';
}

} xml title: spring-map.xml ```

执行结果: ![[04.03-9.webp]] 要点: - 使用 <map> 标签 - 如果 key 是简单类型,使用 key 属性,反之使用 key-ref 属性 - 如果 value 是简单类型,使用 value 属性,反之使用 value-ref 属性

以下是你原始内容格式化后的 Markdown,严格按照你的要求操作,没有增删文字内容,保留代码块语言与标题格式,移除多余内容(如代码行数/复制按钮提示),并保持整体结构清晰一致:

4.3.6 注入 List

List:有序可重复

```java title:People.java package com.powernode.spring6.beans;

import java.util.List;

/** * @author 动力节点 * @version 1.0 * @className People * @since 1.0 */ public class People {     // 一个人有多个名字     private List names;

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

@Override     public String toString() {         return "People{" +                 "names=" + names +                 '}';     } }

```xml title:spring-collection.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="peopleBean" class="com.powernode.spring6.beans.People">
        <property name="names">
            <list>
                <value>铁锤</value>
                <value>张三</value>
                <value>张三</value>
                <value>张三</value>
                <value>狼</value>
            </list>
        </property>
    </bean>
</beans>                      

```java title:PeopleTest.java @Test public void testCollection(){     ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");     People peopleBean = applicationContext.getBean("peopleBean", People.class);     System.out.println(peopleBean); }

执行结果:
![[04.03-10.webp]]
注意:注入 List**的时候使用 list 标签,如果 List**中是简单类型使用 value 标签,反之使用 ref 标签。

### 4.3.7 注入 Set

**Set**:无序不可重复

```java title:People.java
package com.powernode.spring6.beans;

import java.util.List;
import java.util.Set;

/**
 * @author 动力节点
 * @version 1.0
 * @className People
 * @since 1.0
 */
public class People {
    // 一个人有多个电话
    private Set phones;

    public void setPhones(Set phones) {
        this.phones = phones;
    }

    //......

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

```xml title:spring-set.xml

<bean id="peopleBean" class="com.powernode.spring6.beans.People">
    <property name="phones">
        <set>
            <!--非简单类型可以使用ref,简单类型使用value-->
            <value>110</value>
            <value>110</value>
            <value>120</value>
            <value>120</value>
            <value>119</value>
            <value>119</value>
        </set>
    </property>
</bean>

                       ``` 执行结果: ![[04.03-11.webp]] 要点:
- 使用\标签
- set**中元素是简单类型的使用 value 标签,反之使用 ref 标签。

4.3.8 注入 Map

java title:People.java package com.powernode.spring6.beans; import java.util.List; import java.util.Map; import java.util.Set; /** * @author 动力节点 * @version 1.0 * @className People * @since 1.0 */ public class People {     // 一个人有多个住址     private Map addrs;     public void setAddrs(Map addrs) {         this.addrs = addrs;     }     //......     @Override     public String toString() {         return "People{" +                 "addrs=" + addrs +                 ", phones=" + phones +                 ", names=" + names +                 '}';     } }

```xml title:spring-map.xml

<bean id="peopleBean" class="com.powernode.spring6.beans.People">
    <property name="addrs">
        <map>
            <!--如果key不是简单类型,使用 key-ref 属性-->
            <!--如果value不是简单类型,使用 value-ref 属性-->
            <entry key="1" value="北京大兴区"/>
            <entry key="2" value="上海浦东区"/>
            <entry key="3" value="深圳宝安区"/>
        </map>
    </property>
</bean>

         ``` 执行结果: ![[04.03-12.webp]] 要点:
- 使用\标签
- 如果 key 是简单类型,使用 key 属性,反之使用 key-ref 属性。
- 如果 value 是简单类型,使用 value 属性,反之使用 value-ref 属性。

4.3.9 注入 Properties

java. util. Properties 继承 java. util. Hashtable,所以 Properties 也是一个 Map。 java title:People.java package com.powernode.spring6.beans; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * @author 动力节点 * @version 1.0 * @className People * @since 1.0 */ public class People {     private Properties properties;     public void setProperties(Properties properties) {         this.properties = properties;     }     //......     @Override     public String toString() {         return "People{" +                 "properties=" + properties +                 ", addrs=" + addrs +                 ", phones=" + phones +                 ", names=" + names +                 '}';     } }

```xml title:spring-properties.xml

<bean id="peopleBean" class="com.powernode.spring6.beans.People">
    <property name="properties">
        <props>
            <prop key="driver">com.mysql.cj.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306/spring</prop>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>

                      ``` 执行测试程序: ![[04.03-13.webp]] 要点:
- 使用\标签嵌套\标签完成。

4.3.10 注入 null 和空字符串

注入空字符串使用:\ 或者 value=""
注入 null 使用:\ 或者不为该属性赋值
- 我们先来看一下,怎么注入空字符串。 java title:Vip.java package com.powernode.spring6.beans; /** * @author 动力节点 * @version 1.0 * @className Vip * @since 1.0 */ public class Vip {     private String email;     public void setEmail(String email) {         this.email = email;     }     @Override     public String toString() {         return "Vip{" +                 "email='" + email + '\'' +                 '}';     } }

```xml title:spring-null.xml

<bean id="vipBean" class="com.powernode.spring6.beans.Vip">
    <!--空串的第一种方式-->
    <!--<property name="email" value=""/>-->
    <!--空串的第二种方式-->
    <property name="email">
        <value/>
    </property>
</bean>

    

```java title:VipTest.java
@Test
public void testNull(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-null.xml");
    Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
    System.out.println(vipBean);
}

执行结果: ![[04.03-14.webp]]

  • 怎么注入 null 呢?

    • 第一种方式:不给属性赋值

```xml title:spring-null.xml

<bean id="vipBean" class="com.powernode.spring6.beans.Vip" />

   

执行结果:
![[04.03-15.webp]]
    - 第二种方式:使用\<null/>

```xml title:spring-null.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="vipBean" class="com.powernode.spring6.beans.Vip">
        <property name="email">
            <null/>
        </property>
    </bean>

</beans>                                 

执行结果: ![[04.03-17.webp]]

4.3.11 注入的值中含有特殊符号

XML 中有 5 个特殊字符,分别是:\<、>、'、"、&
以上 5 个特殊符号在 XML 中会被特殊对待,会被当做 XML 语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。

image.png

解决方案包括两种:
- 第一种:特殊符号使用转义字符代替。
- 第二种:将含有特殊符号的字符串放到:\<![CDATA[]]> 当中。因为放在 CDATA 区中的数据不会被 XML 文件解析器解析。

5 个特殊字符对应的转义字符分别是:

特殊字符 转义字符
> >
\< <
' '
" "
& &

先使用转义字符来代替: ```java title:Math.java package com.powernode.spring6.beans;

/** * @author 动力节点 * @version 1.0 * @className Math * @since 1.0 */ public class Math {     private String result;

public void setResult(String result) {         this.result = result;     }

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

```xml title:spring-special.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="mathBean" class="com.powernode.spring6.beans.Math">
        <property name="result" value="2 &lt; 3"/>
    </bean>
</beans>             

```java title:MathTest.java @Test public void testSpecial(){     ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-special.xml");     Math mathBean = applicationContext.getBean("mathBean", Math.class);     System.out.println(mathBean); }

执行结果:
![[04.3-1.png]]

我们再来使用 CDATA 方式:
```xml title:spring-special-cdata.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="mathBean" class="com.powernode.spring6.beans.Math">
        <property name="result">
            <!--只能使用value标签-->
            <value><![CDATA[2 < 3]]></value>
        </property>
    </bean>

</beans>                                               

注意:使用 CDATA 时,不能使用 value 属性,只能使用 value 标签。
执行结果:

![[04.03-2.png]]


4.4 p 命名空间注入

目的:简化配置。 使用 p 命名空间注入的前提条件包括两个: - 第一:在 XML 头部信息中添加 p 命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p http://www.springframework.org/schema/p%22" - 第二:p 命名空间注入是基于 setter 方法的,所以需要对应的属性提供 setter 方法。

```Java title:Customer.java package com.powernode.spring6.beans;

/** * @author 动力节点 * @version 1.0 * @className Customer * @since 1.0 **/ public class Customer { private String name; private int age;

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

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

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

}

```XML title:spring-p.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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="customerBean" class="com.powernode.spring6.beans.Customer" p:name="zhangsan" p:age="20"/>

</beans>

```Java title:测试程序 @Test public void testP(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml"); Customer customerBean = applicationContext.getBean("customerBean", Customer.class); System.out.println(customerBean); }

执行结果:
![[04.04-1.png]]
把 setter 方法去掉:
![[04.04-2.png]]

所以 p 命名空间实际上是对 set 注入的简化。


---
## 4.5 c 命名空间注入

**c 命名空间是简化构造方法注入的**。

使用 c 命名空间的两个前提条件: 
- 第一:需要在 xml 配置文件头部添加信息:xmlns: c\="[http://www.springframework.org/schema/c](http://www.springframework.org/schema/c)" 
- 第二:需要提供构造方法。

```Java title:MyTime.java
package com.powernode.spring6.beans;

/**
 * @author 动力节点
 * @version 1.0
 * @className MyTime
 * @since 1.0
 **/
public class MyTime {
    private int year;
    private int month;
    private int day;

    public MyTime(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyTime{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

```XML title:spring-c.xml

<!--<bean id="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:year="1970" c:month="1" c:day="1"/>-->

<bean id="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:_0="2008" c:_1="8" c:_2="8"/>

```Java title:测试程序.java
@Test
public void testC(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
    MyTime myTimeBean = applicationContext.getBean("myTimeBean", MyTime.class);
    System.out.println(myTimeBean);
}

执行结果: ![[04.05-1.png]] 把构造方法注释掉: ![[04.05-2.png]]

所以,c 命名空间是依靠构造方法的。 注意:不管是 p 命名空间还是 c 命名空间,注入的时候都可以注入简单类型以及非简单类型。


4.6 util 命名空间

使用 util 命名空间可以让配置复用。
使用 util 命名空间的前提是:在 spring 配置文件头部添加配置信息。 如下:
![[04.06-1.png]] ```java title:MyDataSource1.java package com.powernode.spring6.beans;

import java.util.Properties;

/
@author 动力节点
@version 1.0
* @className MyDataSource1
* @since 1.0
*/
public class MyDataSource1 {
    private Properties properties;

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

@Override
    public String toString() {
        return "MyDataSource1{" +
                "properties=" + properties +
                '}';
    }
}

```java title:MyDataSource2.java
package com.powernode.spring6.beans;  

import java.util.Properties;  

/**  
* @author 动力节点  
* @version 1.0  
* @className MyDataSource2  
* @since 1.0  
*/  
public class MyDataSource2 {  
    private Properties properties;  

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

    @Override  
    public String toString() {  
        return "MyDataSource2{" +  
                "properties=" + properties +  
                '}';  
    }  
}  

```xml title:spring-util.xml

<util:properties id="prop">
    <prop key="driver">com.mysql.cj.jdbc.Driver</prop>
    <prop key="url">jdbc:mysql://localhost:3306/spring</prop>
    <prop key="username">root</prop>
    <prop key="password">123456</prop>
</util:properties>

<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
    <property name="properties" ref="prop"/>
</bean>

<bean id="dataSource2" class="com.powernode.spring6.beans.MyDataSource2">
    <property name="properties" ref="prop"/>
</bean>

                                java title:TestUtil.java @Test
public void testUtil(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
    MyDataSource1 dataSource1 = applicationContext.getBean("dataSource1", MyDataSource1.class);
    System.out.println(dataSource1);
    MyDataSource2 dataSource2 = applicationContext.getBean("dataSource2", MyDataSource2.class);
    System.out.println(dataSource2);
}
```

执行结果:
![[04.06-2.png]]


4.7 基于 XML 的自动装配

Spring 还可以完成自动化的注入,自动化注入又被称为自动装配。 它可以根据名字进行自动装配,也可以根据类型进行自动装配。

4.7.1 根据名称自动装配

```java title:UserDao.java package com.powernode.spring6.dao;

/
@author 动力节点
@version 1.0
* @className UserDao
* @since 1.0
*/
public class UserDao {

public void insert(){
        System.out.println("正在保存用户数据。");
    }
}

```java title:UserService.java
package com.powernode.spring6.service;  

import com.powernode.spring6.dao.UserDao;  

/**  
* @author 动力节点  
* @version 1.0  
* @className UserService  
* @since 1.0  
*/  
public class UserService {  

    private UserDao aaa;  

    // 这个set方法非常关键  
    public void setAaa(UserDao aaa) {  
        this.aaa = aaa;  
    }  

    public void save(){  
        aaa.insert();  
    }  
}  

Spring 的配置文件这样配置:

```xml title:spring-autowire.xml

<bean id="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>

<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>

          

这个配置起到关键作用:  
-  `UserServiceBean` 中需要添加 `autowire="byName"`,表示通过名称进行装配。  
-  `UserService` 类中有一个 `UserDao` 属性,而 `UserDao` 属性的名字是 aaa,对应的 set 方法是 `setAaa ()`,正好和 `UserDaoBean` 的 id 是一样的。这就是根据名称自动装配。  

```java title:TestAutowireByName.java
@Test  
public void testAutowireByName(){  
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");  
    UserService userService = applicationContext.getBean("userService", UserService.class);  
    userService.save();  
}  

执行结果:
![[04.07-1.png]] 我们来测试一下,byName 装配是和属性名有关还是和 set 方法名有关系:

```java title:UserService.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

/
@author 动力节点
@version 1.0
* @className UserService
* @since 1.0
*/
public class UserService {
    // 这里没修改
    private UserDao aaa;

/public void setAaa(UserDao aaa) {
    this.aaa = aaa;
    }
/

// set方法名变化了
    public void setDao(UserDao aaa){
        this.aaa = aaa;
    }

public void save(){
        aaa.insert();
    }
}

在执行测试程序:  
![[04.07-2.png]]

通过测试得知,aaa 属性并没有赋值成功。也就是并没有装配成功。  
我们将 spring 配置文件修改以下:  

```xml title:spring-autowire.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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
  <!--这个id修改了-->
  <bean id="dao" class="com.powernode.spring6.dao.UserDao"/>

</beans>         

执行测试程序:
![[04.07-3.png]] 这说明,如果根据名称装配 (byName),底层会调用 set 方法进行注入。
例如:setAge () 对应的名字是 age,setPassword () 对应的名字是 password,setEmail () 对应的名字是 email。

4.7.2 根据类型自动装配

```java title:AccountDao.java package com.powernode.spring6.dao;

/
@author 动力节点
@version 1.0
* @className AccountDao
* @since 1.0
*/
public class AccountDao {
    public void insert(){
        System.out.println("正在保存账户信息");
    }
}

```java title:AccountService.java
package com.powernode.spring6.service;  

import com.powernode.spring6.dao.AccountDao;  

/**  
* @author 动力节点  
* @version 1.0  
* @className AccountService  
* @since 1.0  
*/  
public class AccountService {  
    private AccountDao accountDao;  

    public void setAccountDao(AccountDao accountDao) {  
        this.accountDao = accountDao;  
    }  

    public void save(){  
        accountDao.insert();  
    }  
}  

```xml title:spring-autowire.xml

<!--byType表示根据类型自动装配-->
<bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>

<bean class="com.powernode.spring6.dao.AccountDao"/>

          

```java title:TestAutowireByType.java
@Test  
public void testAutowireByType(){  
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");  
    AccountService accountService = applicationContext.getBean("accountService", AccountService.class);  
    accountService.save();  
}  

执行结果:
![[04.07-4.png]] 我们把 UserService 中的 set 方法注释掉,再执行:
![[04.07-5.png]] 可以看到无论是 byName 还是 byType,在装配的时候都是基于 set 方法的。所以 set 方法是必须要提供的。提供构造方法是不行的,大家可以测试一下。这里就不再赘述。
如果 byType,根据类型装配时,如果配置文件中有两个类型一样的 bean 会出现什么问题呢?

```xml title:spring-autowire.xml

<bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>

<bean id="x" class="com.powernode.spring6.dao.AccountDao"/>
<bean id="y" class="com.powernode.spring6.dao.AccountDao"/>

           

执行测试程序:  
![[04.07-6.png]]
测试结果说明了,**当 byType 进行自动装配的时候,配置文件中某种类型的 Bean 必须是唯一的,不能出现多个。**  


---
## 4.8 Spring 引入外部属性配置文件

我们都知道编写数据源的时候是需要连接数据库的信息的,
例如:`driver url username password` 等信息。

这些信息可以单独写到一个属性配置文件中吗,这样用户修改起来会更加的方便。当然可以。  

第一步:写一个数据源类,提供相关属性。
```java
package com.powernode.spring6.beans;

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;

/**
 * @author 动力节点
 * @version 1.0
 * @className MyDataSource
 * @since 1.0
 **/
public class MyDataSource implements DataSource {
    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    private String driver;
    private String url;
    private String username;
    private String password;

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

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

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

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

    //......
}

第二步:在类路径下新建 jdbc. properties 文件,并配置信息。 ```properties title:jdbc.properties driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/spring username=root password=root123

第三步:在 spring 配置文件中引入 context 命名空间。
```XML title:spring-properties.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

第四步:在 spring 中配置使用 jdbc. properties 文件。 ```XML title:spring-properties.xml

<context:property-placeholder location="jdbc.properties"/>

<bean id="dataSource" class="com.powernode.spring6.beans.MyDataSource">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</bean>

测试程序:Java title:测试 @Test public void testProperties(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml"); MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class); System.out.println(dataSource); } ```

执行结果: ![[Java/Java_Spring/attachments/04 Spring对IoC的实现/04.08-1.png]]