Spring 理解IoC思想&依赖注入、IoC容器理解

该篇博客主要理解Ioc思想&依赖注入(DI),并讲讲关于Spring的IoC容器

该篇博客主要阐述

1、理解Ioc思想&依赖注入(DI)
2、Spring的IoC容器

一、理解Ioc思想&依赖注入(DI)

1、理解IoC思想(Inverse of Control 控制反转)

其思想是反转资源获取的方向。传统的向资源查找方式要求组件向容器发起请求查找资源作为回应,容器就会返回资源(这样的方式可以理解为什么东西都得自己去拿)。而IoC,则是容器主动的将资源推送给它所管理的组件,组件所要做的仅仅是选择一种合适的方式来接收资源(IoC方式可以理解为,什么东西都让别人送过来)。所以IoC这种思想是一种控制反转的思想,也是一种解耦合的思想(这就需要清楚依赖注入了)。而Spring是该思想的一种实现,因此Spring容器通常称为IoC容器

2、依赖注入(DI):Dependency Injection

在《Spring揭秘》中是这样解释的:“在许多书中都将依赖注入看作是IoC的一种方式,不过本书暂且忽略这些观点,将IoC和依赖注入等同看待,在读者理解了依赖注入之后,可以再结合其他资料对IoC做进一步的研究”。

而笔者本人对依赖注入理解是:可看作是IoC的一种方式,即组件以一些预定义好的方式(比如setter注入、构造器注入、接口注入,这几种注入方式会在之后博客中解释)接收来自容器的资源注入。

在《Spring IN ACTION》中是这样理解的:“例如一个订单管理组件需要信用卡认证组件,则它不需要自己创建。订单管理组件只需要表明自己两手空空,容器就会主动赋予它一个信用卡认证组件,这种创建应用对象之间协作关系的行为通常被称为装配,这也是依赖注入的本质。”

在本文最后也就是了解Spring容器之后,将会对以上思想再进行实践探索.但是Spring配置Bean的方式将在之后的博客中再做阐述

二、Spring的IoC容器

Spring是一个基于容器的框架,所以说说关于Spring的IoC容器吧

1、Spring提供了两种容器类型

  • BeanFactory
  • ApplicationContext
BeanFactory

基础类型IoC容器,提供完整的IoC服务支持,如果没有特殊指定,默认采用延迟初始化策略。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入。所以相对来说,容器启动初期速度较快(因为延迟初始化了),所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较适合的。但话说回来,Bean工厂对大多数应用来说往往太低级了,所以一般不使用

ApplicationContext

ApplicationContext是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了用于Bean工厂的所有支持,ApplicationContext还提供了其他高级用法,比如事件发布等

以下是BeanFactory和Application之间的关系有一个更清晰的认识

这里写图片描述

ApplicationContext间接继承自BeanFactory


源码里也可以看出两者关系

这里写图片描述


这里写图片描述

2、ApplicationContext(应用上下文)

这里笔者就不过多阐述BeanFactory了,主要说说它的间接子接口(应用上下文ApplicationContext)

Spring自带了几种类型的应用上下文,下面罗列3种用户最有可能遇到的
  • ClassPathXmlApplicationContext——从类路径下的XML配置文件中加载上下文定义,把应用上下文当作类资源
  • FileSystemXmlapplicationcontext——读取文件系统下的XML配置文件并加载上下文定义
  • XmlWebApplicationContext——读取Web应用下的XML配置文件并装载上下文定义
ClassPathXmlApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FileSystemXmlapplicationcontext
ApplicationContext context = new FileSystemXmlapplicationcontext("c:/applicationContext.xml")

之后用Spring容器我将以ClassPathXmlApplicationContext的方式实现。


理解IoC

首先在笔者之前的博客中已经很详细的说明了三层架构的模式,不明白的可以转战该篇博客https://blog.csdn.net/w_linux/article/details/79919523。在三层架构中,Service层对Dao层的依赖性非常强。大家可以看以下的例子,利用传统方法实现Service调用Dao层


IUserDao

package com.linjie;

/**
 * @author LinJie
 * Dao接口
 */
public interface IUserDao {
    //查询用户年龄
    public void FindUserAge();
}

UserDaoImpl

package com.linjie;

/**
 * @author LinJie
 * Dao层实现类
 */
public class UserDaoImpl implements IUserDao {
    @Override
    public void FindUserAge() {
        System.out.println("UserDaoImpl_Dao层被调用");
    }

}

IUserService

package com.linjie;

/**
 * @author LinJie
 * service接口
 */
public interface IUserService {
    //登录
    public void login();
}

UserServiceImpl

package com.linjie;

public class UserServiceImpl implements IUserService {

    @Override
    public void login() {
        System.out.println("UserServiceImpl_Service层被调用");
        //传统方式调用Dao层
        IUserDao userDao = new UserDaoImpl();
        userDao.FindUserAge();
    }

}

测试类

package com.linjie;

import org.junit.Test;

public class SpringTest {
    @Test
    public void Test() {
        IUserService userService = new UserServiceImpl();
        userService.login();
    }
}

结果如下

这里写图片描述

即使运行结果没问题,但是我们关注的是它的耦合性依赖度。假如我们要修改UserDaoImol的类名的话,你会发现这并不是这么简单的事,你需要该其他地方的对应的代码,这就是我们所说Service层对Dao层的依赖性太强,代码过于耦合。所以解决办法就是使用Spring IoC
首先配置XML文件(applicationContext.xml)创建于src下

关于创建Bean的具体步骤和具体意义,将在下一篇博客做详细的阐述,这里只要理解Sping IoC思想即可

applicationContext.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">

    <!-- 配置要创建的对象信息 -->
    <!-- id/name:用于识别不同的bean,唯一标识一个节点,标识对象的名字 -->
    <!-- bean:每个bean相当于要创建的对象(利用反射机制) -->
    <!-- class:创建哪个类的对象,要全类名 -->

    <bean id="userDao" class="com.linjie.UserDaoImpl"></bean>

    <!-- DI:依赖注入
        双方都必须是bean,在创建service的时候,主动将dao的依赖对象交给service -->

        <bean id="userService" class="com.linjie.UserServiceImpl">

        <!-- setter方法注入
        name:要与类中的userDao一致(即创建的userDao变量),然后调用userDao的setUserDao方法
        ref:Spring容器中定义的bean对象的名字 -->

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

</beans>

IUserDao

package com.linjie;

/**
 * @author LinJie
 * Dao接口
 */
public interface IUserDao {
    //查询用户年龄
    public void FindUserAge();
}

UserDaoImpl

package com.linjie;

/**
 * @author LinJie
 * Dao层实现类
 */
public class UserDaoImpl implements IUserDao {
    @Override
    public void FindUserAge() {
        System.out.println("UserDaoImpl_Dao层被调用");
    }

}

IUserService

package com.linjie;

/**
 * @author LinJie
 * service接口
 */
public interface IUserService {
    //登录
    public void login();
}

UserServiceImpl(采用setter注入)

package com.linjie;

public class UserServiceImpl implements IUserService {
    private IUserDao userDao;//与配置文件中property name="userDao"的name对应(其实name=""是与setUserDao的名称相同)

    //Spring框架自动创建UserDaoImpl对象后,通过setUserDao方法给userDao赋值
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    public IUserDao getUserDao() {
        return userDao;
    }

    @Override
    public void login() {
        System.out.println("UserServiceImpl_Service层被调用");
        userDao.FindUserAge();
    }

}

这里写图片描述

可以在UserServiceImpl中看出,不再使用传统方法的那种过度依赖的模式,不再new了,而是Spring自动创建,并添加配置即可,这样降低了层与层之间的耦合性,从而只需要关注具体的业务方法,而不用关注如何创建对象,实现控制反转。ps:感觉Spring的日志真的很好用哈哈哈



参考

《Spring揭秘》

《Spring IN ACTION》

猜你喜欢

转载自blog.csdn.net/w_linux/article/details/80025048