全网疯传的Spring学习笔记【IOC、DI和Spring工厂】,看完我也想写一个了!

举报
XiaoLin_Java 发表于 2022/01/31 17:30:31 2022/01/31
【摘要】 四、控制反转(IOC)和依赖注入(DI) 4.1、控制反转(IOC)    控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。    简单来说控制反转就是把对于成员...

四、控制反转(IOC)和依赖注入(DI)

4.1、控制反转(IOC)

    控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

    简单来说控制反转就是把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成。

    IoC 是一个概念,是一种思想,其实现方式多种多样。Spring 框架使用依赖注入(DI)实现 IoC。

4.2、依赖注入(DI)

    依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA对 classB 有依赖。

    依赖注入(Dependency Injection):当一个类需要另一个类时,就可以把另一个类作为本类的成员变量,最终通过Spring的配置文件进行注入(赋值)。简单来说就是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

    Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。

4.3、总结

   Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

五、Spring工厂

5.1、简单对象和复杂对象

5.1.1、简单对象

    简单对象指的就是可以直接通过调用构造方法(new)创建出来的对象。

5.1.2、复杂对象

    复杂对象指的就是不可以直接通过调用构造方法(new)创建出来的对象。比如JDBC的Connection对象、Mybatis的SqlSessionFactory对象。

5.2、Spring创建复杂对象的三种方式

5.2.1、FactoryBean

5.2.1.1、FactoryBean接口

    如果在applicationContext.xml配置文件中配置的class属性是FactoryBean接口的实现类,那么通过id属性获得的是这个类所创建的复杂对象(底层会调用重写的getObject()方法)。

public class MyFactoryBean implements FactoryBean<Connection> {

  // 用于书写创建复杂对象的代码,并且把复杂对象作为方法的返回值返回
  @Override
  public Connection getObject() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","123456");
    return connection;
  }

  // 返回所创建的复杂对象的Class对象
  @Override
  public Class<?> getObjectType() {
    return Connection.class;
  }

  // 配置是否是单例模式
  @Override
  public boolean isSingleton() {
    return false;
  }

  <bean id="factoryBean" class="com.test.MyFactoryBean">
  /**
  * 用于测试factoryBean
  */
  @Test
  public void testFactoryBean(){
    ClassPathXmlApplicationContext ctr = new ClassPathXmlApplicationContext("/applicationContext.xml");
    Connection conn  = (Connection) ctr.getBean("factoryBean");
    System.out.println(conn);
  }

5.2.1.2、FactoryBean接口的细节

  1. 如果我不想获得创建的复杂对象(Connection),想获得普通的简单对象(FactoryBean),我们仅仅只需在getBean(id)的前面加一个&即可。
import java.sql.Connection;
import java.sql.DriverManager;
import org.springframework.beans.factory.FactoryBean;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/24 19:47
 */
public class MyFactoryBean implements FactoryBean<Connection> {

  @Override
  public Connection getObject() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager
        .getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false", "root",
            "1101121833");
    return connection;
  }

  @Override
  public Class<?> getObjectType() {
    return Connection.class;
  }

  @Override
  public boolean isSingleton() {
    return true;
  }
}
<?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="factoryBean" class="MyFactoryBean">

  </bean>
</beans>
import java.sql.Connection;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/24 19:50
 */
public class MyFactoryBeanTest {

  /**
  * 用于测试复杂类型对象的创建
  */
  @Test
  public void testMyFactoryBeanTest(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
    MyFactoryBean connection = (MyFactoryBean)applicationContext.getBean("&factoryBean");// 获取普通的简单对象FactoryBean,不获取复杂的Connection对象
    System.out.println(connection);
  }
}
  1. isSingleton()方法,如果返回值为true时,他只会创建一个对象,返回false时会创建多个对象,一般根据对象的特点来判断返回true(SqlSessionFactory)还是false(Connection)。

5.2.1.3、BeanFactory实现原理图

Spring-第 3 页

5.2.1.4、FactoryBean总结

    FactoryBean是Spring中用于创建复杂对象的一种方式 也是Spring原生提供的,后面框架整合会大量运用。

5.2.2、实例工厂

5.2.2.1、FactoryBean的弊端

    使用FactoryBean的话有Spring的侵入,实现了FactoryBean接口,一旦离开了Spring,整个类都无法使用。

5.2.2.2、实例工厂的使用

// 实例工厂
public class ConnectionFactory {
  public Connection getConnection(){
    Connection conn = null;
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","1101121833");
    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }
   return conn;
  }
}
  <bean id="connFactory" class="com.factory.ConnectionFactory"/>
  <bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>

5.2.3、静态工厂

    前面我们学了实例工厂,由于实例工厂的getConnection()方法是实例方法,需要由对象来调用,所以需要先创建对象然后再通过对象来调用方法。

    而静态工厂由于getConnection()方法是静态方法,不需要由对象来调用,直接通过类进行调用。这就是实例工厂与静态工厂最大的区别。

public class ConnectionStaticBeanFactory {
  public static Connection getConnection(){
    Connection conn = null;
    try {
      Class.forName("com.mysql.jdbc.Driver");
      conn = DriverManager.getConnection("jdbc:mysql:///javaweb?characterEncoding=utf-8&useSSL=false","root","1101121833");
    } catch (ClassNotFoundException | SQLException e) {
      e.printStackTrace();
    }
    return conn;
  }
}
 <bean id="staticBeanFactory" class="com.factory.ConnectionStaticBeanFactory" factory-method="getConnection"/>

5.3、创建对象的细节

5.3.1、控制简单对象的创建次数

    控制简单对象的创建次数我们只需要配置bean标签的scope属性值即可。他常用的有两个值:

  1. singleton:默认为单例模式,只会创建一个简单对象。
  2. prototype:每次都会创建一个新的对象。
<bean id="person" scope="singleton(prototype)"  class="com.doamin.Person"/>

5.3.2、控制复杂对象的创建次数

   FactoryBean接口的isSingleton()方法的返回值来进行控制(如果没有isSingleton()方法,那么还是通过scope属性来进行控制):

  1. 返回true:只会创建一次。
  2. 返回false:每一次都会创建一个新的对象。

5.3.3、控制对象创建次数的原因

  可以被大家共享的对象(SqlSessionFactory、各种Dao、Service)可以只创建一次,不可以被大家共享的对象(Connection、SqlSession、Controller)可以创建多次,控制对象创建次数的最大好处是可以节省不必要的内存浪费。

5.4、对象的生命周期

    生命周期指的是一个对象的创建、存活、消亡的一个完整过程。由Spring来负责对象的创建、存活、销毁。了解生命周期,有利于我们使用好Spring为我们创建的对象。

    Spring帮我们创建的对象有三个阶段:

  1. 创建阶段
  2. 初始化阶段
  3. 销毁阶段

5.4.1、创建阶段

  1. 当 scope = “singleton” 时,Spring工厂创建的同时,对象会随之创建。如果我们不想在Spring工厂创建的同时创建,想在获取对象的时候创建,只需在配置文件的bean标签添加一个lazy-init = true即可。
  2. 当 scope = “prototype” 时,Spring工厂会在获取对象的同时创建对象。

5.4.2、初始化阶段

   Spring工厂在创建完对象后,会调用对象的初始化方法,完成对应的初始化操作。

  初始化方法是由程序员根据需求提供初始化方法,由Spring工厂调用,最终完成初始化操作。他有两种调用的方式:

  1. 实现InitializingBean接口(有Spring侵入的问题)。
  2. 提供一个普通方法并修改配置文件。

5.4.2.1、InitializingBean接口

// 这个就是初始化方法,做一些初始化操作,Spring会进行调用 
@Override
  public void afterPropertiesSet() throws Exception {
		// 初始化操作
  }

5.4.2.2、提供普通方法

    由于实现InitializingBean接口存在Spring侵入的问题,所以Spring提供了另一个方法给我们进行初始化操作,那就是提供一个普通的方法,然后去配置文件中增加init-method="方法名"熟悉的配置即可。

  public void init(){
    System.out.println("我是初始化方法");
  }
<bean id="product" class="com.domain.Product" init-method="init"/>

5.4.2.3、注意

    如果一个对象既实现了InitializingBean接口同时又提供了普通的初始化方法,那么两个初始化方法都会执行,先执行的是InitializingBean接口的方法,再执行普通的初始化方法。

    在执行初始化操作之前,会先进行属性的注入,注入在前,初始化在后。

    初始化需要做的操作一般是数据库、IO、网络操作。

5.4.3、销毁阶段

    在工厂关闭之前,Spring会在销毁对象前,会调用对象的销毁方法,完成销毁操作。

   销毁方法是程序员根据需求定义销毁方法,由Spring工厂调用销毁方法,完成销毁操作。他也有两种方法:

  1. 实现DisposableBean接口。
  2. 定义普通的销毁方法在配置文件中配置。

5.4.3.1、实现DisposableBean接口

public class Product implements InitializingBean, DisposableBean {
      @Override
  public void destroy() throws Exception {
    System.out.println("销毁操作,资源释放");
  }
}

5.4.3.2、定义普通方法

public class Product implements InitializingBean, DisposableBean { 
	public void MyDestory(){
    	System.out.println("自己定义的销毁方法");
  	}
}
 <bean id="product" class="com.domain.Product" destroy-method="MyDestory"/>

5.4.3.3、注意

  1. 销毁方法的操作只适用于scope=“singleton”。
  2. 销毁操作主要指的是一些资源的释放操作。

5.5、Spring整合配置文件

    一般来说像数据库的一些配置信息我们都不会直接写在代码里面,会将他们抽取出来成一个配置文件,再利用Spring进行注入。我们只需要加入一个标签即可完成。

<!--告诉Spring你的db.properties在哪里-->  
<context:property-placeholder location="classpath:/db.properties"/>

<!--用$(db.properties中的key)来进行取值-->
<bean id="conn" class="com.factory.BeanFactory">
	<property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。