详解(Spring Ioc)本质 DI

Ioc本质

控制反转Ioc,是一种设计思想,而不是一种新的技术。DI(依赖注入)是实现Ioc的一种方法,也有人认为DI只是Ioc的另一种说法。在没有Ioc的程序中,使用面向对象编程,对象的创建与对象之间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方发,控制反转就是:获得以来对象的方式反转了。
Ioc是spring框架的核心内容,使用多种方式完美的实现了Ioc,可以使用XML配置,也可以使用注解,新版本的spring可以零配置实现Ioc.
spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象

详解(Spring Ioc)本质 DI

采用XML方式配置bean的时候,bean的定义信息和实现分离的,而采用注解的方式把二者合为一体,bean的定义信息直接以注解的形式的定义在实体类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产获取特定对象的方式。在spring中实现控制反转的是Ioc容器,其实现方法是依赖注入(DI)

依赖注入

指spring创建对象的过程中,将对象依赖属性(常量,对象,集合)通过配置设置给该对象。
依赖注入的方式

  1. 构造方法注入
  2. set方法注入
  3. P命名空间注入

构造方法注入

StudentServiceImpl中提供有参构造

public class StudentServiceImpl implements StudentService {
    private StudentDao studentDao;


    public StudentServiceImpl(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    //set方法注入


    /*public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }*/

    @Override
    public void getStudent() {
        studentDao.getStudent();
    }
}

修改配置文件
在bean标签中提供了一个子标签来完成构造方法的注入
构造方法的注入有两种方式


    <!--依赖注入 通过构造注入方式一-->
    <!--
        name:构造方法参数名称
        value:简单数据类型
        ref:应用数据类型(从IOC容器中获取的对象)
    -->
    <!--配置studentDao的实现类studentDaoImpl-->
    <bean id="studentDao" class="com.lifly.dao.impl.StudentDaoImpl"></bean>
    <!--配置studentService的实现类studentServiceImpl-->
    <bean id="studentService" class="com.lifly.service.impl.StudentServiceImpl">
        <constructor-arg name="studentDao" ref="studentDao"></constructor-arg>
    </bean>

通过构造注入第二种不常用仅了解即可

  <!--依赖注入 通过构造注入方式一(二)-->
    <!--
        index:构造方法参数索引
        type:该索引的java类型
        value:简单数据类型
        ref:引用数据类型(从IOC容器中获取的对象)
    -->
    <bean id="studentDao" class="com.lifly.dao.impl.StudentDaoImpl"></bean>
    <!--配置studentService的实现类studentServiceImpl-->
    <bean id="studentService" class="com.lifly.service.impl.StudentServiceImpl">
        <constructor-arg index="0" type="com.lifly.dao.StudentDao" ref="studentDao"></constructor-arg>
    </bean>

set 方法注入

在StudentServiceImpl中提供set方法

public class StudentServiceImpl implements StudentService {
    private StudentDao studentDao;
    
  /*  public StudentServiceImpl(StudentDao studentDao) {
        this.studentDao = studentDao;
    }*/

    //set方法注入
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    @Override
    public void getStudent() {
        studentDao.getStudent();
    }
}

修改配置文件
使用set方法注入,提供一个子标签,完成set方法注入

    <!--setter方法注入-->
    <!--
        name:set方法的属性名去掉set,首字母小写
        value:简单数据类型
        ref:引用数据类型(从IOC容器中获取)
    -->
    <bean id="studentDao" class="com.lifly.dao.impl.StudentDaoImpl"></bean>
    <bean id="studentService" class="com.lifly.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

P命名空间注入

P命名空间注入底层(本质)使用的也是set方法注入,只是在上面的基础上简化
使用P命名空间注入,需要先倒入约束
xmlns:p=“http://www.springframework.org/schema/p”
与set方法注入一样,只需要在xml配置文件中修改配置文件即可

 <!--p命名空间注入-->
    <bean id="studentDao" class="com.lifly.dao.impl.StudentDaoImpl"></bean>
    <bean id="studentService" class="com.lifly.service.impl.StudentServiceImpl" p:studentDao-ref="studentDao"></bean>

依赖注入的数据类型

注入简单类型

定义一个JavaBean例如Person类

public class Person {
//基本数据类型
    private String username;
    private int age;
//    单列集合 array list set
    private String[] array;
    private List<String> list;
    private Set<String> set;
//    双列集合 map,properties
    private Map<String,String> map;
    private Properties properties;

   //get和set方法
   //有参无参构造

    @Override
    public String toString() {
        return "Person{" +
                "username='" + username + '\'' +
                ", age=" + age +
                ", array=" + Arrays.toString(array) +
                ", list=" + list +
                ", set=" + set +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

编写配置文件

DI注入简单类型

<!--Di注入简单数据类型-->
    <bean id="person" class="com.lifly.bean.Person">
        <property name="username" value="lifly"/>
        <property name="age" value="24"/>

DI注入单列集合

 <!--注入单列集合 array list set-->
        <property name="list">
            <list>
                <value>峡谷之巅</value>
                <value>黑色玫瑰</value>
                <value>弗雷尔卓德</value>
            </list>
        </property>
        <property name="array">
            <array>
                <value>德玛西亚</value>
                <value>艾欧尼亚</value>
                <value>乱葬之海</value>
            </array>
        </property>
        <property name="set">
            <set>
                <value>古力娜扎</value>
                <value>迪丽热吧</value>
                <value>马尔扎哈</value>
            </set>
        </property>

Di注入双列集合

 <!--注入双列集合-->
        <property name="map">
            <map>
                <entry key="天" value="地"></entry>
                <entry key="人" value="和"></entry>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="大桥">孙策</prop>
                <prop key="小乔">周瑜</prop>
                <prop key="香香">刘备</prop>
            </props>
        </property>

spring核心接口

  • BeanFactory

Ioc的*接口,它定义了Ioc最基础的功能,但是其功能比较简单,一般面向spring自身使用
特点:在第一次使用到某个Bean(调用getBean())才对该Bean进行加载实例(用的时候再创建)-----》懒汉设计模式

  • ApplicationContext

这是在BeanFactory基础上衍生出的接口,它扩展了BeanFactory的功能,一般面向程序员使用
特点:在容器启动时,一次性创建并加载所有的Bean(初始化的时候全部创建好)—》饿汉设计模式

spring核心实现类

  • ClassPathXmlApplicationContext

读取类路径(classpath)下的xml配置文件

  • FileSystemXmlApplicationContext

读取本地磁盘下的xml配置文件

  • AnnotationConfigApplicationContext

读取java配置类加载配置

spring核心方法

  1. getBean(String name)

通过指定id获取对象的实例,需要手动强转
例如以下代码

      //读取applicationContext核心配置文件

        //通过指定id获取对象的实例,需要要手动强转
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取UserDao
        UserDao userDao = (UserDao) classPathXmlApplicationContext.getBean("userDao");
        userDao.getUser();
  1. getBean(ClassrequiredType)

通过指定类型获取对象的实例,不需要强制转换
例如以下代码

    /**
     * getBean(Class<T>requiredType)
     * 如果同一个接口类型下有多个实力对象,会报错
     * org.springframework.beans.factory.NoUniqueBeanDefinitionException:
     *         No qualifying bean of type 'com.lifly.dao.UserDao' available:
     *         expected single matching bean but found 2: userDao,userDaoMysql
     */
    @Test
    public void springQucikTest2(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = classPathXmlApplicationContext.getBean(UserDao.class);
        userDao.getUser();
    }
  1. getBean(String name,ClassrequiredType)

通过指定id和类型获取对象的实例
例如以下代码

    /**
     * getBean(String name,Class<T>requiredType)
     * 通过指定id和类型获取对象的实例
     */
    @Test
    public void springQucikTest3(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDaoMysql = classPathXmlApplicationContext.getBean("userDaoMysql", UserDao.class);
        userDaoMysql.getUser();
    }

spring配置文件

Bean标签配置

相关属性

属性名 描述
id 在Ioc容器的唯一标识
class 创建对象实例的权限定名
scope 声明此对象的作用范围
singleton:单例对象(默认)
prototype:多利对象
init-method 在对象创建时,执行此方法
destory-method 在对象销毁时,执行此方法

作用范围

1. singleton(单例对象)
何时创建:ioc容器初始化时,创建对象
对象何时运行:只要ioc容器在,对象就一直存在
何时销毁:ioc容器关闭时,销毁对象

2. prototype(多例对象)
何时创建:在调用getBean()方法时,创建
对象运行:一直使用就一直活着
何时销毁:当对象不再使用后,根据JVM,GC机制垃圾回收

上一篇:Spring依赖注入原理(DI)


下一篇:Spring学习总结(一)---谈谈对Spring IOC的理解(一:理论知识理解)