• 02 Spring_IOC 控制反转


    目录

    一、SpringIOC_控制反转思想

     1.IOC介绍:

    2.上述写法中有两个缺点:

    二、SpringIOC_自定义对象容器

    注意:(不需要记代码,只是模拟IOC思想,帮助理解)

     1.创建实体类

    2.创建Dao接口和实现类

    3.创建配置文件bean.properties,该文件中定义管理的对象

    4.创建容器管理类,该类在类加载时读取配置文件,将配置文件中配置的对象全部创建并放入容器中。

    5.创建Dao对象的调用者StudentService

    6.测试StudentService

    7.测试结果

    8.测试结论:

    三、SpringIOC_Spring实现IOC

     1.创建Maven工程,引入依赖

    2.创建POJO类、Dao类和接口

    3.编写xml配置文件,配置文件中配置需要Spring帮我们创建的对象

    4.测试从Spring容器中获取对象

    5.测试结果

    四、SpringIOC_Spring容器类型

     1.容器接口(BeanFactory、ApplicationContext)

    2.ApplicationContext有以下三个常用实现类:

    3.代码演示如:

    五、SpringIOC_对象的创建方式

    1.使用无参构造方法

    2.使用工厂类的普通方法构建对象

    3.使用工厂类的静态方法构建对象

    4.知识点整理:

    六、SpringIOC_对象的五种创建策略

    1.singleton:

    2.prototype:

    七、SpringIOC_对象的销毁时机:

     对象的创建策略不同,销毁时机也不同:

    八、SpringIOC_声明周期方法

    1.定义声明周期方法

    2.配置声明周期方法

    3.测试方法

    4.测试结果

     5.知识点整理:

    九、SpringIOC_获取Bean对象的方式

    1.通过id/name获取

    2.通过类型获取

    3.通过 id/name+类型 获取

    4.整体代码段:


    一、SpringIOC_控制反转思想

     1.IOC介绍:

    IOC(Inversion of Control) :程序将创建对象的权利交给框架。

    之前在开发过程中,对象实例的创建是由调用者管理的,代码如下:
    1. public interface StudentDao {
    2.    // 根据id查询学生
    3.    Student findById(int id);
    4. }
    5. public class StudentDaoImpl implements StudentDao{
    6.    @Override
    7.    public Student findById(int id) {
    8.        // 模拟从数据库查找出学生
    9.        return new Student(1,"百战程序员","北 京");
    10.   }
    11. }
    12. public class StudentService {
    13.    public Student findStudentById(int id){
    14. // 此处就是调用者在创建对象
    15.        StudentDao studentDao = new StudentDaoImpl();
    16.        return studentDao.findById(1);
    17.   }
    18. }

    2.上述写法中有两个缺点:

    (1)浪费资源: StudentService 调用方法时即会创建一个对象,如果不断调用方法则会创建大量StudentDao 对象。
    (2)代码耦合度高:假设随着开发,我们创建了 StudentDao 另一个更加完善的实现类StudentDaoImpl2 ,如果在 StudentService中想使用StudentDaoImpl2 ,则必须修改源码。

    IOC思想是将创建对象的权利交给框架,框架会帮助我们创建对象,分配对象的使用,控制权由程序代码转移到了框架中,控制权发生了反转,这就是SpringIOC 思想。 IOC 思想可以完美的解决以上两个问题。

    二、SpringIOC_自定义对象容器

    注意:(不需要记代码,只是模拟IOC思想,帮助理解)

    接下来我们通过一段代码模拟 IOC 思想。创建一个集合容器,先将对象创建出来放到容器中,需要使用对象时,只需要从容器中获取对象即可,而不需要重新创建,此时容器就是对象的管理者。

     1.创建实体类

    1. public class Student {
    2.    private int id;
    3.    private String name;
    4.    private String address;
    5. // 省略getter/setter/构造方法/tostring
    6. }

    2.创建Dao接口和实现类

    1. public interface StudentDao {
    2.    // 根据id查询学生
    3.    Student findById(int id);
    4. }
    1. public class StudentDaoImpl implements StudentDao{
    2.    @Override
    3.    public Student findById(int id) {
    4.        // 模拟从数据库查找出学生
    5.        return new Student(1,"百战程序员","北京");
    6.   }
    7. }
    1. public class StudentDaoImpl2 implements StudentDao{
    2.    @Override
    3.    public Student findById(int id) {
    4.        // 模拟根据id查询学生
    5.        System.out.println("新方法!!!");
    6.        return new Student(1,"百战程序员","北京");
    7.   }
    8. }

    3.创建配置文件bean.properties,该文件中定义管理的对象

    studentDao=com.itbaizhan.dao.StudentDaoImpl

    4.创建容器管理类,该类在类加载时读取配置文件,将配置文件中配置的对象全部创建并放入容器中。

    1. //容器类,负责管理对象,在类加载时读取配置文件并创建对象
    2. public class Container {
    3. static Map map = new HashMap();
    4. // 读取配置文件并创建对象
    5. static {
    6. InputStream is = Container.class.getClassLoader().getResourceAsStream("bean.properties");
    7. Properties properties = new Properties();
    8. try{
    9. properties.load(is);
    10. }catch (IOException e) {
    11. e.printStackTrace();
    12. }
    13. // 遍历配置文件中的所有对象
    14. Enumeration keys = properties.keys();
    15. while (keys.hasMoreElements()){
    16. String key = keys.nextElement().toString();
    17. String value = properties.getProperty(key);
    18. // 创建对象
    19. try {
    20. Object o = Class.forName(value).newInstance();
    21. // 把对象放入集合当中
    22. map.put(key,o);
    23. } catch (InstantiationException e) {
    24. e.printStackTrace();
    25. } catch (IllegalAccessException e) {
    26. e.printStackTrace();
    27. } catch (ClassNotFoundException e) {
    28. e.printStackTrace();
    29. }
    30. }
    31. }
    32. // 从容器中获取对象
    33. public static Object getBean(String key){
    34. return map.get(key);
    35. }
    36. }
    37. 5.创建Dao对象的调用者StudentService

      1. public class StudentService {
      2. public Student findStudentById(int id){
      3. // 从容器中获取对象
      4. StudentDao studentDao = (StudentDao) Container.getBean("studentDao");
      5. System.out.println(studentDao.hashCode());
      6. return studentDao.findById(id);
      7. }
      8. }

      6.测试StudentService

      1. public class Test {
      2. public static void main(String[] args) {
      3. StudentService studentService = (StudentService) Container.getBean("studentService");
      4. System.out.println(studentService.findStudentById(1));
      5. System.out.println(studentService.findStudentById(1));
      6. }
      7. }

      7.测试结果

      8.测试结论:

      (1)StudentService从容器中每次拿到的都是同一个StudentDao对象,节约了资源。

      studentDao=com.itbaizhan.dao.StudentDaoImpl2
      (2)如果想使用StudentDaoImpl2 对象,只需要修改bean.properties的内容即可,无需修改Java 源码。

       (3)在IOC思想中,将创建好的对象保存到“容器中”

       

      三、SpringIOC_Spring实现IOC

       接下来我们使用Spring实现IOCSpring内部也有一个容器用来管理对象。

       1.创建Maven工程,引入依赖

      1. <dependencies>
      2. <dependency>
      3. <groupId>org.springframeworkgroupId>
      4. <artifactId>spring-contextartifactId>
      5. <version>5.3.13version>
      6. dependency>
      7. <dependency>
      8. <groupId>junitgroupId>
      9. <artifactId>junitartifactId>
      10. <version>4.12version>
      11. <scope>testscope>
      12. dependency>
      13. dependencies>

      2.创建POJO类、Dao类和接口

      1. public class Student {
      2.    private int id;
      3.    private String name;
      4.    private String address;
      5. // 省略getter/setter/构造方法/tostring
      6. }
      1. public interface StudentDao {
      2.    // 根据id查询学生
      3.    Student findById(int id);
      4. }
      1. public class StudentDaoImpl implements StudentDao{
      2.    @Override
      3.   public Student findById(int id) {
      4.        // 模拟从数据库查找出学生
      5.        return new Student(1,"百战程序员","北京");
      6.   }
      7. }

      3.编写xml配置文件,配置文件中配置需要Spring帮我们创建的对象

      1. <beans xmlns="http://www.springframework.org/schema/beans"
      2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      3. xsi:schemaLocation="http://www.springframework.org/schema/beans
      4. http://www.springframework.org/schema/beans/spring-beans.xsd">
      5. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl">bean>
      6. beans>

      4.测试从Spring容器中获取对象

      1. public class TestContainer {
      2. @Test
      3. public void t1(){
      4. // 创建Spring容器
      5. /**
      6. * ApplicationContext:容器接口
      7. * ClassPathXmlApplicationContext:容器接口实现类,该类可以从项目中读取配置文件,传入相对路径
      8. * FileSystemXmlApplicationContext:容器接口实现类,该类从磁盘中读取配置文件,传入绝对路径
      9. */
      10. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
      11. // ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\txxiaoer\\Desktop\\SSM框架Demo\\springdemo\\spring_ioc1\\src\\main\\resources\\bean.xml");
      12. // 从容器中获取对象
      13. StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
      14. StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
      15. System.out.println(studentDao1.hashCode());
      16. System.out.println(studentDao2.hashCode());
      17. System.out.println(studentDao1.findById(1));
      18. }
      19. }

      5.测试结果

       6.知识点整理:

      (1)创建的studentDao1和studentDao2两个对象是同一个对象,并可以调用方法。

      (2)Spring配置文件中,通过配置标签“”配置容器中的对象

      (3)Spring配置文件中,标签的属性为配置“对象名”,属性可以配置“对象类型”。

      四、SpringIOC_Spring容器类型

       1.容器接口(BeanFactory、ApplicationContext

      (1)BeanFactory:BeanFactory是Spring容器中的顶层接口,它可以对Bean对象进行管理。

      (2)ApplicationContext:ApplicationContext是BeanFactory的子接口。它除了继承BeanFactory的所有功能外,还添加了对国际化、资源访问、事件传播等方面的良好支持。(一般ApplicationContext用的比较多)

      2.ApplicationContext有以下三个常用实现类:

      (1) ClassPathXmlApplicationContext:该类可以从项目中读取配置文件,传入想对路径
      (2) FileSystemXmlApplicationContext:该类可以从磁盘中读取配置文件,传入绝对路径
      (3) AnnotationConfigApplicationContext:使用该类不读取配置文件,而是会读取注解

      3.代码演示如:

      1. public class TestContainer {
      2. @Test
      3. public void t1(){
      4. // 创建Spring容器
      5. /**
      6. * ApplicationContext:容器接口
      7. * ClassPathXmlApplicationContext:容器接口实现类,该类可以从项目中读取配置文件,传入相对路径
      8. * FileSystemXmlApplicationContext:容器接口实现类,该类从磁盘中读取配置文件,传入绝对路径
      9. */
      10. ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
      11. // ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\txxiaoer\\Desktop\\SSM框架Demo\\springdemo\\spring_ioc1\\src\\main\\resources\\bean.xml");
      12. // 从容器中获取对象
      13. StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
      14. StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
      15. System.out.println(studentDao1.hashCode());
      16. System.out.println(studentDao2.hashCode());
      17. System.out.println(studentDao1.findById(1));
      18. }
      19. }

      五、SpringIOC_对象的创建方式

      Spring 会帮助我们创建 bean ,那么它底层是调用什么方法进行创建的呢?

      1.使用无参构造方法

      Spring默认使用类的空参构造方法创建bean:
      1. // 假如类没有空参构造方法,将无法完成bean的创建
      2. public class StudentDaoImpl implements StudentDao{
      3. // 设置一个有参的构造方法,默认去除了无参的构造方法
      4.    public StudentDaoImpl(int a){}
      5.    
      6.    @Override
      7.    public Student findById(int id) {
      8.        // 模拟根据id查询学生
      9.        return new Student(1,"百战程序员","北 京");
      10.   }
      11. }

       如上,IDEA会报红提示没有无参构造方法,不能通过编译。

      2.使用工厂类的普通方法构建对象

      Spring 可以调用工厂类的方法创建 bean
      (1)创建工厂类,工厂类提供创建对象的方法:
      1. public class StudentDaoFactory {
      2.    public StudentDao getStudentDao(){
      3. //return new StudentDaoImpl(1):返回StudentDaoImpl对象,利用有参构造方法
      4.        return new StudentDaoImpl(1);
      5.   }
      6. }

      (2)在配置文件中配置创建bean的方式为工厂方式。

      1. <bean id="studentDaoFactory" class="com.itbaizhan.dao.StudentDaoFactory">bean>
      2. <bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao">bean>

      3.使用工厂类的静态方法构建对象

      Spring 可以调用工厂类的静态方法创建 bean
      1
      (1)创建工厂类,工厂提供创建对象的静态方法。
      1. public class StudentDaoFactory2 {
      2. public static StudentDao getStudentDao(){
      3. return new StudentDaoImpl(1);
      4. }
      5. }

      (2)在配置文件中配置创建bean的方式为工厂静态方法。

      1. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoFactory2" factory-method="getStudentDao">bean>

      4.知识点整理:

      (1)Spring不可以使用“有参构造方法”创建bean对象;可以使用“空参构造方法”“工厂方法”“静态工厂方法”创建bean对象。

      (2)Spring调用工厂类的普通方法创建bean对象需要配置“工厂对象和被创建的对象”

      六、SpringIOC_对象的五种创建策略

       Spring通过配置 中的 scope 属性设置对象的创建策略,共有五种创建策略(其中singleton和prototype为两种常用的创建策略):

      1.singleton:

      单例,默认策略。整个项目只会创建一个对象,通过 中的 lazy-init 属性可以设置单例对象的创建时机:

      lazy-init="false"(默认) 立即创建,在容器启动时会创建配置文件中的所有Bean 对象。
      lazy-init="true" 延迟创建,第一次使用 Bean 对象时才会创建。

      (1)配置单例策略:

      1. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton" lazy-init="false" >bean>

      (2)测试单例策略:

      1. public class StudentDaoImpl2 implements StudentDao{
      2. public StudentDaoImpl2(){
      3. System.out.println("创建了StudentDao!");
      4. }
      5. }
      1. @Test
      2. public void t2(){
      3. // 创建Spring容器
      4. ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
      5. // StudentDao studentDao = new StudentDaoImpl2();等同于下面的创建方式,下面的创建方式是从Spring容器中拿到对象
      6. // 从容器中获取对象
      7. StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
      8. StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
      9. StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao");
      10. System.out.println(studentDao1.hashCode());
      11. System.out.println(studentDao2.hashCode());
      12. System.out.println(studentDao3.hashCode());
      13. }

      (3)测试结果:

       (4)结论:

      lazy-init="false"(默认):立即创建,在容器启动时会创建配置文件中的所有Bean对象。

      2.prototype

      多例,每次从容器中获取时都会创建新的对象。

      (1)配置多例策略:

      1. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="prototype" >bean>

      (2)测试多例策略:

      1. @Test
      2. public void t2(){
      3. // 创建Spring容器
      4. ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
      5. // StudentDao studentDao = new StudentDaoImpl2();等同于下面的创建方式,下面的创建方式是从Spring容器中拿到对象
      6. // 从容器中获取对象
      7. StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
      8. StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
      9. StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao");
      10. System.out.println(studentDao1.hashCode());
      11. System.out.println(studentDao2.hashCode());
      12. System.out.println(studentDao3.hashCode());
      13. }

      (3)测试结果:

       (4)结论:

      每次从容器中获取时都会创建新的对象。

      3.request :每次请求创建一个对象,只在 web 环境有效。
      4.session :每次会话创建一个对象,只在 web 环境有效。
      5.gloabal-session :一次集群环境的会话创建一个对象,只在 web
      环境有效。

      七、SpringIOC_对象的销毁时机:

       对象的创建策略不同,销毁时机也不同:

      1.singleton :对象随着容器的销毁而销毁。(因为整个项目中只有一个对象,并且存在容器当中,如果对象被随意地销毁的话,再想获取对象就获取不到了,所以正常情况下单例的对象不会销毁,只会随着容器的销毁而销毁)
      2.prototype :使用 JAVA 垃圾回收机制销毁对象。(每次获取对象时都会创建新的对象,不存在容器当中,不然容器中要存很多这种对象。所以多例对象也不由Spring来销毁,因为太多了,所以直接使用Java的垃圾回收机制销毁对象,什么时候对象没用了就将其销毁。)
      3.request :当处理请求结束, bean 实例将被销毁。
      4.session :当 HTTP Session 最终被废弃的时候, bean 也会被销毁掉。
      5.gloabal-session :集群环境下的 session 销毁, bean 实例也将被销毁。

      八、SpringIOC_声明周期方法

       Bean对象的生命周期包含创建——使用——销毁,Spring可以配置Bean对象在创建和销毁时自动执行的方法:

      1.定义声明周期方法

      1. public class StudentDaoImpl2 implements StudentDao{
      2. //创建时自动执行的方法
      3. public void init(){
      4. System.out.println("创建StudentDao!!!");
      5. }
      6. //销毁时自动执行的方法
      7. public void destroy(){
      8. System.out.println("销毁StudentDao");
      9. }
      10. // 构造方法在创建对象的时候被调用
      11. public StudentDaoImpl2(){
      12. System.out.println("创建了StudentDao!");
      13. }
      14. }

      2.配置声明周期方法

      1. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton"
      2. init-method="init" destroy-method="destroy" >bean>

      3.测试方法

      1. @Test
      2. public void t3(){
      3. // 创建Spring容器
      4. ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
      5. // 销毁Spring容器,
      6. // ClassPathXmlApplicationContext才有销毁容器的方法,ApplicationContext引用没有销毁容器的方法
      7. ac.close();
      8. }

      4.测试结果

       5.知识点整理:

      (1)Spring中, 中配置“init-method ”属性可以定义创建对象时执行的方法,一般放入一些资源

      (2)在Spring中, 中配置“destroy-method ”属性可以定义销毁对象时执行的方法,一般放入一些资源关闭

      九、SpringIOC_获取Bean对象的方式

      Spring 有多种获取容器中对象的方式:

      1.通过id/name获取

      (1)配置文件
      1. <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
      2. <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>

      (2)获取对象

      StudentDao studentDao = (StudentDao) ac.getBean("studentDao");

      2.通过类型获取

      (1)配置文件

      <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>

      (2)获取对象

      StudentDao studentDao2 = ac.getBean(StudentDao.class);

      3.通过 id/name+类型 获取

      虽然使用类型获取不需要强转,但如果在容器中有一个接口的多个实现类对象,则获取时会报错,此时需要使用类型+id/name 获取:
      (1)配置文件
      1. <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
      2. <bean name="studentDao1" class="com.itbaizhan.dao.StudentDaoImpl">bean>

      (2)获取对象

      1. //传入id/name属性值+类型
      2. StudentDao studentDao2 = ac.getBean("studentDao",StudentDao.class);

      4.整体代码段:

      (1)配置文件:

      1. <bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2">bean>
      2. <bean name="studentDao1" class="com.itbaizhan.dao.StudentDaoImpl">bean>

      (2)测试方法中获取对象

      1. @Test
      2. public void t4(){
      3. // 创建Spring容器
      4. ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
      5. // 第一种方式:根据name获取对象(需要进行类型强转)
      6. StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
      7. System.out.println(studentDao1);
      8. // 第二种方式:根据类型获取对象(不需要进行类型强转,因为传入的就是确切的类型)
      9. // 这种方式有个问题:整个容器中同类型对象有可能有多个,这种情况就很难分辨了。
      10. // 如:StudentDao接口有多个实现类,这时就无法分辨具体要创建哪个实现类的对象了
      11. StudentDao studentDao2 = ac.getBean(StudentDao.class);
      12. System.out.println(studentDao2);
      13. // 第三种方式:根据name/id+类型获取对象
      14. StudentDao studentDao3 = ac.getBean("studentDao",StudentDao.class);
      15. System.out.println(studentDao3);
      16. }

    38. 相关阅读:
      Nginx的安装使用----反向代理服务器
      Elasticsearch集群搭建学习
      实现游戏中的轮廓描边
      mc我的世界云服务器租用价格表
      Docker部署Openldap和phpLDAPadmin
      Python超越Java语言,跃居世界编程语言第2位了!你却还在犹豫学不学Python?
      Ai-WB2模组HTTP 客户端 POST请求方法
      C++最佳实践之常用关键字
      【初识JavaSe语法笔记起章】——标识符、关键字、字面常量、数据类型、类型转换与提升、字符串类型
      lvgl 实现状态提示图标自动对齐补位显示
    39. 原文地址:https://blog.csdn.net/m0_51697147/article/details/126049543