浅谈Spring IOC (IOC模型)

522 阅读7分钟

@[toc]

前言

关于目前我们先解读关于Spring的IOC部分,这个也是个大头。下面是某个大佬的文章,不过这里并不是说查看人家的文章,而是通过自己的理解实现一个简单的IOC容器,后面再将其“标准化”和Spring对接实现对Spring的拓展。当然我们现在这边还是容器部分的实现。这部分实现了,那个后面的不就自然而然嘛。

(我本人没有深入去了解,我们先根据自己的理解去实现一个我们自己理解的IOC,然后再去对比修正)

单纯的通过文章目录 不难发现其实关于 这部分的实现大致还是三大块

  1. 对XML的解析读取(前面的BeanDefinition我认为其实还是对Bean怎么在XML当中自动存储进行初始化定义)
  2. 对读取到的 XML 文件解析得到对象
  3. 通过对象进行组装,实现相应的模式(例如功单例模式,多例模式,包括后面的AOP面向切面也是在这部分后面)

现在我们大致应该是知道了一个流程,这个流程其实和先前的谋篇文章的思路有点类似。

Java Dome(AOP模式回顾小Dome)

其实这里也是使用到了我们类似的一个模式

那么这里为了规范我们自己去简单规划一下,还是大体分三个部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4PnZ0Oib-1638954118768)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208083244164.png)]

说明

BeanDaulft

首先是 BeanDaulft 这个是对Java Bean 进行一个简单的初始化,如何对一个类进行存储定义。这里考虑到文章的篇幅和简单实现,所以我们这次不是通过 XML 去实现,而是通过 peropriter 配置文件去实现,对我们这边容器进行定义

BeanFactory

这个玩意主要就是读取配置文件当中的玩意,之后我们把配置文件当中的内容组合在一块,也就是通过java反射和内省(Class.forname(),Introspector)我们读取到IO流然后组装。

BuilderBean

这个其实是对前面的那两个玩意进行调配其实,也就是我们获取一个搞定好的BeanFactory,之后我们通过BeanFactory进行对象的创建。这个非常重要,因为如果我们想要实现单例模式,或者多例模式我们就必须得到一个BeanFactory然后对其进行封装。

对比Spring操作

通过我们说明我们大致明白了每一个包所充当的功能角色。那么具体实现的结果其实大致会和前面那篇文章 实现 AOP 的类似

package day07;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class AOPDomeTest {
    public static void main(String[] args) {

        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("day07/bean.properties");
        try {
            BeanFactory beanFactory = new BeanFactory(new InputStreamReader(in,"utf8"));
            SendAgentFindHouse sendAgentFindHouse = (SendAgentFindHouse) beanFactory.getBean("bean.SendAgentFindHouse");
            BuyHouse buyHouse = (BuyHouse) sendAgentFindHouse.SendAgent();
            buyHouse.buyHouse();

        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
}

这个是那个AOP最终调用的代码 你会发现一个神奇的玩意叫做:

beanFactory.getBean("bean.SendAgentFindHouse");

之后你再对比Spring最原始使用xml注解实现的getBean() 的调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NTbJsyLu-1638954118769)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208084910159.png)]

这里Spring通过上下文 Context 来获取了我们的配置文件,这个配置文件放了这个东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9eDQzoAX-1638954118769)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208085950277.png)]

我通过id Hello 我就是获取了 com.huterox.pojo.Hello 这个Class 然后通过反射去做,只不过前面是那个通过

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0S1fI9wM-1638954118770)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208090153962.png)]

懂了吧,你再看看我先前的项目结构(这些都是防在 day07这个包下面的(至于是不是第七天学的我就不知道了,有点遗憾的是那个源代码找不到了,不然我不会去再一遍))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IFv6lpO-1638954118770)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208090240888.png)]

OK 那么现在我们通过对比 Spring 最原始的一个操作之一 搞清楚了我们这个几个包到底要做啥。那么我们就实际动动小手

我们这边为了先简便一点,我们就先把 properites 文件命名为 Application 毕竟我们今天没有上下文支持。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VAz1JKJ6-1638954118771)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208161257494.png)]

大概长这样,目前(不着急,咱们还是要慢慢优化滴)

那么首先我们这个还是比较原始滴,在我们自己定义一个类的时候要写你想要对应的ID 和所在包名的类

也就是先前那种写法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RjMhJTIq-1638954118771)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208092734374.png)]

后面我在通过注解的方式来实现,还是比较简单滴。

V0.001 初步模型

OK那么现在我们开始来探讨一下先如何实现那个获取配置文件,然后就拿到我们生成的对象的操作。

效果

行,咱们先来看看结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tl5x79W1-1638954118772)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208164110142.png)]

首先咱们是在 Test包下面创建了一个Hello类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbIOnPvU-1638954118773)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208164151775.png)]

这里咱们还是先探讨最简单的模式,假设我们只需要简单的去让它创建一个对象。先不需要去给具体的对象值(当然这个其实也是用反射的厉害之处,管你是不是公开的,私有的我可以给你搞出来)

这个时候你可能有点迷糊,这玩意我直接new不香吗。首先我们搞清楚咱们的需求,如果我们是自己写的项目,那当然每一个包都可以管理的好好的。但是如果这是一个大型项目,我们必然需要一个机制来统一管理我们所有的类,也可说是 “中央集权” 那么为什么要用反射呢。要知道直接 new 的效率可比反射高多了(具体可以查看这篇文章(Java反射和new效率对比)这不是我写的),很简单为了拓展未知业务,为了方便统一处理。通过这个我可以让我的核心组件知道可以使用由别人创建的类,举个例子就是 Spring。

实现

说了那么多咱们来看看大概的实现,由于是最简单的版本,所以嘛,超级简单的。

咱们这里不像那个 Spring 需要那么多过滤之类的(我敢打赌 Spring 对 XML 的支持代码都得一大堆)咱们就直接获取配置文件就可以。

而且 咱们现在还没有用到注解,而且不需要实现某些模式,所以直接搞。那么这里就用到了两个类实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K4C5fFdO-1638954118774)(C:\Users\31395\AppData\Roaming\Typora\typora-user-images\image-20211208165619870.png)]

由上而下:

package BeanDaulft;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

public class BeanReader {
    private InputStreamReader inputStreamReader;
    private Properties properties = new Properties();
    public BeanReader(){}
    public BeanReader(InputStreamReader in) throws IOException {
        this.inputStreamReader = in;
        properties.load(in);
    }

    public Properties GetBeanReader(){
        return properties;
    }

    public Properties GetBeanReader(String FileName) throws IOException {
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(FileName);
        properties.load(new InputStreamReader(in,"utf8"));
        return properties;
    }
}

package BeanFactory;

import BeanDaulft.BeanReader;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

public class BeanMake {
    private Properties prop;
    private BeanReader beanReader;

    public BeanMake(String Path) throws IOException {
        beanReader = new BeanReader();
        Properties properties = beanReader.GetBeanReader(Path);
        this.prop = properties;

    }

    public BeanMake(Properties prop){
        this.prop = prop;
    }

    public Object getBean(String name) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Class<?> beanClass = Class.forName(prop.getProperty(name));
        Object bean = beanClass.getConstructor().newInstance();
        return bean;
    }
}

接下来就是调用嘛

package Test;

public class Hello {
    public void SayHello(){
        System.out.println("博主真真帅");
    }
}

Hello = Test.Hello
package Test;

import BeanFactory.BeanMake;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        BeanMake beanMake = new BeanMake("Application.properties");
        Hello hello = (Hello) beanMake.getBean("Hello");
        hello.SayHello();
    }
}

没错,现在我们就相当于有了一个容器。

至于 先前 Spring 老是扯皮的 IOC 反转控制 ,其实也是这个,所谓反转,其实就是依赖转移,我们通过 BeanFactroy去创建我们的对象

而不是直接 new 一个 ,好处是方面拓展。那么看完这个咱们就可以再去好好看看Spring源码了~

总结

这个呢,其实还只是我自己的理解,不太清楚对不对。不过慢慢纠正嘛,我这个就是单纯的看了看目录,思维导图加上自己的Spring 的一个体验 觉得应该是这样的。那么之后就是 在未来将 开设 Spring 源码研读,JAVA JVM 学习以及 Go 语言专栏。 那么今天暂时到这里,后面我再看看然后验证猜想对不对,实现实现咱们这个小Dome。 最最后 就是那个 关于 WhiteHole 博客社区的源码,这个项目还在开发当中,等能够上线了在推送GitHub,主要是现在缺一个后台管理的前端模板,这个我后面找找。然后就快期末了时间不太够~