手写Spring框架学习Spring原理之IOC(一)


GitHub地址: https://github.com/dixinjoker/king-spring-theory

为什么使用Spring框架

Spring-IOC原理

IOC是什么?

  • 控制反转。将创建对象的权力交给Spring工厂,从new对象 ====》 向工厂去获取一个对象。

为什么用IOC?

  • 面向接口编程,较之new对象的方式更利于解耦(跟具体的实现类解耦)。
  • IOC工厂达成易于扩展、替换实现类的功能。使程序更灵活。
  • AOP的基石

Spring-IOC的实现原理

ApplicationContext 扩展了 BeanFactory 接口,持有了一个BeanFactory 。BeanFactory 是Spring中bean的管理工厂。

开始手写

难道用脚写? 在这里插入图片描述

注意:idea新建项目为Spring Initializr类型(什么都不用勾选),这里我用的JDK版本是1.8。

项目结构项目结构

第一步:定义BeanFactory 接口

IOC是一个bean的工厂,为我们提供Bean对象。
定义BeanFactory  接口

第二步:提供实现类 DefaultBeanFactory
  1. 创建实现类DefaultBeanFactory。
public class DefaultBeanFactory implements BeanFactory {
    @Override
    public Object getBean(String beanName) throws Throwable {
        //如何创建这个名字的 bean实例
        //需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
        return null;
    }

2.test下sample包中创建 用户接口与实现类

public interface UserService {
    void fun0();
}
public class UserServiceImpl implements UserService{
    @Override
    public void fun0() {
        System.out.println(this + ".fun0()");
    }
}

3.创建BeanDefinition 类,实现用户给出他的bean定义信息的功能

public class BeanDefinition {

    private Class<?> beanClass;
    
    public BeanDefinition(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public Class<?> getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

}
第三步:提供 BeanDefinition 注册功能

提供给用户一个可以给入bean的定义信息的功能 BeanDefinition 注册功能

创建 BeanDefinitionRegistry接口 ,提供 注册功能接口

public interface BeanDefinitionRegistry {
    void registerBeanDefinition(String beanName,BeanDefinition beanDefinition);
} 
第四步:实现一个最简单的BeanFactory

1.完善DefaultBeanFactory

public class DefaultBeanFactory implements  BeanFactory,BeanDefinitionRegistry {

    //Bean定义的map,以beanName为key
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    @Override
    public Object getBean(String beanName) throws Throwable {
        //如何创建这个名字的 bean实例
        //需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
        Object bean = null;
        bean = createBeanInstance(beanName);
        return bean;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        //注册保存====》使用map存=====》在getBean方法中使用
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    private Object createBeanInstance(String beanName) throws IllegalAccessException, InstantiationException {
        //使用最简单方法(调用构造方法)来创建实例
        return beanDefinitionMap.get(beanName).getBeanClass().newInstance();
    }
}

在这对以上代码进行说明:
大手子应该瞄一眼就看懂了吧。滑稽.jpg。简单说明一下

  1. DefaultBeanFactory 类实现bean工厂和bean定义注册接口;
  2. 选用map存放bean定义信息(new一个concurrentHashMap),key为beanName;
  3. registerBeanDefinition()方法内往map里面存beanName和beanDefinition;
  4. getBean()中创建bean实例并返回bean
  5. 快捷生成createBeanInstance(),使用最简单的构造方法创建实例。

2 .写完生成getBean()的测试类进行测试。没错,写完(一半了)!

class DefaultBeanFactoryTest {

    DefaultBeanFactory defaultBeanFactory;

    @BeforeEach
    public void init() throws Throwable {
        defaultBeanFactory = new DefaultBeanFactory();
        BeanDefinition bd = new BeanDefinition(UserServiceImpl.class);
        defaultBeanFactory.registerBeanDefinition("userService",bd);
    }

    @Test
    void getBean() throws Throwable {
        UserService userService = (UserService) defaultBeanFactory.getBean("userService");
        userService.fun0();
    }
}

在这对以上代码进行说明:

  • 对我自己说明,哈哈哈哈
  • 完成初始化,进行测试。
  • 这里需要注意的是 @BeforeEach注解。我用的是junit 5,如果是使用junit 4的靓仔这里替换为@Before食用。

运行结果:

运行结果
在这里插入图片描述

第五步:实现单例的支持

用户需要在beanDefinition中告诉我们这个Bean是单例
我为什么贴源码贴的这么全啊,这都不能把各位靓仔骗到我的 gayHub上了。

  • 在BeanDefinition中加上以下代码,get,set方法没贴
public static enum Scop{
        singleton,prototype;
    }
    //默认单例
    private Scop scop = Scop.singleton;
第六步:增加单例的处理

DefaultBeanFactory 增加单例的处理

	//保存单例实例,因为不知道是什么类,所以v为Object
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

此时我们的getBean()改为:

 	@Override
    public Object getBean(String beanName) throws Throwable {
        //1.如何创建这个名字的 bean实例
        //2.需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息

        //3.单例 第一次获取,创建实例,后续获取前面创建的实例===》如何持有当前的单例实例===》使用map
        //判断是否是单例的bean
        BeanDefinition bd = this.beanDefinitionMap.get(beanName);
        if (null == bd){
            throw new IllegalArgumentException(beanName+"bean 不存在!");
        }
        Object bean = null;
        if (bd.getScop()==BeanDefinition.Scop.singleton){
            bean = this.singletonObjects.get(beanName);
            if (null == bean){
                //需要创建bean单例
                bean = createBeanInstance(beanName);
                this.singletonObjects.put(beanName,bean);
            }
        }
        else {
            bean = createBeanInstance(beanName);
        }
        return bean;
    }

看似牛b, 用脑壳一想如果是并发怎么办?怎么保证创建的是单例?

那就再改进,使用 双重检查+锁机制。getBean()又被 了:

public Object getBean(String beanName) throws Throwable {
        //1.如何创建这个名字的 bean实例
        //2.需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息

        //3.单例 第一次获取,创建实例,后续获取前面创建的实例===》如何持有当前的单例实例===》使用map
        //判断是否是单例的bean
        BeanDefinition bd = this.beanDefinitionMap.get(beanName);
        if (null == bd){
            throw new IllegalArgumentException(beanName+"bean 不存在!");
        }

        Object bean = null;
        if (bd.getScop()==BeanDefinition.Scop.singleton){
            bean = this.singletonObjects.get(beanName);
            if (null == bean){//第一次检查
                //需要创建bean单例
                synchronized (this.singletonObjects) { //双重检查+锁机制
                    bean = this.singletonObjects.get(beanName);//查看此刻有无
                    if (null == bean) { //第二次检查
                        bean = createBeanInstance(beanName);
                        this.singletonObjects.put(beanName, bean);
                    }
                }
            }
        }
        else {//非单例
            bean = createBeanInstance(beanName);
        }
        return bean;
    }

猛男搓搓小手开始测试

美滋滋

延伸:单例Bean能否初始时实例化

单例bean能否在容器启动时完成实例化?有没有必要?有什么好处?怎么做?

写在最后:

关于延伸的部分我也有写,但是不知道有没有写出来的必要。写完这些我已经感觉写的臭长了。
博主第一次写文章,修修改改的不尽人意。应该也没多少人看,看的也只有靓仔。滑稽.jpg
不周到之处希望靓仔们海涵之余尽管指正,谢过。
喝水不搞挖井人,在这感谢众生,我爱这个魔幻的世界!(在他们身上学到太多)。
我,将继承挖井之意志。将吾之学,尽皆共享。初步打算是先记录Spring里面涉及的源码,这篇文章只是个开始。


推荐阅读: 手写Spring框架学习Spring原理 之xml(二)
联系方式:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值