【Spring5】003-IOC容器+基于xml实现Bean管理

1. IOC概念和原理

1.1概念

(1)控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理

(2)使用 IOC 目的:为了降低耦合度

1.2原理

IOC底层原理所用到的技术有三个,分别是:xml 解析、工厂模式、反射
接下来用画流程图的方式进行讲解:

  • 如下图所示,有两个类,分别是UserService和UserDao,UserService中有一个execute()方法,UserDao中有一个add()方法。现在的需求是在UserService类中调用UserDao中的add()方法,下图所示的就是最原始的方式,通过创建对象调方法实现,即在UserService中创建UserDao类的对象userDao,然后利用userDao来调用add()方法。
    这种方式的缺点是耦合度太高了,当UserDao的路径发生变化,UserService也需要跟着变化, 当UserDao中的方法发生变化,UserService也需要跟着变化,不利于项目拓展。
    在这里插入图片描述
  • 为了降低耦合度,可以利用工厂模式,在上面两个类的基础上,创建一个工厂类UserFactory,工厂类中有一个静态的getDao()方法,返回UserDao对象。通过在UserService中用工厂类UserFactory的静态方法来创建UserDao对象并调用UserDao中的方法。这种方法一定程度上降低了UserService和UserDao的耦合度。
    但是同时也存在新的问题,即耦合度出现在工厂类中了,并没有把耦合度降低到最低限度,需要进一步解耦。这个时候就要加上xml解析和反射技术了,三个技术合并在一起就是IOC技术了。
    在这里插入图片描述
  • 基于上述分析,IOC的解耦过程如下:
    第一步: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="dao" class="com.rqs.spring5.utils.UserDao"/>
    
    </beans>
    
    
    第二步:有service类和dao类,创建工厂类。
    package com.rqs.spring5.utils;
    
    /**
    * 伪代码
    */
    public class UserFactory {
       public static UserDao getDao() {
    //        return new UserDao();
           String classValue = class属性值;//1.利用xml解析获得class属性的值"com.rqs.spring5.utils.UserDao",
           Class clazz = Class.forName(classValue);//2.通过反射创建对象
           return (UserDao) clazz.newInstance();
       }
    }
    
    

2.IOC接口

  1. IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

  2. Spring 提供 IOC 容器实现的两种方式:(两个接口)

    • BeanFactory:IOC 容器的基本实现,是 Spring 内部的接口,一般不提供给开发人员进行使用(但是也可以用)

      加载配置文件时不会创建对象,在获取对象(使用)才去创建对象

    • ApplicationContextBeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

      加载配置文件时就会创建配置文件中的对象

  3. ApplicationContext 接口有实现类,所框出来的两个是主要的实现类,其中ClassPathXmlApplicationContext在写入门案例的时候已经使用过,这个实现类中是通过配置文件的类路径来写的参数(比如:ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");),而FileSystemXmlApplicationContext中的参数需要写的是配置文件在系统盘符的全路径。
    在这里插入图片描述
    在这里插入图片描述

3.传统方法中向属性中注入值的方式

举例:向入门案例中User类的userName中注入值
(1)可以用set方法

package com.rqs.spring5;

import java.awt.print.Book;

public class User {
   private String userName;

   public void setUserName(String userName) {
       this.userName = userName;
   }

   public void add() {
       System.out.println("add");
   }

   public static void main(String[] args) {
       User user = new User();
       user.setUserName("abc");
   }
}

(2)通过有参数的构造器

package com.rqs.spring5;

public class User {
   private String userName;

   public User(String userName) {
       this.userName = userName;
   }

   public void add() {
       System.out.println("add");
   }

   public static void main(String[] args) {
       User user = new User("abc");

   }
}

4.什么是Bean管理

Bean管理指的是两个操作:
(1)Spring创建对象
(2)Spring注入属性

4.1.基于xml实现Bean管理

4.1.1Spring创建对象:基于xml方式

  1. 在spring配置文件中,使用bean标签,标签中添加对应属性,就可以实现对象的创建
  2. 在bean标签中有很多属性,常用的属性有:
    • id属性:对象的唯一标识
    • class属性:对象的类全路径
    • name:作用同id,但是id中不能加特殊符号,name中可以,比如加斜杠符号
      在这里插入图片描述
  3. 创建对象的时候,默认执行无参数的构造方法

    所以,当在入门案例中加上有参构造器之后执行代码,就会报错
    在这里插入图片描述
    在这里插入图片描述

4.1.2.Spring注入属性:基于xml方式

4.1.2.1.DI概念介绍

DI,依赖注入,其实就是注入属性,它是IOC中的一种具体实现,需要在创建对象的基础上完成

4.1.2.2.第一种xml注入方式:使用 set 方法进行注入

(1)创建类,定义属性和对应的 set 方法
User类代码:

package com.rqs.spring5;

/**
 * 演示使用set方法进行属性的注入
 */
//创建类
public class User {
    //创建属性
    private String userName;

    //创建属性对应的set方法
    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void add() {
        System.out.println(userName);
    }

}

(2)在 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="user" class="com.rqs.spring5.User">
        <!--使用property完成属性注入
            name:类里面的属性名称
            value:向属性中注入的值
        -->
        <property name="userName" value="不可不说的宋朝史"></property>
    </bean>

</beans>

进行测试:
TestSpring5 代码:

package com.rqs.spring5.testdemo;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5 {
    @Test
    public void testAdd() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象
        User user = context.getBean("user", User.class);//user跟配置文件中的id要保持一致 <bean id="user" class="com.rqs.spring5.User"/>
        user.add();

    }
}

项目结构如下:
在这里插入图片描述

4.1.2.2.1 p名称空间注入(了解)

可以简化基于xml的set方法注入属性,不使用property就可以完成属性注入。
第一步:在配置文件中添加 p 名称空间
在这里插入图片描述
第二步:不使用property,在 bean 标签里用p:userName="不可不说的宋朝史"的形式进行进行属性注入
在这里插入图片描述

4.1.2.3.第二种xml注入方式:使用有参数构造进行注入

(1)创建类,定义属性,创建属性对应有参数构造方法
User类代码:

package com.rqs.spring5;

/**
 * 演示使用有参构造器方法进行属性的注入
 */
//创建类
public class User {
    //创建属性
    private String userName;

    //创建有参构造器
    public User(String userName) {
        this.userName = userName;
    }

    public void add() {
        System.out.println(userName);
    }

}

(2)在 spring 配置文件中进行配置

配置时要注意在上文中提到的,创建对象的时候,默认执行无参数的构造方法,如果写了有参构造器就会报错,例如配置文件中的这段代码默认使用午餐构造器创建对象,因此在写了有参构造器的时候配置文件报错
在这里插入图片描述
需要在bean标签中写上constructor-arg参数,才可以利用有参构造器的方式注入
配置文件代码:

<?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="user" class="com.rqs.spring5.User">
        <constructor-arg name="userName" value="rqs"></constructor-arg>
    </bean>

</beans>

进行测试:
TestSpring5 代码:

package com.rqs.spring5.testdemo;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5 {
    @Test
    public void testAdd() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象
        User user = context.getBean("user", User.class);//user跟配置文件中的id要保持一致 <bean id="user" class="com.rqs.spring5.User"/>
        user.add();

    }
}

项目结构如下:
在这里插入图片描述

4.1.3xml注入其他类型属性

以Book类为例进行介绍:

package com.rqs.spring5;

public class Book {
    //创建属性
    private String bname;
    private String bauthor;
    private String address;
    //创建属性对应的set方法
    public void setBname(String bname) {
        this.bname = bname;
    }

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    public void testDemo() {
        System.out.println(bname+"::"+bauthor+"::"+address);
    }
}

测试代码如下:

package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5 {
    
    @Test
    public void testBook1() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        //获取配置创建的对象
        Book book = context.getBean("book", Book.class);
        System.out.println(book);
        book.testDemo();
    }
}
4.1.3.1字面量(直接量)

字面量是指在程序中通过源代码直接给出的值,例如在int a = 5;代码中,为变量 a 所分配的初始值 5 就是一个字面量。
①null值
在配置文件中的property里写上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"
       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="book" class="com.rqs.spring5.Book">
        <!--设置一个null值-->
        <property name="bname">
            <null></null>
        </property>
        <property name="bauthor" value="元朝史"></property>
        <property name="address" value="图书馆"></property>
    </bean>

</beans>

测试结果:
在这里插入图片描述
②属性中包含特殊符号
属性中包含特殊符号,比如<<>>,会被当成标签符号,直接写会报错
在这里插入图片描述
可以用如下方式解决:

<?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="book" class="com.rqs.spring5.Book">
        <!--设置一个null值-->
        <property name="bname">
            <null></null>
        </property>
        <!--属性中包含特殊符号,比如<<>>,直接写会报错
            解决方式1:把<>进行转义,用&lt;&gt;代替
            解决方式2:把<>写入CDATA,即在<![CDATA[  ]]>里面的中括号写上具体内容,如下所展示的代码
        -->
        <property name="bauthor">
            <value><![CDATA[<<元朝史>>]]></value>
        </property>

        <property name="address" value="图书馆"></property>
    </bean>

</beans>

测试结果:
在这里插入图片描述

4.1.3.2注入属性-外部bean

讲解:在实际操作中,会出现web层调用service层,service层调用dao层的情况,通过service层调用dao层,这个过程就叫做引入外部bean
原始方法过程举例:
(1)创建service和dao类
在这里插入图片描述
UserService类:其中创建UserDao对象和调用方法的过程可以用Spring配置的方式实现

package com.rqs.spring5.service;

import com.rqs.spring5.dao.UserDao;
import com.rqs.spring5.dao.UserDaoImpl;

public class UserService {
    public void add() {
        System.out.println("service add ...........");
        //创建UserDao对象
        UserDao userDao = new UserDaoImpl();
        //调用方法
        userDao.update();
    }
}


UserDao接口:

package com.rqs.spring5.dao;

public interface UserDao {
    public void update();
}

接口实现类UserDaoImpl:

package com.rqs.spring5.dao;

public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("dao update .........");
    }
}

用Spring配置的方式实现:以set方式注入为例
第一步:在service中创建dao类型的对象属性,生成对应的set方法
在配置文件中创建两个类的对象
第二步:在service中注入dao对象
UserService类:

package com.rqs.spring5.service;

import com.rqs.spring5.dao.UserDao;
import com.rqs.spring5.dao.UserDaoImpl;

public class UserService {
    //创建UserDao类型的对象属性,生成对应的set方法
    private UserDao userDao;

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

    public void add() {
        System.out.println("service add ...........");
        userDao.update();//方法调用
    }
} 

UserDao接口:

package com.rqs.spring5.dao;

public interface UserDao {
    public void update();
}

接口实现类UserDaoImpl:

package com.rqs.spring5.dao;

public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("dao update .........");
    }
}

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">
    <!--配置service和dao的对象-->
    <bean id="userService" class="com.rqs.spring5.service.UserService">
        <!--注入userDao对象 
            name属性:类里面的属性名称
            ref属性:创建的外部userDao对象bean标签的id值
        -->
        <property name="userDao" ref="userDao"></property>
    </bean>
    <!--创建外部userDao对象-->
    <bean id="userDao" class="com.rqs.spring5.dao.UserDaoImpl"></bean>
</beans>

测试代码:

package com.rqs.spring5.testdemo;

import com.rqs.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBean {
    @Test
    public void testAdd() {
        //1.加载Spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        //2.获取配置创建的对象
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

}

测试结果:
在这里插入图片描述

4.1.3.3注入属性-内部bean

举例解释:
(1)数据库中表与表之间有不同的关系,比如一对一,一对多。
一对多关系:部门和员工
一个部门里可以有多个员工,一个员工属于一个部门,部门是1,员工是多。
在实体类之间表示一对多的关系。
Dept类:

package com.rqs.spring5.bean;

public class Dept {
    private String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }

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

员工类:

package com.rqs.spring5.bean;

public class Emp {

    private String ename;
    private String gender;

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
    public void add() {
        System.out.println("Emp{" +
                "ename='" + ename + '\'' +
                ", gender='" + gender + '\'' +
                '}');
    }
}

(2)目前两个类之间没有关系,通过在员工类中新增部门对象属性的方式可以表示二者之间的关系,将员工类写成如下形式:
改写员工类:

package com.rqs.spring5.bean;

public class Emp {

    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void add() {
        System.out.println("Emp{" +
                "ename='" + ename + '\'' +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}');
    }
}

(3)然后通过配置文件进行相关配置

<?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="emp" class="com.rqs.spring5.bean.Emp">
        <!--设置普通属性-->
        <property name="ename" value="张三"></property>
        <property name="gender" value=""></property>
        <!--设置对象类型的属性-->
        <property name="dept">
            <!--创建内部的dept对象-->
            <bean id="dept" class="com.rqs.spring5.bean.Dept">
                <property name="dname" value="安保部"></property>
            </bean>
        </property>
    </bean>

</beans>

测试代码:

package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import com.rqs.spring5.bean.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5 {

    @Test
    public void testBean() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        //获取配置创建的对象
        Emp emp = context.getBean("emp", Emp.class);
        emp.add();
    }
}

测试结果:
在这里插入图片描述

4.1.3.4注入属性-级联赋值

(1)级联赋值:第一种写法

<?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="emp" class="com.rqs.spring5.bean.Emp">
        <!--设置普通属性-->
        <property name="ename" value="张三"></property>
        <property name="gender" value=""></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.rqs.spring5.bean.Dept">
        <property name="dname" value="财务部"></property>
    </bean>
</beans>

(2)级联赋值:第二种写法

```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="emp" class="com.rqs.spring5.bean.Emp">
        <!--设置普通属性-->
        <property name="ename" value="张三"></property>
        <property name="gender" value=""></property>
        <!--级联赋值-->
        <property name="dept" ref="dept"></property>
        <property name="dept.dname" value="技术部"></property>
    </bean>
    <bean id="dept" class="com.rqs.spring5.bean.Dept">
        <property name="dname" value="财务部"></property>
    </bean>
</beans>```

在这里插入图片描述

注意到这样写会报错,需要在员工类中设置dept对象类型属性的get方法,如下

package com.rqs.spring5.bean;

public class Emp {

    private String ename;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void add() {
        System.out.println("Emp{" +
                "ename='" + ename + '\'' +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}');
    }
}

加了get方法之后就没有报错:
在这里插入图片描述
测试代码:

package com.rqs.spring5.testdemo;
import com.rqs.spring5.Book;
import com.rqs.spring5.User;
import com.rqs.spring5.bean.Emp;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring5 {

    @Test
    public void testBean() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        //获取配置创建的对象
        Emp emp = context.getBean("emp", Emp.class);
        emp.add();
    }
}

测试结果:
在这里插入图片描述

4.1.3.5注入属性-集合
  1. 注入数组类型的属性

  2. 注入List集合类型属性

  3. 注入Map集合类型属性

  4. 注入Set集合类型属性

    项目结构如下:

    在这里插入图片描述

通过创建一个Stu类进行举例,其中包含了数组、List、Map、Set类型属性,并且生成了各属性对应的set方法,创建了一个test方法,如下所示:

Stu类:

package com.rqs.spring5.collectiontype;

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

public class Stu {
    //1.数组类型属性
    private String[] course;
    //2.list集合类型属性
    private List<String> list;
    //3.map集合类型属性
    private Map<String, String> maps;
    //4.set集合类型属性
    private Set<String> sets;

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }

    public void setCourse(String[] course) {
        this.course = course;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void test() {
        System.out.println(Arrays.toString(course));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(sets);
    }
}

在Spring配置文件中进行配置:

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="stu" class="com.rqs.spring5.collectiontype.Stu">
        <!--数组类型属性注入-->
        <property name="course">
            <array>
                <value>java课程</value>
                <value>mysql课程</value>
                <value>spring5课程</value>
            </array>
        </property>
        <!--List类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
                <value>三三</value>
            </list>
        </property>
        <!--Map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="MYSQL" value="mysql"></entry>
                <entry key="SPRING" value="spring"></entry>
            </map>
        </property>
        <!--Set类型属性注入-->
        <property name="sets">
            <set>
                <value>HTML</value>
                <value>CSS</value>
                <value>JS</value>
            </set>
        </property>
    </bean>
</beans>

进行测试

TestSpring5Demo1类:

package com.rqs.spring5.testdemo;

import com.rqs.spring5.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class TestSpring5Demo1 {
    @Test
    public void testCollection() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Stu stu = context.getBean("stu", Stu.class);
        stu.test();

    }
}

测试结果如下:

在这里插入图片描述

两个细节问题
1.在集合中设置对象类型的值

项目结构如下:

在这里插入图片描述

以上的例子中,集合中的属性都是String类型(比如List集合类型属性List,泛型指定为String),那如果集合中的属性是对象类型呢?

为了在集合中设置对象类型的属性,新建一个实体类Course并重写toString方法,如下所示:

Course类:

package com.rqs.spring5.collectiontype;

public class Course {
    private String cname;

    public void setCname(String cname) {
        this.cname = cname;
    }

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

在原先的Stu类中,新增Course类型的属性,修改test方法:

Stu类(修改版):

package com.rqs.spring5.collectiontype;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Stu {
    //1.数组类型属性
    private String[] course;
    //2.list集合类型属性
    private List<String> list;
    //3.map集合类型属性
    private Map<String, String> maps;
    //4.set集合类型属性
    private Set<String> sets;
    // 学生所学习的多门课程
    private List<Course> courseList;

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }

    public void setCourse(String[] course) {
        this.course = course;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void test() {
        System.out.println(Arrays.toString(course));
        System.out.println(list);
        System.out.println(maps);
        System.out.println(sets);
        System.out.println(courseList);
    }
}

在配置文件中进行配置,新增在集合中设置对象类型值的配置:

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="stu" class="com.rqs.spring5.collectiontype.Stu">
        <!--数组类型属性注入-->
        <property name="course">
            <array>
                <value>java课程</value>
                <value>mysql课程</value>
                <value>spring5课程</value>
            </array>
        </property>
        <!--List类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
                <value>三三</value>
            </list>
        </property>
        <!--Map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="MYSQL" value="mysql"></entry>
                <entry key="SPRING" value="spring"></entry>
            </map>
        </property>
        <!--Set类型属性注入-->
        <property name="sets">
            <set>
                <value>HTML</value>
                <value>CSS</value>
                <value>JS</value>
            </set>
        </property>
        <!--注入List类型集合,其值为对象类型-->
        <property name="courseList">
            <list>
                <!--引入的是下面所创建的多个bean对象:course1和course2-->
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>
    <!--创建多个Course对象-->
    <bean id="course1" class="com.rqs.spring5.collectiontype.Course">
        <property name="cname" value="Java基础课程"></property>
    </bean>
    <bean id="course2" class="com.rqs.spring5.collectiontype.Course">
        <property name="cname" value="Mysql课程"></property>
    </bean>
</beans>

测试类不变:

TestSpring5Demo1类:

package com.rqs.spring5.testdemo;

import com.rqs.spring5.collectiontype.Stu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class TestSpring5Demo1 {
    @Test
    public void testCollection() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Stu stu = context.getBean("stu", Stu.class);
        stu.test();

    }
}

测试结果如下:

在这里插入图片描述

2.将集合注入部分提取出来

项目结构如下:

之前的例子中,所注入的集合属性只能在Stu这个类中进行使用,别的类无法使用,如果不同实体类中的集合属性是一样的,那就可以将集合部分提取出来作为公共部分,让所有实体类都能引入这个集合。

为了演示将集合注入部分提取出出来,新建一个book类:

Book类:

package com.rqs.spring5.collectiontype;

import java.util.List;

public class Book {
    private List<String> list;

    public void setList(List<String> list) {
        this.list = list;
    }

    public void test() {
        System.out.println(list);
    }
}

若要把Book类中的list属性作为一个公共的部分,让其他实体类也可以使用,首先,需要在配置文件中引入util名称空间(名称空间这个词,在“4.1.2.2.1 p名称空间注入(了解)”这个章节中有出现过p名称空间),如下图所示。然后,需要使用util名称空间完成集合注入部分的提取,见配置文件代码。
image-20221210211639041

bean1.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: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">
    <!--1.提取list集合类型属性的注入-->
    <util:list id="bookList"><!--id设置为bookList,可供后续使用-->
        <!--<ref bean=""></ref>-->
        <value>一读就懂的中国史</value>
        <value>明朝那些事</value>
        <value></value>
    </util:list>

    <!--2.提取list集合类型属性的注入后,用如下方式使用-->
    <bean id="book" class="com.rqs.spring5.collectiontype.Book">
        <property name="list" ref="bookList"></property>
    </bean>
</beans>

测试方法:

TestSpring5Demo1类:

package com.rqs.spring5.testdemo;

import com.rqs.spring5.collectiontype.Book;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class TestSpring5Demo1 {
    @Test
    public void testCollection() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Book book = context.getBean("book", Book.class);
        book.test();

    }
}

测试结果如下:
在这里插入图片描述

4.1.4 FactoryBean

项目结构如下:
在这里插入图片描述

说明:

  • Spring有两种类型的bean,一种是自己创建的普通的bean,另外一种是工厂bean(或者叫FactoryBean)

  • 普通bean,在配置文件中,bean定义的是什么类型,返回就必须是什么类型,比如下图中bean定义的是Book类型,就必须返回Book类型
    在这里插入图片描述

  • 工厂bean,在配置文件中定义的bean类型可以和返回类型不一样,演示如下

    • 第一步:创建类,实现接口FactoryBean,让这个类作为工厂bean

    • 第二步:实现接口中的方法,在实现的方法中定义返回的bean类型

      MyBean类:

      package com.rqs.spring5.factorybean;
      
      import com.rqs.spring5.collectiontype.Course;
      import org.springframework.beans.factory.FactoryBean;
      
      public class MyBean implements FactoryBean<Course>{
      
          //定义返回bean类型
          @Override
          public Course getObject() throws Exception {
              Course course = new Course();
              course.setCname("中国史纲");
              return course;
          }
      
          @Override
          public Class<?> getObjectType() {
              return null;
          }
      
          @Override
          public boolean isSingleton() {
              return FactoryBean.super.isSingleton();
          }
      }
      

      配置文件:
      bean2.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:util="http://www.springframework.org/schema/util"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
          <bean id="myBean" class="com.rqs.spring5.factorybean.MyBean">
          </bean>
      </beans>
      

      测试类:

      package com.rqs.spring5.testdemo;
      
      import com.rqs.spring5.collectiontype.Course;
      import com.rqs.spring5.factorybean.MyBean;
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      
      public class TestSpring5Demo1 {
          @Test
          public void testCollection() {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
      //        MyBean myBean = context.getBean("myBean", MyBean.class);
              Course course = context.getBean("myBean", Course.class);
              System.out.println(course);
      
          }
      }
      

      测试结果:
      在这里插入图片描述
      如下图所示,虽然在配置文件中定义的是MyBean类型
      在这里插入图片描述
      但是,最后返回的结果是Course类型。

4.1.5 bean作用域

  1. 在Spring中,可以设置bean实例是单实例还是多实例

  2. 在Spring中,默认情况下,所创建的bean是一个单实例对象

    对第2点做出验证,验证如下:

    如下图所示,在Spring配置文件中,为Book类做了一个bean的配置。
    在这里插入图片描述 所以,在如下图所示的测试文件中,在得到book1和book2对象后,可以分别输出book1和book2对象。在这里插入图片描述

    默认情况下,如果Spring所创建的对象是单实例对象,那么输出的两个实例对象的地址是同一个地址,如果Spring所创建的对象是多实例对象,那么输出的两个实例对象的地址补是同一个地址。 如下图所示,经过验证,输出的两个实例对象的地址是同一个地址,所以在默认情况下,所创建的bean是一个单实例对象,但是可以将其设置为多实例对象。在这里插入图片描述

  3. 设置Spring所创建对象是单实例对象还是多实例对象的方法。

    在Spring配置文件的bean标签中有一个属性(scope)用于设置单实例或者多实例。

    如下图所示,scope常用的属性值有两个:第一个是默认值singleton,表示是单实例对象;第二个值是 prototype,表示是多实例对象。在这里插入图片描述
    如下图所示,如果设置为多实例对象后,之前的输出的两个实例对象的地址现在不是同一个地址了在这里插入图片描述
    在这里插入图片描述

  4. 设置scope值为singleton时,在加载Spring配置文件的时候就会创建单实例对象

  5. 设置scope值为singleton时,不是在加载Spring配置文件的时候就会创建对象,而是在调用getBean方法时创建多实例对象
    在这里插入图片描述
    演示 bean的 生命周期:

    项目结构目录:

    在这里插入图片描述

    创建一个Orders实体类:

    package com.rqs.spring5.bean;
    
    public class Orders {
        //为了便于展示bean的生命周期,写出无参数构造
        public Orders() {
            System.out.println("第一步 执行无参数构造创建bean实例");
        }
        private String oname;
    
        public void setOname(String oname) {
            this.oname = oname;
            System.out.println("第二步 调用set方法设置属性值");
        }
    
        //需要自己创建初始化的方法
        //这是一个普通的方法默认不会执行,需要通过配置才能让这个方法执行
        //通过配置bean的属性init-method来配置,例如: init-method="initMethod"
        public void initMethod() {
            System.out.println("第三步 执行初始化的方法");
        }
    
        //创建执行的销毁的方法
        public void destroyMethod() {
            System.out.println("第五步 执行销毁的方法");
        }
    }
    

    配置bean及其id、class、init-method、destroy-method方法:

    <?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">
        <bean id="orders" class="com.rqs.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
            <property name="oname" value="手机"></property>
        </bean>
    </beans>
    

    测试方法:

    package com.rqs.spring5.testdemo;
    
    import com.rqs.spring5.bean.Orders;
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    public class TestSpring5Demo1 {
    
        @Test
        public void testBean4() {
            /*ApplicationContext context =
                    new ClassPathXmlApplicationContext("bean4.xml");*/
            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("bean4.xml");
            Orders orders = context.getBean("orders", Orders.class);
            System.out.println("第四步 利用getBean()方法获取创建的Bean实例对象");
            System.out.println(orders);
            //手动让bean实例销毁
            context.close();
        }
    
    }
    

    测试结果:
    在这里插入图片描述

4.1.6.1 bean后置处理器

bean的后置处理器,bean生命周期其实有七步,初始化前后各有一个方法:

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递bean后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  5. 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
  6. bean 可以使用了(对象获取到了)
  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

演示添加后置处理器效果:
项目结构:
在这里插入图片描述

  1. 创建类,实现接口 BeanPostProcessor,创建后置处理器(Spring在类实现接口BeanPostProcessor之后就会把该类当做后置处理器进行执行,后置处理器会为配置文件中的所有bean都添加后置处理器的处理,即若有两个bean:book和orders,二者都会被添加后置处理器的处理)
    MyBeanPost类:

    package com.rqs.spring5.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.lang.Nullable;
    import org.springframework.util.SocketUtils;
    
    public class MyBeanPost implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之前执行的方法");
            return bean;
        }
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之后执行的方法");
            return bean;
        }
    }
    
  2. 配置后置处理器
    bean4.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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <bean id="orders" class="com.rqs.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
            <property name="oname" value="手机"></property>
        </bean>
        <!--配置后置处理器,供上面的bean使用-->
        <bean id="myBeanPost" class="com.rqs.spring5.bean.MyBeanPost"></bean>
    </beans>
    
  3. 测试方法

    package com.rqs.spring5.testdemo;
    
    import com.rqs.spring5.bean.Orders;
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    public class TestSpring5Demo1 {
    
        @Test
        public void testBean4() {
            /*ApplicationContext context =
                    new ClassPathXmlApplicationContext("bean4.xml");*/
            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("bean4.xml");
            Orders orders = context.getBean("orders", Orders.class);
            System.out.println("第四步 利用getBean()方法获取创建的Bean实例对象");
            System.out.println(orders);
            //手动让bean实例销毁
            context.close();
        }
    
    }
    

    测试结果:
    在这里插入图片描述

4.1.6.2 xml自动装配
  1. 什么是自动装配?

    在之前的演示中,可以通过配置文件中的标签为实体类注入属性,这种方式叫做手动装配。

    而自动装配中Spring可以根据指定的装配规则(属性名称或者属性类型)自动将匹配的属性值进行注入。

  2. 演示自动装配过程

    项目结构:
    在这里插入图片描述

    Emp员工类:

    package com.rqs.spring5.autowire;
    
    public class Emp {
        private Dept dept;
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "dept=" + dept +
                    '}';
        }
    
        public void test() {
            System.out.println(dept);
        }
    }
    

    Dept部门类:

    package com.rqs.spring5.autowire;
    
    public class Dept {
        @Override
        public String toString() {
            return "Dept{}";
        }
    }
    

    bean5.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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <!--实现自动装配
        bean标签中有一个属性:autowire,通过它配置自动装配
        autowire中比较常用的属性值是byName和byType,分别是根据属性名称和属性类型来注入。
        byName要求:注入bean的id属性的值和实体类中的属性名要一致。
        -->
        <bean id="emp" class="com.rqs.spring5.autowire.Emp" autowire="byName">
            <!--自动装配不需要写property-->
            <!--<property name="dept" ref="dept"></property>-->
        </bean>
        <bean id="dept" class="com.rqs.spring5.autowire.Dept"></bean>
    </beans>
    

    测试类:

    package com.rqs.spring5.testdemo;
    
    import com.rqs.spring5.autowire.Emp;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    public class TestSpring5Demo1 {
    
        @Test
        public void test4() {
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("bean5.xml");
            Emp emp = context.getBean("emp", Emp.class);
            System.out.println(emp);
    
        }
    
    }
    

    测试结果显示,没有用到property属性来注入属性,但是属性依然被注入了,自动装配成功:
    在这里插入图片描述
    上面演示的xml配置文件中是根据属性名称的规则来进行自动装配的,如果是根据属性类型来进行自动装配,则autowire的属性值要改为byType。如下图所示,如果是通过属性类型自动装配,则会通过实体类Emp的属性dept的类型Dept,在配置文件中找到Dept对象来进行自动装配。


    在这里插入图片描述
    另外,在实际情况中,一般不用xml自动装配,而是利用注解的方式进行注入。

4.1.6.3引入外部属性文件

应用场景:

在之前的例子中,在配置文件中创建了bean对象之后,如果一个类的属性特别多,在bean中需要写入很多property。这样写不是很方便,特别是当属性值发生变化的时候,需要修改配置文件,而配置文件中可能还要其它的配置,所以改起来不是很方便。

所以,可以把一些固定的属性值或者相关的值放到其他文件中,然后将其引入到配置文件中。

例如,数据库的一些属性可以写在properties文件中,然后引入到配置文件中读取properties文件的内容进行注入。

演示:

方式一:直接配置数据库信息

  1. 配置德鲁伊连接池
  2. 引入德鲁伊连接池的jar包image-20221211134804365,方法同【Spring5】002-入门案例中jar包的导入。
    在这里插入图片描述

方式二:通过引入外部属性文件配置数据库

  1. 创建properties 格式的外部属性文件,写入数据库信息
    在这里插入图片描述
  2. 把外部 properties 属性文件引入到 spring 配置文件中
    需要先引入context名称空间
    在这里插入图片描述
    在 spring 配置文件使用标签引入外部属性文件
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值