(四)Spring对IoC的实现


Spring学习目录

上一篇:(三)简单使用Spring

下一篇:(五)Spring之Bean的作用域

环境

spring6里程碑版本的仓库
依赖:spring context依赖、junit依赖、log4j2依赖
log4j2.xml文件放到类路径下。

IoC 控制反转

控制反转是一种思想。
控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
控制反转,反转的是什么?

  • 将对象的创建权利交出去,交给第三方容器负责。
  • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。

控制反转这种思想如何实现呢?
DI(Dependency Injection):依赖注入

依赖注入

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

  • 依赖:A对象和B对象的关系。
  • 注入:通过手段让A与B产生关系(依赖)。

依赖注入常见的实现方式包括两种:

  • 1.set注入(执行set方法给属性赋值)
  • 2.构造方法注入(执行构造方法给属性赋值)

set注入

创建UserDao

public class UserDao {
    //使用slf4j记录日志
    private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
    public void insert(){
        logger.info("新增用户数据");
    }
}

创建UserService
必须提供set方法,Spring就会调用这个set方法,给userDao属性赋值,这里的set的方法可以随意,只需要以set开头即可,但是之后在spring配置文件配置set注入的时候就不能随便写了。这里先不按照规范写一个set方法测试。

/**
 * set注入
 */
public class UserService {
    private UserDao userDao;
    //提供set方法之后,还需要在配置文件里面配置
    public void setMyUserDao(UserDao xy){
        this.userDao = xy;
    }      
    public void saveUser(){
        System.out.println("set注入");
        userDao.insert();
        
    }
}

创建spring.xml配置文件
通过property标签进行set注入。

  • property标签:配置set注入
    • name属性:是set方法的方法名去掉”set“,然后首字母小写
    • ref属性:翻译为引用(references),指定的是要注入的bean的id
<?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">

    <!--set注入-->
    <!--配置dao-->
    <bean id="userDaoBean" class="com.dependency.injection.dao.UserDao"/>

    <!--配置service-->
    <bean id="userServiceBean" class="com.dependency.injection.service.UserService">
        <property name="myUserDao" ref="userDaoBean"/>
    </bean>
    <bean id="carDaoBean" class="com.dependency.injection.dao.CarDao"></bean>
</beans>

测试程序:

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

请添加图片描述
实现原理:

  • 1.通过property标签获取到set方法名称:setMyUserDao
  • 2.通过反射机制推断出属性名:userDao
 this.userDao = xy;
  • 3.通过反射机制调用setUserDao()方法给属性赋值

说明property标签的name是:setUserDao()方法名演变得到的。演变的规律是:

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

一般情况下set方法名不会自定义,编写都是要符合java规范,也可以采用自动生成

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

所以spring.xml配置文件property标签的name就可以直接复制属性名

	<property name="userDao" ref="userDaoBean"/>

构造注入

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

public class CarDao {
    //使用slf4j记录日志
    private static final Logger logger = LoggerFactory.getLogger(CarDao.class);
    public void insert(){
        logger.info("新增汽车数据");
    }
}

创建CarService:
提供构造方法

/**
 * 构造注入
 */
public class CarService {
    private UserDao userDao;
    private CarDao carDao;

    public CarService(UserDao userDao, CarDao carDao) {
        this.userDao = userDao;
        this.carDao = carDao;
    }

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

    public void setCarDao(CarDao carDao) {
        this.carDao = carDao;
    }

    public void saveCar(){
        System.out.println("构造注入");
        userDao.insert();
        carDao.insert();
    }
}

创建spring2.xml配置文件:
构造注入需要在这个文件配置,通过constructor-arg标签配置。

  • constructor-arg标签:配置构造注入
    • index属性:指定的参数下标,以0开始
    • name属性:指定参数的名字
    • ref属性:指定的是要注入的bean的id

构造注入有三种配置方式:

  • 1.根据参数下标注入,使用index属性
  • 2.根据参数名字注入,使用name属性
  • 3.不指定参数下标和名字,Spring可以自己做自动类型匹配把ref注入
<?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">

    <!--构造注入-->
    <!--配置dao-->
    <bean id="userDaoBean" class="com.dependency.injection.dao.UserDao"></bean>
    <bean id="carDaoBean" class="com.dependency.injection.dao.CarDao"></bean>

    <!--配置service-->
    <bean id="carServiceBean" class="com.dependency.injection.service.CarService">
        <!--根据参数下标注入-->
        <constructor-arg index="0" ref="userDaoBean" />
        <constructor-arg index="1" ref="carDaoBean" />
    </bean>

    <bean id="carServiceBean2" class="com.dependency.injection.service.CarService">
        <!--根据参数名字注入-->
        <constructor-arg name="userDao" ref="userDaoBean" />
        <constructor-arg name="carDao" ref="carDaoBean" />
    </bean>

    <bean id="carServiceBean3" class="com.dependency.injection.service.CarService">
        <!--不指定参数下标和名字,Spring可以自己做自动类型匹配把ref注入-->
        <constructor-arg ref="userDaoBean" />
        <constructor-arg ref="carDaoBean" />
    </bean>
</beans>

测试程序:

    @Test
    public void testConstructorDI(){

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");

        //根据参数下标注入测试
        CarService carServiceBean = applicationContext.getBean("carServiceBean", CarService.class);
        carServiceBean.saveCar();
        //根据参数名字注入测试
        CarService carServiceBean2 = applicationContext.getBean("carServiceBean2", CarService.class);
        carServiceBean2.saveCar();
        //不指定参数下标和名字测试
        CarService carServiceBean3 = applicationContext.getBean("carServiceBean", CarService.class);
        carServiceBean3.saveCar();
    }

请添加图片描述

set注入专题

注入外部Bean

注入外部Bean就是使用ref属性注入,上面的set注入就是一个外部Bean注入
创建OrderDao:

public class OrderDao {
    private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
    public void insert(){
        logger.info("生成订单。。。");
    }
}

创建OrderService:

public class OrderService {
    private OrderDao orderDao;

    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
    public void  generate(){
        orderDao.insert();
    }
}

创建配置文件set-DI.xml

<!--
        注入外部Bean:在property标签使用ref属性注入
    -->
    <!--定义Bean-->
    <bean id="orderDaoBean" class="com.dependency.injection.dao.setDI.OrderDao"/>
    <bean id="orderServiceBean" class="com.dependency.injection.service.setDI.OrderService">
        <!--注入外部Bean:使用ref属性注入-->
        <property name="orderDao" ref="orderDaoBean"/>
    </bean>

测试程序:

    @Test
    public void testSetDI2(){
        //外部Bean测试
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
        OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
        orderServiceBean.generate();

        
    }

请添加图片描述

注入内部Bean(了解)

在property标签嵌套bean标签注入,称为内部Bean
在set-DI.xml配置:

	<!--
        注入内部Bean
    -->
    <bean id="orderServiceBean2" class="com.dependency.injection.service.setDI.OrderService">
        <property name="orderDao">
            <bean class="com.dependency.injection.dao.setDI.OrderDao"/>
        </property>
    </bean>

测试程序:

    @Test
    public void testSetDI2(){
        //外部Bean测试
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
        OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
        orderServiceBean.generate();

        //内部Bean测试
        OrderService orderServiceBean2 = applicationContext.getBean("orderServiceBean2", OrderService.class);
        orderServiceBean2.generate();
    }

请添加图片描述

注入简单类型

我们之前在进行注入的时候,对象的属性是另一个对象。使用的是一个ref属性。
如果我们注入的是一个int类型,我们就需要使用value属性赋值。
注入简单类型,Spring规定了哪些是简单类型呢,进入源码可以看一下BeanUtils工具类

public static boolean isSimpleValueType(Class<?> type) {
            return Void.class != type && Void.TYPE != 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);
        }

分别包括:

  • isPrimitiveOrWrapper:基本类型或其对应的包装类
  • Enum:枚举类型
  • CharSequence: 字符接口,凡是实现了字符接口都为简单类型,String实现了该接口,所以String为简单类型
  • Number:数字类型,凡是继承这个抽象类,都为简单类型
  • Date:日期类型
  • Temporal:时间和时区接口,这个是java8以后的新特性
  • URI:uri类
  • URL:url类
  • Locale:语言类,例如英文、中文等等
  • Class:class类

需要注意的是Date类型,想要使用Value进行赋值的话,这个value是有格式要求的。例如
这种格式是无法转换成java.util.Date类型的

<property name="birth" value="2020-11-11"/>

需要以下特定格式:

<property name="birth" value="Wed Nov 02 18:47:56 CST 2022"/>

但是这个格式是外国人看得比较舒服的格式,中国人看不习惯,所以,在实际开发中,不会把这个Date当作简单类型,一般采用ref进行赋值

我们来测试以下简单类型的注入
枚举类:

/**
 * 表示季节
 */
public enum Season {
    SPRING,SUMMER,AUTUMN,WINTER
}

创建SimpleValueType类:

/**
 * 测试简单类型
 */
public class SimpleValueType {
    //基本类型及其包装类

    private boolean flag;
    private Boolean flag2;

    private char c;
    private Character c2;

    //枚举类型
    private Season season;
    //CharSequence
    private String username;

    //Number
    private int age;
    private Integer age2;

    //Class
    private Class clazz;

    //Date
    private Date birth;

    @Override
    public String toString() {
        return "SimpleValueType{" +
                "flag=" + flag +
                ", flag2=" + flag2 +
                ", c=" + c +
                ", c2=" + c2 +
                ", season=" + season +
                ", username='" + username + '\'' +
                ", age=" + age +
                ", age2=" + age2 +
                ", clazz=" + clazz +
                ", birth=" + birth +
                '}';
    }

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

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public void setFlag2(Boolean flag2) {
        this.flag2 = flag2;
    }

    public void setC(char c) {
        this.c = c;
    }

    public void setC2(Character c2) {
        this.c2 = c2;
    }

    public void setSeason(Season season) {
        this.season = season;
    }

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

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

    public void setAge2(Integer age2) {
        this.age2 = age2;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
}

set-DI.xml文件配置

 <!--测试简单类型-->
    <bean id="svtBean" class="com.dependency.injection.bean.SimpleValueType">
        <property name="age" value="20"/>
        <property name="age2" value="20"/>
        <property name="username" value="张三"/>
        <property name="season" value="SPRING"/>
        <property name="flag" value="false"/>
        <property name="flag2" value="true"/>
        <property name="c" value="男"/>
        <property name="c2" value="女"/>
        <property name="clazz" value="java.lang.String"/>
        <property name="birth" value="Wed Nov 02 18:47:56 CST 2022"/>
        <!--这个格式是外国人看得比较舒服的格式,中国人看不习惯,所以,在实际开发中,不会把这个Date当作简单类型,一般采用ref进行赋值-->
    </bean>

测试程序:

    @Test
    public void testSimpleTypeSet(){
        //简单类型
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
        SimpleValueType svtBean = applicationContext.getBean("svtBean", SimpleValueType.class);
        System.out.println(svtBean);
    }

请添加图片描述

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

简单类型注入有什么用呢?
假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。可以让Spring容器管理数据源

/**
 * 把数据源交给Spring容器管理
 */
public class MyDataSource implements DataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + 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 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;
    }
}

set-DI.xml配置:

    <bean id="myDataSource" class="com.dependency.injection.jdbc.MyDataSource">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

测试程序:

    @Test
    public void testMyDataSource(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
        MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);
        System.out.println(myDataSource);
    }

请添加图片描述

级联属性赋值(了解)

创建Clazz班级类:

/**
 * 班级,级联赋值
 */
public class Clazz {
    private String name;//班级名称

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

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

创建Student学生类:

/**
 * 学生,级联赋值
 */
public class Student {
    private String name;

    private Clazz clazz;//学生属于哪个班级

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

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

    public Clazz getClazz() {
        return clazz;
    }

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

创建cascade.xml配置文件:
级联赋值,需要有两个注意点:

  • 1.这种情况Student里面还想要提供这个clazz属性的get方法
  • 2.这种方式赋值,想要保证clazz有值,也就是说先配置clazz的注入信息,再进行赋值,顺序不能颠倒。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="studentBean" class="com.dependency.injection.bean.Student">
        <property name="name" value="张三"/>
        <property name="clazz" ref="clazzBean" />
       	<!--这里的顺序不能颠倒,并且保证Student类里有getName方法-->
        <property name="clazz.name" value="高三二班" />
    </bean>

    <!--<bean id="clazzBean" class="com.dependency.injection.bean.Clazz">
        <property name="name" value="高三一班"/>
    </bean>-->
    <bean id="clazzBean" class="com.dependency.injection.bean.Clazz"></bean>
</beans>

测试程序:

    @Test
    public void testCascade(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("cascade.xml");
        Clazz clazzBean = applicationContext.getBean("clazzBean", Clazz.class);

        Student studentBean = applicationContext.getBean("studentBean", Student.class);
        System.out.println(studentBean);
        System.out.println(clazzBean);

    }

请添加图片描述

注入数组

简单类型数组注入

创建Arrays类

/**
 * 注入数组
 */
public class Arrays {
    private String[] like;//爱好
    public void setLike(String[] like) {
        this.like = like;
    }
    @Override
    public String toString() {
        return "Arrays{" +
                "like=" + java.util.Arrays.toString(like) +
                '}';
    }
}

创建SpringArray.xml文件配置:
属性是简单类型,并且是数组,property标签里面使用array标签套value标签,value标签里面输入值

<!--数组注入-->
    <bean id="arrayBean" class="com.dependency.injection.bean.Arrays">
        <property name="like">
            <array>
                <value></value>
                <value></value>
                <value>rap</value>
            </array>
        </property>
    </bean>

测试程序:

    @Test
    public void testArrays(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringArray.xml");
        Arrays arrayBean = applicationContext.getBean("arrayBean", Arrays.class);
        System.out.println(arrayBean);
    }

请添加图片描述

非简单类型数组注入

创建朋友类:

/**
 * 朋友类,注入数组
 */
public class Friend {
    private String name;

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

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

修改Arrays类:

/**
 * 注入数组
 */
public class Arrays {
    private String[] like;//爱好

    //多个朋友
    private Friend[] friends;

    public void setFriends(Friend[] friends) {
        this.friends = friends;
    }

    @Override
    public String toString() {
        return "Arrays{" +
                "like=" + java.util.Arrays.toString(like) +
                ", friends=" + java.util.Arrays.toString(friends) +
                '}';
    }

    public void setLike(String[] like) {
        this.like = like;
    }

    /*@Override
    public String toString() {
        return "Arrays{" +
                "like=" + java.util.Arrays.toString(like) +
                '}';
    }*/
}

修改SpringArray.xml配置文件:
不是简单类型,且是数组
需要提前创建好Friend的bean,使用array标签,套ref标签,在bean属性中填Friend的id

	<bean id="arrayBean" class="com.dependency.injection.bean.Arrays">
        <property name="like">
            <array>
                <value></value>
                <value></value>
                <value>rap</value>
            </array>
        </property>
        <property name="friends">
            <array>
                <ref bean="friendBean1"/>
                <ref bean="friendBean2"/>
                <ref bean="friendBean3"/>
            </array>
        </property>
    </bean>
    <bean id="friendBean1" class="com.dependency.injection.bean.Friend">
        <property name="name" value="张三"/>
    </bean>

    <bean id="friendBean2" class="com.dependency.injection.bean.Friend">
        <property name="name" value="李四"/>
    </bean>

    <bean id="friendBean3" class="com.dependency.injection.bean.Friend">
        <property name="name" value="王五"/>
    </bean>

再次运行测试程序:
请添加图片描述

注入集合

注入List集合

创建Collection类

/**
 * 注入集合
 */
public class Collection {
    //注入List集合
    private List<String> names;
    
    public void setNames(List<String> names) {
        this.names = names;
    }

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

创建spring-collection.xml配置:
注入List集合,与注入数组类似,简单类型使用list标签套value标签,非简单类型就套ref标签
这里演示简单类型
List集合:有序可重复

<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
        
        <property name="names">
        	<!--注入List集合-->
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>赵六</value>
                <value>赵六</value>
            </list>
        </property>
    </bean>

测试程序:

    @Test
    public void testCollection(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
        Collection collectionBean = applicationContext.getBean("collectionBean", Collection.class);
        System.out.println(collectionBean);
    }

请添加图片描述

注入Set集合

修改Collection类:

/**
 * 注入集合
 */
public class Collection {
    //注入List集合
    private List<String> names;
    //注入Set集合
    private Set<String> addrs;
    
    @Override
    public String toString() {
        return "Collection{" +
                "names=" + names +
                ", addrs=" + addrs +
                '}';
    }

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

修改spring-collection.xml配置:
注入Set集合,也是与数组差不多,简单类型使用set标签套value标签,非简单类型就套ref标签
这里演示简单类型
Set集合:无序不可重复

<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
        <!--注入List集合-->
        <property name="names">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>赵六</value>
                <value>赵六</value>
            </list>
        </property>
        <!--注入Set集合-->
        <property name="addrs">
            <set>
                <value>上海</value>
                <value>北京</value>
                <value>广州</value>
                <value>广州</value>
            </set>
        </property>                 
    </bean>

再次运行测试程序,发现没有两个广州,无序不可重复
在这里插入图片描述

注入Map集合

修改Collection类:

/**
 * 注入集合
 */
public class Collection {
    //注入List集合
    private List<String> names;
    //注入Set集合
    private Set<String> addrs;
    //注入Map
    private Map<Integer,String> phones;

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

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

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

    
    public void setAddrs(Set<String> addrs) {
        this.addrs = addrs;
    }
}

修改spring-collection.xml配置:
注入Map集合 使用map标签套entry标签

  • 如果是简单类型,则entry标签的属性使用key和value
  • 如果非简单类型,则entry标签的属性使用key-ref和value-ref

key是无序不可重复,value有序可重复
注意:如果key或key-ref没有输入值的话,默认会是null,且只能有一个null

    <!--集合注入-->
    <bean id="collectionBean" class="com.dependency.injection.bean.Collection">
        <!--注入List集合-->
        <property name="names">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>赵六</value>
                <value>赵六</value>
            </list>
        </property>
        <!--注入Set集合-->
        <property name="addrs">
            <set>
                <value>上海</value>
                <value>北京</value>
                <value>广州</value>
                <value>广州</value>
            </set>
        </property>
        <!--注入Map集合-->
        <property name="phones">
            <map>
                <entry key="1" value="110"/>
                <entry key="2" value="111"/>
                <entry key="3" value="119"/>
                <entry key="" value="120"/>
            </map>
        </property>
    </bean>

再次运行程序
请添加图片描述

注入Properties

注入Properties属性类,Properties本质上也是Map集合,
/因为它的父类是一个Hashtbale实现了Map接口。但是注入方式不一样,Properties的key和value的类型只能是String
修改Collection类:

/**
 * 注入集合
 */
public class Collection {
    //注入List集合
    private List<String> names;
    //注入Set集合
    private Set<String> addrs;
    //注入Map
    private Map<Integer,String> phones;

    //注入Properties
    private Properties properties;
    
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    

    @Override
    public String toString() {
        return "Collection{" +
                "names=" + names +
                ", addrs=" + addrs +
                ", phones=" + phones + "\n" +
                ", properties=" + properties +
                '}';
    }
    

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

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

    
    public void setAddrs(Set<String> addrs) {
        this.addrs = addrs;
    }
}

修改spring-collection.xml配置:
注入Properties属性类

  • 使用props标签套prop标签
  • key写在prop标签key的属性里面,value写在prop标签里面

注意:与map不一样key如果没写值,它就不是null,它是真的为空–“”

   <bean id="collectionBean" class="com.dependency.injection.bean.Collection">
        <!--注入List集合-->
        <property name="names">
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
                <value>赵六</value>
                <value>赵六</value>
            </list>
        </property>
        <!--注入Set集合-->
        <property name="addrs">
            <set>
                <value>上海</value>
                <value>北京</value>
                <value>广州</value>
                <value>广州</value>
            </set>
        </property>
        <!--注入Map集合 -->
        <property name="phones">
            <map>
                <entry key="1" value="110"/>
                <entry key="2" value="111"/>
                <entry key="3" value="119"/>
                <entry key="" value="120"/>
            </map>
        </property>
        <!--注入Properties属性类-->
        <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>
                <prop key=""></prop>
            </props>
        </property>
    </bean>

再次运行测试程序:
请添加图片描述

注入null和空字符串

创建Empty类

/**
 * 注入null或空字符串
 */
public class Empty {
    //注入null
    private String name1;
    private String name2;
    
    //注入空字符串
    private String name3;
    private String name4;
    
    

    public void setName1(String name1) {
        this.name1 = name1;
    }

    public void setName2(String name2) {
        this.name2 = name2;
    }

    public void setName3(String name3) {
        this.name3 = name3;
    }

    public void setName4(String name4) {
        this.name4 = name4;
    }

    @Override
    public String toString() {
        return "Empty{" +
                "name1='" + name1 + '\'' +
                ", name2='" + name2 + '\'' +
                ", name3='" + name3 + '\'' +
                ", name4='" + name4 + '\'' +
                '}';
    }
}

创建spring-empty.xml配置:
注入null,两种方式

  • 不给name属性赋值
  • 或者在property里面使用null标签

注意:如果在value里面写一个null,这是赋值一个字符串null,而不是一个真正的null

注入空字符串,两种方式

  • 不给value赋值,默认为空字符串
  • 或者在property里面使用value标签,不给这个标签赋值
    <!--
        注入null或空字符串
    -->

    <bean id="emptyBean" class="com.dependency.injection.bean.Empty">

        <!--注入null,name2不赋值-->
        <property name="name1">
            <null/>
        </property>
        
        <!--注入空字符串-->
        <property name="name3" value=""/>
        <property name="name4">
            <value/>
        </property>

    </bean>

测试程序:

    @Test
    public void testEmpty(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-empty.xml");
        Empty emptyBean = applicationContext.getBean("emptyBean", Empty.class);
        System.out.println(emptyBean);
    }

请添加图片描述

注入特殊字符

XML中有5个特殊字符,分别是:<、>、'、"、&
以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
解决方案包括两种:
第一种方案:特殊符号使用转义字符(实体符号)代替。
第二种方案:使用<![CDATA[]]>,因为放在CDATA区中的数据不会被XML文件解析器解析。它会把里面的内容当中一个普通字符串处理。

注意:使用<![CDATA[]]>,不能在property使用value属性了,只能嵌套一个value标签

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

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

创建MathBean类:

/**
 * 注入特殊字符
 */
public class MathBean {
    private String result1;//使用转义字符
    private String result2;//使用<![CDATA[]]>

    public void setResult1(String result1) {
        this.result1 = result1;
    }

    public void setResult2(String result2) {
        this.result2 = result2;
    }

    @Override
    public String toString() {
        return "MathBean{" +
                "result1='" + result1 + '\'' +
                ", result2='" + result2 + '\'' +
                '}';
    }
}

创建spring-special.xml:

 <!--注入特殊字符-->
    <bean id="mathBean" class="com.dependency.injection.bean.MathBean">
        <property name="result1" value="2 &lt; 3"/>
        <property name="result2">
            <value><![CDATA[2 < 3]]></value>
        </property>
    </bean>

测试程序:

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

请添加图片描述

p命名空间注入

目的:简化配置。
创建P类

/**
 * p命名空间注入
 */
public class P {
    private String name;

    private int age;

    private Date birth;

    @Override
    public String toString() {
        return "P{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birth=" + 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;
    }
}

创建spring-p.xml配置:
p命名空间注入,底层还是set注入,只不过p命名空间注入可以让spring配置变得更加简单。
使用步骤:

  • 第一步:使用p命名空间,需要在beans标签加上一个配置
    xmlns:p=“http://www.springframework.org/schema/p”
  • 第二步:在bean标签使用即可,普通类型使用p:属性名=“属性值”,非简单类型使用p:属性名-ref=“bean的id”
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="pBean" class="com.dependency.injection.bean.P" p:age="20" p:name="张三" p:birth-ref="birthBean"/>
    <bean id="birthBean" class="java.util.Date"/>
</beans>

测试程序:

    @Test
    public void testP(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spting-p.xml");
        P pBean = applicationContext.getBean("pBean", P.class);
        System.out.println(pBean);
    }

请添加图片描述

c命名空间注入

目的:简化配置。
创建C类:

/**
 * c命名空间注入
 */
public class C {
    private String name;
    private int age;
    private Date birth;

    public C(String name, int age, Date birth) {
        this.name = name;
        this.age = age;
        this.birth = birth;
    }

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

创建spring-c.xml配置:
c命名空间注入,底层还是基于构造方法注入的,c命名空间注入是用来简化构造注入的
使用步骤:

  • 第一步:与p命名空间一样,需要在上面beans标签加上一个配置
    xmlns:c=“http://www.springframework.org/schema/c”

  • 第二步:在bean标签使用即可

    • 简单类型:根据下标:c:_下标=“属性值”,根据属性名:c:属性名=“属性值”

    • 非简单类型:根据下标:c:_下标-ref=“属性值”,根据属性名:c:属性名-ref=“属性值”

<?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="cBean" class="com.dependency.injection.bean.C" c:_0="张三" c:age="20" c:birth-ref="birthBean"/>
    <bean id="birthBean" class="java.util.Date"/>
</beans>

测试程序:

    @Test
    public void testC(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
        C cBean = applicationContext.getBean("cBean", C.class);
        System.out.println(cBean);
    }

请添加图片描述

util命名空间

util命名空间注入,主要是让配置复用。
例如写两个数据源,都是同样的配置,就可以使用utils
创建数据源MyDataSource1类:

/**
 * 把数据源交给Spring容器管理
 */
public class MyDataSource1 implements DataSource {
    private Properties properties;

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

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

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

创建数据源MyDataSource2类:

/**
 * 把数据源交给Spring容器管理
 */
public class MyDataSource2 implements DataSource {
    private Properties properties;

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

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

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

创建spring-util.xml配置:

  • 第一步:使用utils命名空间,需要在上面beans标签加上两个配置
xmlns:utils="http://www.springframework.org/schema/util"

xsi里面添加,用来约束xml文件的

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
  • 第二步:配置相关的util标签

  • 第三步:在property里面用ref属性进行复用

<?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="properties">
        <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>
    <!--数据源1-->
    <bean id="ds1" class="com.dependency.injection.jdbc.MyDataSource1">
        <property name="properties" ref="properties"/>
    </bean>

    <!--数据源2-->
    <bean id="ds2" class="com.dependency.injection.jdbc.MyDataSource2">
        <property name="properties" ref="properties"/>
    </bean>
</beans>

测试程序:

    @Test
    public void testUtil(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
        MyDataSource1 ds1 = applicationContext.getBean("ds1", MyDataSource1.class);
        MyDataSource2 ds2 = applicationContext.getBean("ds2", MyDataSource2.class);
        System.out.println(ds1);
        System.out.println(ds2);
    }

请添加图片描述

基于XML的自动装配

Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配。
自动装配也是基于set方式实现的。

根据名称自动装配

还是使用之前的OrderDao和OrderService
创建spring-outowire.xml配置:
根据名称自动装配

  • 第一步:需要在bean标签配置属性autowire=“byName”
  • 第二步:被注入的bean的id就不能随便写了,必须写set方法,去掉set,然后首字母小写。
<bean id="orderDao" class="com.dependency.injection.dao.setDI.OrderDao"/>
<bean id="orderService" class="com.dependency.injection.service.setDI.OrderService" autowire="byName"></bean>

请添加图片描述

根据类型自动装配

还是使用之前的UserDao、UserService

修改spring-outowire.xml:
根据类型自动装配
只需要在bean标签配置属性autowire=“byType”

注意:根据类型自动装配的使用,在有效的配置文件当中,某种类型的实例只能有一个,也就是说某种类型的bean只能有一个。
例如配置以下程序之后会报错:
在这里插入图片描述

因为在之前的配置文件有UserDao的bean对象,Spring“晕了”,不知道哪个bean是需要注入。
把之前的bean对象注释掉即可

	 <!--根据名称自动装配-->
	<bean id="orderDao" class="com.dependency.injection.dao.setDI.OrderDao"/>
    <bean id="orderService" class="com.dependency.injection.service.setDI.OrderService" autowire="byName"></bean>
    <!--根据类型自动装配-->
    <bean class="com.dependency.injection.dao.UserDao"/>   
    <bean id="userDaoBean" class="com.dependency.injection.service.UserService" autowire="byType"/>

测试程序:

    @Test
    public void testOutowrieByType(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-outowire.xml");
        UserService userService = applicationContext.getBean("userDaoBean", UserService.class);
        userService.saveUser();
    }

请添加图片描述

Spring引入外部属性配置文件

引入外部的properties文件
创建数据源MyDataSource3类:

public class MyDataSource3 implements DataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + 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 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文件:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/spring
username=root
password=123456

创建spring-properties.xml配置:
引入外部的properties文件

  • 第一步:引入context命名空间。
xmlns:context="http://www.springframework.org/schema/context"

在xsi引入 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

  • 第二步:使用context:property-placeholder标签location属性的引入,location是默认从类的根路径加载。
<context:property-placeholder location="jdbc.properties"/>
  • 第三步:在property标签的value属性使用${key}进行取值。
<?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">
                           
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="myDataSource" class="com.dependency.injection.jdbc.MyDataSource3">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>
</beans>

测试程序:

    @Test
    public void testProperties(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
        MyDataSource3 myDataSource = applicationContext.getBean("myDataSource", MyDataSource3.class);
        System.out.println(myDataSource);
    }

请添加图片描述
运行后发现username居然不是root,而是shinle,而我的电脑名称也是shinle,是不是很巧。

请添加图片描述
这是因为${}进行加载的时候,Spring默认加载的是电脑的系统环境,所以配置文件一般都会加一个前缀。

jdbc.properties文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123456

spring-properties.xml配置文件改为:

	<context:property-placeholder location="jdbc.properties"/>
    <bean id="myDataSource" class="com.dependency.injection.jdbc.MyDataSource3">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

再次运行:
请添加图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆亦何为

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值