从零写Spring注解版框架系列 IoC篇 (1) 框架设计

本文的注解版IoC框架跟其他手写IoC框架的不同之处在与:在实现了 @Component 和 @Autowired 的同时还实现了@Qualifier,并解决单例模式下循环依赖的问题,以上3个注解的使用效果参照 Spring 。

项目 Github 地址为:https://github.com/linshenkx/winter-core
相关文章地址:
从零写Spring注解版框架系列 IoC篇 (2)实现 @Component、@Autowired、@Qualifier注解

一 设计思想

1 IoC的定义

IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。

所谓 IoC ,就是由 Spring IoC 容器来负责对象的生命周期和对象之间的关系

2 Spring中的 IoC

模块结构

传统xml模式下的Spring实现IoC由几个模块组成:

  1. Resource和ResourceLoader
    Spring 将资源的定义和资源的加载区分开:
    • Resource:统一资源,为 Spring 框架所有资源的抽象和访问接口
    • ResourceLoader:统一资源定位,主要应用于根据给定的资源文件地址,返回对应的 Resource
  2. BeanDefinition
    用来描述 Spring 中的 Bean 对象
  3. BeanDefinitionReader
    资源解析器,用于读取 Spring 的配置文件的内容,并将其转换成 Ioc 容器内部的数据结构 :BeanDefinition
  4. BeanFactory
    一个非常纯粹的 bean 容器,它是 IoC 必备的数据结构,其中 BeanDefinition 是它的基本结构。BeanFactory 内部维护着一个BeanDefinition map ,并可根据 BeanDefinition 的描述进行 bean 的创建和管理。
  5. ApplicationContext
    Spring 容器,也叫应用上下文,与我们应用息息相关。它继承 BeanFactory ,是 BeanFactory 的扩展升级版

Spring IoC 的每一个模块都自成体系,设计精妙但也确实复杂繁重,初学者难免觉得巍然不可近。
单从原理思路出发的话则简单很多,Spring IoC 无非就是替你管理了类的实例,在你需要的时候给你注入进去。

工作原理
IoC 初始化

IoC初始化阶段负责从用户指定路径获取bean的描述和配置信息,将bean的描述信息收归 BeanFactory 的HashMap管理。
这个阶段的操作目标是 bean的描述信息(BeanDefinition)而不是 Bean本身
代码:

ClassPathResource resource = new ClassPathResource("bean.xml"); 
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 
reader.loadBeanDefinitions(resource); 
  1. 资源定位
    用户给定一个资源路径(如xml的路径,用file或者url等),ResourceLoader 根据策略从该路径获取到 Resource
  2. 装载
    新建 IoC 容器 BeanFactory,根据 BeanFactory 创建一个 BeanDefinitionReader 对象对 Resource 进行解析,获取到 BeanDefinition
  3. 注册
    将得到的 BeanDefinition 注入到一个 HashMap 容器中,IoC 容器就是通过这个 HashMap 来维护这些 BeanDefinition 的
ResourceLoader
BeanDefinitionReader
统一管理
资源路径
Resource
BeanDefinition
BeanDefinition的HashMap
加载Bean

默认使用懒加载策略,即等到用户需要这个 Bean 的时候再根据 BeanDefinition 完成 Bean 的加载
这里的功能扩展比较多,但最主要解决的是 Bean的属性填充(@Value)和依赖处理(@Autowired)
另外Bean的加载主要有单例(Singleton)和原型(prototype)两种模式
需要注意的有 循环依赖 问题,对于单例模式出现的循环依赖Spring会进行分析检测,对于原型模式则不管,由用户负责。

二 设计思路

  1. 核心:@Component和@Autowired
    上面的分析是基于xml的,如果是使用注解的话还会简单一些,免去了xml 读取、解析的操作。
    核心功能通过 @Component 和 @Autowired 两个注解即可完成。
    其中,@Component 负责将 Bean 交由 IoC 容器处理,@Autowired 负责将 IoC 容器管理的 Bean 注入的对应属性中

  2. @Qualifier
    另外,@Autowired 默认是按照类型匹配的,Java官方还有一个@Resource注解是按照名称匹配的,这里我们还是使用主流的@Autowired注解,同时,为了证明不是偷懒我还会实现 @Qualifier 注解来指定名称。

  3. 单例 VS 原型
    现在先完成单例模式,原型模式用得不多,不也推荐使用,对于有状态的Bean推荐使用 ThreadLocal 来对状态进行屏蔽。

  4. IoC 容器
    首先我们的功能核心放在一个 ApplicationContext 下,其实就只是一个 BeanFactory 而已。
    Bean的描述信息BeanDefinition放在一个Map(beanDefinationFactory)里面,因为我们用的是注解,BeanDefinition直接用对应类的 Class 对象就好。
    Bean实例化后的单例对象也放在一个Map里面(singletonbeanFactory)。

  5. 确定加载的Bean
    由于我们可以在按照类型寻找的前提下根据用户指定名进行匹配,所以不管是使用@Component进行Bean发现还是使用@Autowired进行Bean注入,要确定一个 Bean 的实例都需要两个信息:type(类型)和 beanId(名字)

    这里比较麻烦的问题是父类可以由子类注入,接口可以由实现类注入。而且类型和名字并不是独立匹配的,是在类型匹配的前提下进行名字识别,以获取唯一bean

    为了达到匹配效果,我使用了这种结构来描述bean信息(beanDefinationFactory) :Map<String,Map<String,Class>>
    ,即全称类名(type)+自定义类名(beanId)==> 真类信息

    为了达到每个bean只实例化一次,我用全称类名来确定唯一bean实例(singletonbeanFactory):Map<String,Object>

  6. 加载流程
    同一个bean可能有多个类型(比方说其父类、接口等),所以在beanDefinationFactory中多个描述信息可能对应的是一个Class
    首先根据type和beanId获取Class对象
    再根据Class对象从singletonbeanFactory中获取单例

三 功能目标

根据上文的设计思路,我们这个简单的 IoC 注解版框架的 最基本功能是实现以下几个注解:

1 注解设计

  1. @Component
    将注解的 Bean 纳入 IoC框架管理,同时可以通过@Component的value指定该Bean的名称(beanId),不指定则为类名首字母小写,type为所有超类、接口和自身
  2. @Autowired
    将 IoC 框架管理的 Bean 注入注解标记的属性
  3. @Qualifier
    搭配 @Autowired 实现在类型匹配的前提下按名称匹配。

这么说是不是很简单?下面再来细化以下规则

2 规则细化

  1. IoC框架对于一个单例类只实例化一次
  2. 只使用@Autowired注解的情况下默认按类型匹配
    1. 如果 IoC 框架中仅有一个该类型的 Bean,则实例化该Bean
    2. 如果 IoC 框架中有多个该类型的 Bean,则产生歧义。此时根据 @Autowired 标记的属性名(注意不是类型名)来识别唯一的Bean
      1. 如果在该类型的多个Bean找得到名称匹配的Bean,则实例化该bean
      2. 如果找不到,则抛出异常,提醒 存在多个同类不同名Bean,无法识别
  3. 如果在使用@Autowired的同时使用@Qualifier注解,则根据@Qualifier的value作为Bean的名称在该类型下寻找匹配的bean
    1. 如果找得到,则实例化该Bean
    2. 如果没找到,则抛出异常,提醒该名称的Bean不存在。注意,这时即使该类型下只有一个Bean,也会抛出异常。

3 应用举例

有类图如下,其中HelloController、HelloServiceImpl、HelloServiceImpl1、HelloServiceImpl2都使用了@Component注解。

类图
各类如下,以下注入可正常完成。

@Component
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello!"+name;
    }
}

@Component("myHelloService1")
public class HelloServiceImpl1 implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello1!"+name;
    }
}

@Component("myHelloService2")
public class HelloServiceImpl2 implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello2!"+name;
    }
}

@Component
public class HelloController {
    //根据属性名匹配,匹配到 HelloServiceImpl
    @Autowired
    private HelloService helloService;

    //根据属性名匹配,匹配到 HelloServiceImpl2
    @Autowired
    private HelloService myHelloService2;

    //根据指定名匹配,匹配到 HelloServiceImpl1
    @Autowired
    @Qualifier("myHelloService1")
    private HelloService helloService1;

    //根据指定名匹配,匹配到 HelloServiceImpl2
    @Autowired
    @Qualifier("myHelloService2")
    private HelloService helloService2;

    //根据类型匹配,匹配到 HelloServiceImpl2
    @Autowired
    private HelloServiceImpl2 helloService22;

    public String helloDefault(String name){
        return helloService.sayHello(name);
    }
    public String hello1(String name){
        return helloService1.sayHello(name);
    }
    public String hello2(String name){
        return myHelloService2.sayHello(name);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值