Spring源码学习之路---IOC(一)

本文深入探讨Spring框架的IOC(控制反转)概念,通过代码示例对比了使用IOC前后对象依赖管理的变化,详细讲解了IOC如何简化开发流程,降低代码耦合度。同时,文章剖析了Spring IOC容器的结构体系,包括BeanFactory接口的功能与实现,以及BeanDefinition接口在描述Bean对象中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.前言

  lz最近打算再过一遍Spring,温故而知新,想当年初入IT时不知从何入手,甚是捉急,IOC、AOP看了无数遍也不知其解,随着工作经验的累积,渐渐悟到了学习路线,整理出来,供大家研讨。

二. 本编讲点

无论你是小白还是老司机,Spring三大特性一定背吐了:
1.IOC(控制反转) 2.DI(依赖注入) 3.AOP(面向切面);
其中,IOC和DI是紧密相连的,所以本篇文章主要讲一讲什么是IOC/DI,到底有什么用!

三.什么是IOC,有什么用?

  首先IOC是个容器,官方一点的话来说,最主要是完成了完成对象的创建和依赖的管理注入等等,这句话大家应该都知道,个人理解就是简化了开发,直接上代码感受一下,我们模拟一个购物的场景

/**
 * 〈商场〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Shop {
    public Person person;
    public String name;

    public Shop(Person person) {
        this.person = person;
        setName("打开淘宝");

    }

    public void open() {
        System.out.println(getName());
        person.say();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Person {
    public Pick pick;

    private String name;

    public Person(Pick pick) {
        this.pick = pick;
        setName("Jack MA");
    }

    public void say() {
        System.out.println("我是"+getName()+",我要开始买东西了!");
        pick.pick();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

/**
 * 〈挑选物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Pick {
    public Pay pay;
    private String commodity;

    public Pick(Pay pay) {
        this.pay = pay;
        setCommodity("防脱发洗发液");
    }

    public void pick() {
        System.out.println("我挑选了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}
/**
 * 〈挑选物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Pick {
    public Pay pay;
    private String commodity;

    public Pick(Pay pay) {
        this.pay = pay;
        setCommodity("防脱发洗发液");
    }

    public void pick() {
        System.out.println("我挑选了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}

  ok了,这一个简单的场景就模拟出来了,为了能更好的体会IOC的好处,Lz特意写的麻烦些,再写个测试接口跑一下

/**
 * 〈一句话功能简述〉<br>
 * 〈测试接口〉
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {

    @RequestMapping(value = "/demo")
    @ResponseBody
    public String demo() {
        Pay pay = new Pay();
        Pick pick = new Pick(pay);
        Person person = new Person(pick);
        Shop shop = new Shop(person);
        shop.open();
       return "ok";
    }
}

好了,用原始的方法,我们通过对象的传递来维护依赖关系,那么我们用spring的情况下,该怎么写呢?

/**
 * 〈商场〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringShop")
public class Shop {

    @Autowired
    private Person person;

    public String name;

    public Shop() {
        setName("打开淘宝");

    }
    public void open() {
        System.out.println(getName());
        person.say();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

/**
 * 〈顾客〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPerson")
public class Person {
    
    @Autowired
    private Pick pick;

    private String name;

    public void say() {
        System.out.println("我是" + getName() + ",我要开始买东西了!");
        pick.pick();
    }

    public Person() {
        setName("Jack MA");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/**
 * 〈挑选物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPick")
public class Pick {

    @Autowired
    private Pay pay;

    private String commodity;

    public Pick() {

        setCommodity("防脱发洗发液");
    }

    public void pick() {
        System.out.println("我挑选了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}
/**
 * 〈付款〉<br>
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPay")
public class Pay {

    private String payWay;

    public  Pay(){
        setPayWay("支付宝");
    }

    public String getPayWay() {
        return payWay;
    }

    public void setPayWay(String payWay) {
        this.payWay = payWay;
    }

    public void pay() {
        System.out.println("我用" + getPayWay() + "付款");

    }
}
/**
 * 〈一句话功能简述〉<br>
 * 〈测试接口〉
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@RestController(value = "SpringTestController")
@RequestMapping(value = "/test2")
public class TestController {
    @Autowired
    private com.koolearn.donut.practise.spring.Shop shop;

    @RequestMapping(value = "/demo")
    @ResponseBody
    public String demo() {
        shop.open();
        return "ok";
    }
}

  怎么样,用了注解之后是不是感觉一身轻松,在实际项目中,一个对象不可能只依赖一个对象,大多数是依赖多个甚至十几个,并且依赖关系是n层,维护起来让人崩溃,有了Spring,你不用关心对象的实例化,更不用维护依赖关系,真正的实现了解耦!这就是程序员的春天,哈哈。
By the way,spring注入的另一种方式是xml配置,这里就不说了

四.理解IOC思想

  lz觉得编程难点,是难在理解代码的思想,因为它是抽象的,和数学物理一样,看不见摸不着,正确理解并掌握了设计思想,才会从码农进阶为互联网精英,如果你是一个善于思考的人,可能会发现我们所学习到的知识似乎都遵循着某种规律,即使再抽象的东西,其本质思想,也会跟现实生活中有点联系,就拿IOC来说,就是把new对象的权利交给了Spring容器来处理,在现实中,不就是把一些自己嫌麻烦的事情,交给第三方来处理嘛;举个栗子,n多年前我们打车去一个地方,只能出门拦空车,节假日和大冬天一等就是好久,有时还要拼车,你需要告诉司机你去哪,司机看顺不顺路,再考虑是否带上你,这就可以看成是自己new 一个对象(出租车),乘客的目的地就像是一种依赖关系;现在好了,下一个app,输入起点终点,交给平台(容器)就什么也不用管了,美滋滋。
   好了,有点扯远了,知道了IOC的作用,我们接下来看看源码,研究一下是怎么实现的吧!

五.Spring IOC的结构体系

(1)BeanFactory
大名鼎鼎的Bean工厂,Spring中Bean的创建是典型的工厂模式,看一下代码:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

  第一次看源码的时候一脸蒙蔽,包那么多,那么复杂,不过研究东西就要静下心来,这个就是Spring核心的Bean工厂定义,好像是2001年写的,这个类是Spring中所有的bean工厂,也就是俗称IOC容器的祖宗(膜拜3秒钟),各种IOC容器都只是它的实现或者为了满足特别需求的扩展实现,包括我们平时用的ApplicationContext。从接口中的方法名字不难看出,这些工厂的实现最大的作用就是根据bean的名称或者类型等等,返回一个bean的实例
来看一下他的关系图
在这里插入图片描述
BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是从图中能发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。
  现在我们来思考一件事情:一个工厂想要拥有这个功能,需要具备几个因素呢?
1.需要持有各种Bean的定义,否则无法正确的完成bean的实例化;
2.需要持有bean之间的依赖关系,否则在实例化过程中也会出现问题。例如上例,我们只是各自持有person和shop,却不知道他们的依赖关系,那么在shop初始化后,调用open方法时,就会报空指针。这是因为shop其实没有真正的被正确的实例化。
3.以上两种都要依赖我们写依赖关系的定义,我们暂且认为是xml文件,那么我们需要一个工具来读取它
在我的理解中,只要满足上述三种条件,便可以创建一个bean工厂,当然,Spring还有更高级的做法,以上只是我直观的去想如何实现IOC。
  接下来接着思考,第一步,如何持有Bean的定义?我们知道在Spring的Xml配置文件中,有个lazy-init的属性,这就说明我们是可以控制bean在何时实例化的。这个属性默认是false的,也就是说Spring容器初始化后,配置了延迟加载的bean都还未产生,只是存储了bean的定义,而非实例,在需要的时候,它才会出现,接下来,再介绍一个祖宗级别的接口:BeanDefinition

package org.springframework.beans.factory.config;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
import org.springframework.lang.Nullable;

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;

    void setParentName(@Nullable String var1);

    @Nullable
    String getParentName();

    void setBeanClassName(@Nullable String var1);

    @Nullable
    String getBeanClassName();

    void setScope(@Nullable String var1);

    @Nullable
    String getScope();

    void setLazyInit(boolean var1);

    boolean isLazyInit();

    void setDependsOn(@Nullable String... var1);

    @Nullable
    String[] getDependsOn();

    void setAutowireCandidate(boolean var1);

    boolean isAutowireCandidate();

    void setPrimary(boolean var1);

    boolean isPrimary();

    void setFactoryBeanName(@Nullable String var1);

    @Nullable
    String getFactoryBeanName();

    void setFactoryMethodName(@Nullable String var1);

    @Nullable
    String getFactoryMethodName();

    ConstructorArgumentValues getConstructorArgumentValues();

    default boolean hasConstructorArgumentValues() {
        return !this.getConstructorArgumentValues().isEmpty();
    }

    MutablePropertyValues getPropertyValues();

    default boolean hasPropertyValues() {
        return !this.getPropertyValues().isEmpty();
    }

    void setInitMethodName(@Nullable String var1);

    @Nullable
    String getInitMethodName();

    void setDestroyMethodName(@Nullable String var1);

    @Nullable
    String getDestroyMethodName();

    void setRole(int var1);

    int getRole();

    void setDescription(@Nullable String var1);

    @Nullable
    String getDescription();

    boolean isSingleton();

    boolean isPrototype();

    boolean isAbstract();

    @Nullable
    String getResourceDescription();

    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:
在这里插入图片描述
  Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:
在这里插入图片描述
  再看看源码,这个便是spring中的bean定义接口,所以其实我们工厂里持有的bean定义,就是一堆这个玩意,或者是他的实现类和子接口。这个接口并非直接的祖宗接口,他所继承的两个接口一个是core下面的AttributeAccessor,继承这个接口就以为这我们的bean定义接口同样具有处理属性的能力,而另外一个是beans下面的BeanMetadataElement,字面翻译这个接口就是bean的元数据元素,它可以获得bean的配置定义的一个元素。在XML文件中来说,就是会持有一个bean标签。
  仔细观看,能发现beanDefinition中有两个方法分别是String[] getDependsOn()和void setDependsOn(String[] dependsOn),这两个方法就是获取依赖的beanName和设置依赖的beanName,这样就好办了,只要我们有一个BeanDefinition,就可以完全的产生一个完整的bean实例。
  今天就先到这里吧,lz菜的抠脚,也是边看边写的,费了很大劲才梳理出一点东西,更多的是看到哪写到哪,IOC的实现原理用到了反射,我接下来想想怎么写进去。
  文中若有欠妥的地方,希望大家指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值