【死磕Spirng】|我们真的懂IoC吗?

前言

IoC就真的只像我们经常背的八股文吗:IoC是Inversion of Control的简写,意为控制反转,它将创建Bean的主动权交给容器来管理,它的实现方式为DI(Dependency Injection),即为依赖注入。其实IoC还有很多很多我们没有想到的东西。这篇文章从重新认识IoC出发,涉及Spring IoC容器的概述。

重新认识IoC

先来说说IoC的发展史

俗话说,每学一个新知识,就先要学学它的历史

IoC是什么

先来看下维基百科是怎么解释IoC的:

In software engineering, inversion of control (IoC) is a programming principle. IoC inverts the flow of
control as compared to traditional control flow. In IoC, custom-written portions of a computer program
receive the flow of control from a generic framework. A software architecture with this design inverts
control as compared to traditional procedural programming: in traditional programming, the custom
code that expresses the purpose of the program calls into reusable libraries to take care of generic
tasks, but with inversion of control, it is the framework that calls into the custom, or task-specific, code.

大概意思就是:在软件工程当中,反转控制就是一个编程风格(或者准则),它相比于传统的软件架构不是那么的明显易懂,相比之下,它是有一定复杂度的,就比如说依赖的来源,我们编程时是不用关注这个细节的。那么他就可以解耦我们的一些相关设置,就比如我们想要一个特定条件的Bean,那么我们就可以直接从IoC容器中拿,但是它怎么创建的我们不需要关心。

IoC的简史

  • 1983年,Richard E. Sweet 在《The Mesa Programming Environment》中提出“Hollywood
    Principle”(好莱坞原则)
  • 1988年,Ralph E. Johnson & Brian Foote 在《Designing Reusable Classes》中提出“Inversion
    of control”(控制反转)
  • 1996年,Michael Mattsson 在《Object-Oriented Frameworks, A survey of methodological
    issues》中将“Inversion of control”命名为 “Hollywood principle”
  • 2004年,Martin Fowler 在《Inversion of Control Containers and the Dependency Injection
    pattern》中提出了自己对 IoC 以及 DI 的理解
  • 2005年,Martin Fowler 在 《InversionOfControl》对 IoC 做出进一步的说明

IoC是如何实现的

《Expert One-on-One™ J2EE™ Development without EJB™》提到的主要实现策略:
IoC实现的策略主要有两种方式:一种是依赖查找,一种是依赖注入
依赖查找:容器会提供一个回调机制到我们的组件,通过上下文查询的方式找到我们需要的组件。
依赖注入:这个方式是不需要容器来查找的,通常情况下,容器给我们注入或者我们手动注入,可以通过注入的方式:构造器注入、Set方法注入、接口注入

IoC的职责是什么

IoC通常来讲,我们经常说的 ‘好莱坞原则’ :不要来找我,我们来找你

通用职责

1、依赖处理
(1)依赖查找
(2)依赖注入
2、生命周期管理
(1)容器
(2)托管的资源(Java Beans 或其他资源)
3、配置
(1)容器
(2)外部化配置
(3)托管的资源(Java Beans 或其他资源)

Spring IoC容器概述

Spring IoC 依赖查找

基础解释

这里的代码,我们会涉及到几个Api,这里稍作解释一下:
BeanFactory:我们可以简单理解为IoC容器,它是实现IoC容器的最顶级接口
ObjectFactory:一个能返回特定实例对象的对象工厂,这个接口提供延迟查找Bean的功能
ListableBeanFactory :它是BeanFactory的一个扩展,它能够列举所有的Bean实例,而不是通过名字一个一个查找。

代码示例

package com.markus.spring.ioc.dependency.lookup;
import com.markus.spring.ioc.dependency.Super;
import com.markus.spring.ioc.dependency.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Map;
/**
 * Date:Create in 2021/2/2 20:58
 * 通过 Bean 名称进行查找
 * @author markusZhang
 */
public class DependencyLookUpDemo {
    public static void main(String[] args) {
        //1.配置 xml 文件
        //2.启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/context-dependency-lookup.xml");
        // 根据类型 实时查找 查找单个
        lookUpByType(beanFactory);
        // 根据类型 集合实例
        lookUpCollectionByType(beanFactory);
        // 根据注解 查找Bean
        lookUpByAnnotation(beanFactory);


        // 实时查找
//        lookUpInRealTime(beanFactory);
        // 延迟查找
//        lookUpInLazy(beanFactory);
    }

    private static void lookUpByAnnotation(BeanFactory beanFactory){
        if (beanFactory instanceof ListableBeanFactory){
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, Object> beansWithAnnotation = listableBeanFactory.getBeansWithAnnotation(Super.class);
            System.out.println(beansWithAnnotation);
        }
    }

    private static void lookUpCollectionByType(BeanFactory beanFactory){
        if (beanFactory instanceof ListableBeanFactory){
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> beansOfType = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有 User 集合对象:"+beansOfType);
        }
    }

    private static void lookUpByType(BeanFactory beanFactory){
        User bean = beanFactory.getBean(User.class);
        System.out.println("实时查找:"+bean);
    }

    private static void lookUpInLazy(BeanFactory beanFactory){
        ObjectFactory<User> objectFactory = (ObjectFactory) beanFactory.getBean("objectFactory");
        User object = objectFactory.getObject();
        System.out.println("延迟查找:"+object);
    }

    private static void lookUpInRealTime(BeanFactory beanFactory) {
        User user = (User) beanFactory.getBean("user");
        System.out.println("实时查找:"+user);
    }
}

根据Bean的名称查找

1、实时查找
看上面的代码,我们通过最基本的getBean()就可以做到实时查找的功能
2、延迟查找
开头已经提到过ObjectFactory,它能够根据targetBeanName找到特定实例,从而体现出延迟查找的功能

根据Bean的类型查找

1、单个类型
单个类型,可以通过BeanFactory#getBean(ClassType)来实现,这个接口是在Java5之后实现的,因为Java5开始支持泛型
2、集合类型
集合类型的话,就可以用我们上面提到的ListableBeanFactory#getBeansOfType 来得到,它的作用是可以列举所有我们注册的Bean实例。

根据Java注解查找

上面的实例代码也有实现,也就是说,我们在某一个Bean上打上注解,然后可以通过ListableBeanFactory#getBeansWithAnnotation(ClassType)来获取。这个例子我们就可以联想到为什么我们在工程中,想要去获取一个Bean,必须在这个Bean上打注解,例如@Component、@Service等等,其实底层Spring会给我们进行一层过滤

Spring IoC 依赖注入

代码示例

package com.markus.spring.ioc.dependency.injection;

import com.markus.spring.ioc.dependency.repository.UserRepository;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * @author 14222
 * @date 2021-02-02 22:07
 */
public class DependencyInjectionDemo {
    public static void main(String[] args) {
        //1.配置 xml 文件
        //2.启动 Spring 应用上下文
//        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/context-dependency-injection.xml");

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/context-dependency-injection.xml");
        // 依赖来源一:自定义Bean
        UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
//        System.out.println(userRepository.getUsers());

//        System.out.println(userRepository.getBeanFactory() == beanFactory);

        // 依赖查找 错误
//        System.out.println(beanFactory.getBean(BeanFactory.class));
        // 依赖来源二:spring 内建 Bean
        Environment environment = applicationContext.getBean(Environment.class);
        System.out.println("获取 Environment 内建 Bean"+environment);


        // 依赖来源三:内建依赖
        System.out.println("spring 内建依赖:"+userRepository.getBeanFactory());

        ObjectFactory<ApplicationContext> objectFactory = userRepository.getObjectFactory();

        System.out.println(objectFactory.getObject() == applicationContext);

//        ObjectFactory<ApplicationContext> objectFactory = userRepository.getObjectFactory();
//        System.out.println(objectFactory.getObject() == beanFactory);

        // 谁才是真正的IoC容器
        whoIsIoCContainer(userRepository,applicationContext);
    }

    private static void whoIsIoCContainer(UserRepository userRepository,ApplicationContext applicationContext){

        // ConfigurableApplicationContext <- ApplicationContext <- BeanFactory
        // ConfigurableApplicationContext#getBeanFactory()

        System.out.println(userRepository.getBeanFactory() == applicationContext);
    }
}

Autowire

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <import resource="context-dependency-lookup.xml"/>

    <bean id="userRepository" class="com.markus.spring.ioc.dependency.repository.UserRepository" autowire="byType">
<!--        <property name="users">-->
<!--            <util:list>-->
<!--                <ref bean="user"/>-->
<!--                <ref bean="super"/>-->
<!--            </util:list>-->
<!--        </property>-->
    </bean>

</beans>

Autowire 可以通过 名字、类型、构造器进行注入

依赖来源

看了上面的代码示例,可以了解到,spring IoC的来源是:

  1. 自定义 Bean ,也就是我们 xml 或者注解定义的 Bean
  2. 容器内建的 Bean,例如 Environment
  3. 还有 spring 内建的依赖,就比如 BeanFactory ,其实它真实类型是:DefaultListableBeanFactory

谁才是真正的 IoC 容器

我们经常知道的是 ApplicationContext 和 BeanFactory 都可以看做是一个IoC容器的实现,实际上,BeanFactory 是一个IoC基础的实现,而 ApplicationContext 是 BeanFactory 的一个超集,提供了丰富的功能,包括像 面向切面、配置元信息、资源管理、事件、国际化、注解、Environment抽象等。而ApplicationContext的IoC功能也是依赖BeanFactory实现的。我们看下这个图:
在这里插入图片描述

所以说BeanFactory是真正的spring 底层IoC容器!ApplicationContext 是具备应用特性的 BeanFactory 超集!

而我们看到的 BeanFactory接口,其实它在 spring 中真正的实现是:DefaultListableBeanFactory。
在这里插入图片描述
AbstractRefreshableApplicationContext 是 ApplicationContext 的一个实现,作用就是容器在refresh()期间多次回调,创建 Bean 工厂实例

总结

以上就是对IoC的简单陈述,从IoC发展史,再到IoC两个非常重要的实现:依赖注入和依赖查找,搞明白了两种实现,引发出依赖的来源在哪的思考,以及最后搞明白了谁才是Spring IoC 底层的容器。

展望

【啃下Spring】系列 第一期 就结束了,如果有不对或者不明白的地方,请评论区指出哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值