Spring6-4.Spring 对 IoC 的实现

61 阅读12分钟

Spring 对 IoC 的实现

依赖注入

Spring 通过依赖注入的方式来完成 Bean 的管理,Bean 的管理是指 Bean对象的创建以及 Bean 对象之间关系的维护。

set 注入

在 UserService 类中需要配置 UserDao 对象的依赖关系。

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

然后需要在 Spring 配置文件中声明 UserService 的这个 Bean 中如何使用 set 注入的方式管理 UserDao 的依赖关系。

<?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">
        <!-- 想让 Spring 调用对应的 set 方法,需要配置 property 标签 -->
​
        <!-- name 属性:set方法的方法名去掉“set“,然后把剩下的单词首字母变小写 -->
        <!-- ref 后面指定的是 bean 的 id -->
        <!-- <property name="mySQLUserDao" ref="userDaoBean" />-->
        <property name="userDao" ref="userDaoBean"/>
    </bean></beans>

构造注入

首先要定义好构造函数

public class CustomerService {
​
    private UserDao userDao;
​
    private VipDao vipDao;
​
    CustomerService(UserDao userDao, VipDao vipDao) {
        this.userDao = userDao;
        this.vipDao = vipDao;
    }
​
    public void save() {
        userDao.insert();
        vipDao.insert();
    }
}

然后在配置文件中配置构造函数中各个参数关联的 bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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="vipDaoBean" class="com.powernode.spring6.dao.VipDao" />
    <bean id="customerServiceBean" class="com.powernode.spring6.service.CustomerService">
        <!-- 构造注入(第一种方式,通过下标) -->
        <!-- index = 0 用于指定构造方法的第一个参数 -->
        <constructor-arg index="0" ref="userDaoBean" />
        <constructor-arg index="1" ref="vipDaoBean" />
    </bean>
​
    <bean id="customerServiceBean2" class="com.powernode.spring6.service.CustomerService">
        <!-- 构造注入(第二种方式,通过参数名称) -->
        <!-- name = userDao 用于指定构造方法中名为 userDao 的参数 -->
        <constructor-arg name="userDao" ref="userDaoBean" />
        <constructor-arg name="vipDao" ref="vipDaoBean" />
    </bean>
    <bean id="customerServiceBean3" class="com.powernode.spring6.service.CustomerService">
        <!-- 构造注入(第三种方式,根据类型自动判断,不推荐这种方式,可读性太差) -->
        <!-- 不指定下标,也不指定参数名,让 spring 自己做类型匹配 -->
        <constructor-arg ref="userDaoBean" />
        <constructor-arg ref="vipDaoBean" />
    </bean>
</beans>

set注入专题

注入外部 Bean(推荐)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 声明定义 bean -->
    <bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
​
    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
        <!-- 注入外部 bean(推荐): 使用 ref 属性来引入 -->
        <property name="userDao" ref="userDaoBean"/>
    </bean>
</beans>

注入内部 Bean

在 bean 标签中嵌套 bean 标签

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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:在 property 标签中使用嵌套的 bean 标签 -->
            <bean class="com.powernode.spring6.dao.UserDao"/>
        </property>
    </bean></beans>

注入简单类型

public class User{
    
    private String username;
    private int age;
    
    public void setUsername(String username) {
        this.username = username;
    }
  
    public void setAge(int age){
        this.age = age;
    }
}

可以通过 set 注入给 String、int 等简单类型赋值。

<?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">
        </property>
    </bean>
</beans>

简单类型包括哪些呢?可以通过Spring的源码来分析一下:BeanUtils类

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
  • 另外还包括以上简单值类型对应的数组类型

注意:spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在则报错。

级联属性赋值

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 + ''' +
                '}';
    }
}
​
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;
    }
    
    // 使用级联属性赋值,需要 getter 方法
    public Clazz getClazz() {
        return clazz;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", clazz=" + clazz +
                '}';
    }
}
<?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="clazzBean" class="com.powernode.spring6.beans.Clazz"/>
​
    <bean id="student" class="com.powernode.spring6.beans.Student">
        <property name="name" value="张三"/>
​
        <!--要点1:以下两行配置的顺序不能颠倒-->
        <property name="clazz" ref="clazzBean"/>
        <!--要点2:clazz属性必须有getter方法-->
        <property name="clazz.name" value="高三一班"/>
    </bean>
</beans>

注入数组

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

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 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="person" class="com.powernode.spring6.beans.Person">
        <property name="favariteFoods">
            <array>
                <value>鸡排</value>
                <value>汉堡</value>
                <value>鹅肝</value>
            </array>
        </property>
    </bean>
</beans>

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

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 + ''' +
                '}';
    }
}
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 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="goods1" class="com.powernode.spring6.beans.Goods">
        <property name="name" value="西瓜"/>
    </bean>
​
    <bean id="goods2" class="com.powernode.spring6.beans.Goods">
        <property name="name" value="苹果"/>
    </bean>
​
    <bean id="order" class="com.powernode.spring6.beans.Order">
        <property name="goods">
            <array>
                <!--这里使用ref标签即可-->
                <ref bean="goods1"/>
                <ref bean="goods2"/>
            </array>
        </property>
    </bean>
</beans>

注入 List 集合

package com.powernode.spring6.beans;
​
import java.util.List;
​
/**
 * @author 动力节点
 * @version 1.0
 * @className People
 * @since 1.0
 **/
public class People {
    // 一个人有多个名字
    private List<String> names;
​
    public void setNames(List<String> names) {
        this.names = names;
    }
​
    @Override
    public String toString() {
        return "People{" +
                "names=" + names +
                '}';
    }
}
<?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>

注入 Set 集合

Set集合:无序不可重复

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<String> phones;
​
    public void setPhones(Set<String> phones) {
        this.phones = phones;
    }
    
    //......
    
    @Override
    public String toString() {
        return "People{" +
                "phones=" + phones +
                ", names=" + names +
                '}';
    }
}
<?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="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>
</beans>

注入 Map 集合

注意:

  • 如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
  • 如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
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<Integer, String> addrs;
​
    public void setAddrs(Map<Integer, String> addrs) {
        this.addrs = addrs;
    }
    
    //......
    
    @Override
    public String toString() {
        return "People{" +
                "addrs=" + addrs +
                ", phones=" + phones +
                ", names=" + names +
                '}';
    }
​
}
<?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="addrs">
            <map>
                <!--如果key不是简单类型,使用 key-ref 属性-->
                <!--如果value不是简单类型,使用 value-ref 属性-->
                <entry key="1" value="北京大兴区"/>
                <entry key="2" value="上海浦东区"/>
                <entry key="3" value="深圳宝安区"/>
            </map>
        </property>
    </bean>
</beans>

注入 Properties

java.util.Properties 继承 java.util.Hashtable,所以 Properties 也是一个 Map 集合。

虽然这是一个 Map 集合,但是和 Map 的注入方式不太一样。

Properties 的 key 和 value 只能是 String,不能是其他类型。

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 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="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>
</beans>

注入 NULL 和空字符串

注入空字符串使用:<value/> 或者 value="";

注入null使用:<null/> 或者 不为该属性赋值。

  • 我们先来看一下,怎么注入空字符串。
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 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">
        <!--空串的第一种方式(idea推荐使用第一种方式)-->
        <!--<property name="email" value=""/>-->
        <!--空串的第二种方式-->
        <property name="email">
            <value/>
        </property>
    </bean></beans>

怎么注入null呢?

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

<?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" />
</beans>

第二种方式:使用 <null/>

<?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>

注入的值含有特殊符号

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

解决方案包括两种:

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

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

特殊字符转义字符
>&gt;
<&lt;
'&apos;
"&quot;
&&amp;

先用转义字符代替。

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 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>

再来使用CDATA方式。

<?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标签。

p 命名空间注入

这种注入方式也是基于 set 方法注入的,只不过这种方式可以简化配置。

使用p命名空间注入的前提条件包括两个:

  • 第一:在 XML 头部信息中添加 p 命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"
  • 第二:p 命名空间注入是基于 setter 方法的,所以需要对应的属性提供 setter 方法。
package com.powernode.spring6.bean;

import java.util.Date;

public class Dog {

    private String name;
    private int age;
    private Date birth;

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

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

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birth=" + birth +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 
				step1:在 spring 配置文件的头部添加 p 命名空间 
				如果是简单类型,直接 p:属性=;
				如果是饮用烈性,p:属性-ref=“bean”。
		-->
    <bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="小花" p:age="3" p:birth-ref="birthBean" />

    <!-- 这里会调用无参的构造函数,既获取当前的日期 -->
    <bean id="birthBean" class="java.util.Date"/>
</beans>

c 命名空间注入

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

使用c命名空间的两个前提条件:

  • 第一:需要在xml配置文件头部添加信息:xmlns:c="http://www.springframework.org/schema/c"
  • 第二:需要提供构造方法。
public class People {

    private String name;

    private int age;

    private boolean sex;

    public People(String name, int age, boolean sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }
}
<?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: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 id="peopleBean" class="com.powernode.spring6.bean.People" c:_0="小花" c:_1="13" c:_2="true"/>
    <!-- 第二种注入方式 -->
    <bean id="peopleBean2" class="com.powernode.spring6.bean.People" c:name="小花" c:age="13" c:sex="true"/>
</beans>

util 命名空间

使用util命名空间可以让配置复用

使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:

utilnamespace.jpg

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 +
                '}';
    }
}
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 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">

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

基于 XML 的自动装配

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

根据名称自动装配

public class UserDao {

    private static final Logger logger = LoggerFactory.getLogger(UserDao.class);

    public void insert() {
//        System.out.println("数据库正在保存用户信息");
        logger.info("数据库正在保存用户信息");
    }
}
package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

public class UserService {

    private UserDao aaa;

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

    public void save(){
        aaa.insert();
    }
}
<?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"/>
    
    <bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>

</beans>

UserService 类中有一个 UserDao 属性,而 UserDao 属性的名字是 aaa,对应的 set 方法是 setAaa(),正好和UserDao Bean 的 id 是一样的。

测试一下 byName 装配是和属性名有关还是和 set 方法名有关系:

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();
    }
}

在执行测试程序:

autowire_set_err.png

通过测试得知,aaa 属性并没有赋值成功,也就是没有装配成功。

我们将spring配置文件修改以下:

<?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>

测试程序就可以正常执行了。这说明,如果根据名称装配,底层会调用 set 方法进行注入。

根据类型自动装配

package com.powernode.spring6.dao;

public class AccountDao {

    public void insert() {
        System.out.println("Inserting a new account");
    }
}

package com.powernode.spring6.service;

import com.powernode.spring6.dao.AccountDao;

public class AccountService {

    private AccountDao accountDao;

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

    public void save() {
        accountDao.insert();
    }
}
<?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 class="com.powernode.spring6.dao.AccountDao" />

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

当注释掉 AccountService 的set方法时,再执行会报错:

autowire_set_err_2.jpg

可以看到无论是 byName 还是 byType,在装配的时候都是基于 set 方法的。所以 set 方法是必须要提供的,提供构造方法是不行的。

当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个。

Spring 引入外部属性配置文件

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

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

第一步:写一个数据源类,提供相关属性。

package com.powernode.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 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 {
        // 获取数据库连接对象的时候需要 4 个信息:driver、URL、username、password
        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文件,并配置信息。

jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=123455

第三步:在 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:property-placeholder location="jdbc.properties" />
    <!-- 第三步:使用 ${key} 配置数据源 -->
    <bean id="ds" class="com.powernode.spring6.jdbc.MyDataSource">
        <property name="driver" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>