Spring中的IOC

IoC全称是Inversion of Control,就是控制反转,他其实不是spring独有的特性或者说也不是java的特性,他是一种设计思想。而DI(Dependency Injection),即依赖注入就是Ioc的一种实现方式。关于Ioc和DI的具体定义和优缺点等大家可以自行查找资料了解一下,这里就不详细赘述,总之spring的IoC功能很大程度上便捷了我们的开发工作。

在实现我们的Ioc之前,我们先了解一下spring的依赖注入,在spring中依赖注入有三种方式,分别是:

接口注入(Interface Injection)

设值方法注入(Setter Injection)

构造注入(Constructor Injection)

@Component

public class ComponentA {

@Autowired // 1.接口注入

private ComponentB componentB;

@Autowired // 2.设值方法注入

public void setComponentB(ComponentB componentB) {

this.componentB = componentB;

}

@Autowired // 3.构造注入

public ComponentA(ComponentB componentB) {

this.componentB = componentB;

}

}

循环依赖注入

如果只是实现依赖注入的话实际上很简单,只要利用java的反射原理将对应的属性‘注入’进去就可以了。但是必须要注意一个问题,那就是循环依赖问题。循环依赖就是类之间相互依赖形成了一个循环,比如A依赖于B,同时B又依赖于A,这就形成了相互循环。

// ComponentA

@Component

public class ComponentA {

@Autowired

private ComponentB componentB;

}

// ComponentB

@Component

public class ComponentB {

@Autowired

private ComponentA componentA;

}

那么在spring中又是如何解决循环依赖问题的呢,我们大致说一下原理。

如果要创建一个类,先把这个类放进'正在创建池'中,通过反射等创建实例,创建成功的话就把这个实例放入创建池中,并移除'正在创建池'中的这个类。每当实例中有依赖需要注入的话,就从创建池中找对应的实例注入进去,如果没有找到实例,则先创建这个依赖。

利用了这个正在创建的中间状态缓存,让Bean的创建的时候即使有依赖还没有实例化,可以先把Bean放进这个中间状态,然后跑去创建那个依赖,假如那个依赖的类又依赖与这个Bean,那么只要在'正在创建池'中再把这个Bean拿出来,注入到这个依赖中,就可以保证Bean的依赖能够实例化完成。再回头来把这个依赖注入到Bean中,那么这个Bean也实例化完成了,就把这个Bean从'正在创建池'移到'创建完成池'中,就解决了循环依赖问题。

虽然spring巧妙的避免了循环依赖问题,但是事实上构造注入是无法避免循环依赖问题的。因为在实例化ComponentA的构造函数的时候必须得到ComponentB的实例,但是实例化ComponentB的构造函数的时候又必须有ComponentA的实例。这两个Bean都不能通过反射实例化然后放到'正在创建池',所以无法解决循环依赖问题,这时候spring就会主动抛出BeanCurrentlyInCreationException异常避免死循环。

* 注意,前面讲的这些都是基于spring的单例模式下的,如果是多例模式会有所不同,大家有兴趣可以自行了解。

实现IOC

现在可以开始实现IOC功能了

增加注解

先在zbw.ioc包下创建一个annotation包,然后再创建一个Autowired的注解。这个注解的Target只有一个ElementType.FIELD,就是只能注解在属性上。意味着我们目前只实现接口注入的功能。这样可以避免构造注入造成的循环依赖问题无法解决,而且接口注入也是用的最多的方式了。如果想要实现设值方式注入大家可以自己去实现,实现原理几乎都一样。

package com.zbw.ioc.annotation;

import ...

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Autowired {

}

实现IOC类

package com.zbw.ioc;

import ...

@Slf4j

public class Ioc {

/**

* Bean容器

*/

private BeanContainer beanContainer;

public Ioc() {

beanContainer = BeanContainer.getInstance();

}

/**

* 执行Ioc

*/

public void doIoc() {

for (Class> clz : beanContainer.getClasses()) { //遍历Bean容器中所有的Bean

final Object targetBean = beanContainer.getBean(clz);

Field[] fields = clz.getDeclaredFields();

for (Field field : fields) { //遍历Bean中的所有属性

if (field.isAnnotationPresent(Autowired.class)) {// 如果该属性被Autowired注解,则对其注入

final Class> fieldClass = field.getType();

Object fieldValue = getClassInstance(fieldClass);

if (null != fieldValue) {

ClassUtil.setField(field, targetBean, fieldValue);

} else {

throw new RuntimeException("无法注入对应的类,目标类型:" + fieldClass.getName());

}

}

}

}

}

/**

* 根据Class获取其实例或者实现类

*/

private Object getClassInstance(final Class> clz) {

return Optional

.ofNullable(beanContainer.getBean(clz))

.orElseGet(() -> {

Class> implementClass = getImplementClass(clz);

if (null != implementClass) {

return beanContainer.getBean(implementClass);

}

return null;

});

}

/**

* 获取接口的实现类

*/

private Class> getImplementClass(final Class> interfaceClass) {

return beanContainer.getClassesBySuper(interfaceClass)

.stream()

.findFirst()

.orElse(null);

}

}

在知道IOC的原理之后发现其实真的是非常简单,这里用了几十行的代码就实现了IOC的功能。

首先在Ioc类构造的时候先获取到我们之前已经单例化的BeanContainer容器。

然后在doIoc()方法中就是正式实现IOC功能的了。

遍历在BeanContainer容器的所有Bean

对每个Bean的Field属性进行遍历

如果某个Field属性被Autowired注解,则调用getClassInstance()方法对其进行注入

getClassInstance()会根据Field的Class尝试从Bean容器中获取对应的实例,如果获取到则返回该实例,如果获取不到,则我们认定该Field为一个接口,我们就调用getImplementClass()方法来获取这个接口的实现类Class,然后再根据这个实现类Class在Bean容器中获取对应的实现类实例。

测试用例

为了测试我们的Ioc和之前写的BeanContainer编写正确,我们写一下测试用例测试一下。

先在pom.xml添加junit的依赖

...

4.12

...

junit

junit

${junit.version}

test

然后在test包下添加DoodleController、DoodleService、DoodleServiceImpl三个类方便测试

// DoodleController

package com.zbw.bean;

@Controller

@Slf4j

public class DoodleController {

@Autowired

private DoodleService doodleService;

public void hello() {

log.info(doodleService.helloWord());

}

}

// DoodleService

package com.zbw.bean;

public interface DoodleService {

String helloWord();

}

// DoodleServiceImpl

package com.zbw.bean;

@Service

public class DoodleServiceImpl implements DoodleService{

@Override

public String helloWord() {

return "hello word";

}

}

再编写IocTest的测试用例

package com.zbw.ioc;

import ...

@Slf4j

public class IocTest {

@Test

public void doIoc() {

BeanContainer beanContainer = BeanContainer.getInstance();

beanContainer.loadBeans("com.zbw");

new Ioc().doIoc();

DoodleController controller = (DoodleController) beanContainer.getBean(DoodleController.class);

controller.hello();

}

}

看到在DoodleController中输出了DoodleServiceImpl的helloWord()方法里的字符串,说明DoodleController中的DoodleService已经成功注入了DoodleServiceImpl。那么我们的IOC的功能也完成了。

ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC相关推荐

  1. 从零开始实现一个简易的Java MVC框架(六)--加强AOP功能

    前言 在前面从零开始实现一个简易的Java MVC框架(四)--实现AOP和从零开始实现一个简易的Java MVC框架(五)--引入aspectj实现AOP切点这两节文章中已经实现了AOP功能并且引用 ...

  2. 从零开始实现一个简易的Java MVC框架(九)--优化MVC代码

    前言 在从零开始实现一个简易的Java MVC框架(七)--实现MVC中实现了doodle框架的MVC的功能,不过最后指出代码的逻辑不是很好,在这一章节就将这一部分代码进行优化. 优化的目标是1.去除 ...

  3. 依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...

  4. 基于Vue实现一个简易的小程序框架,浅谈kafka | 每日掘金第 194 期

    Hello,又到了每天一次的下午茶时间.酱酱们的下午茶新增优质作者介绍和码上掘金板块,专注于发掘站内优质创作者和优质内容,欢迎大家多提宝贵意见! 酱酱们的下午茶全新改版,欢迎大家多提宝贵意见! 本文字 ...

  5. 设计一个简易的引导任务框架(2) | 4.23粉丝赠书

    今天是4.23世界读书日,公众号向支持的小伙伴们送出下面3本技术图书(三选一)! 参与方式: 本文点赞留言,必须超过20字,以及你想要的图书名字参与活动 积赞最多的前3名读者,将会获得赠书,三选一 活 ...

  6. Java MVC框架性能比较 jsp、struts1、struts2、springmvc3 (转帖)

    为什么80%的码农都做不了架构师?>>>    Java MVC框架性能比较 jsp.struts1.struts2.springmvc3 现在各种MVC框架很多,各框架的优缺点网络 ...

  7. knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案

    knife4j knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量, ...

  8. Java MVC框架性能比较

    Java MVC框架性能比较 - by zvane 现在各种MVC框架很多,各框架的优缺点网络上也有很多的参考文章,但介绍各框架性能方面差别的文章却不多,本人在项目开发中,感觉到采用了struts2框 ...

  9. 如何搭建python框架_从零开始:写一个简单的Python框架

    原标题:从零开始:写一个简单的Python框架 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 你为什么想搭建一个Web框架?我想有下面几个原因: 有一个 ...

最新文章

  1. 常量元素记忆口诀_化学口诀表:帮助学生加深记忆提高解题正确率
  2. Docker for mac安装
  3. ansible playbook lookups组件
  4. C++中关于配置文件的问题
  5. 山大计算机上机复试题目,2010年计算机复试上机 回忆
  6. linux shell 中文件编码查看及转换方法
  7. 网络操作系统 第四章 磁盘管理
  8. Objective-C代码学习大纲
  9. Spark算子--Scala版本 educoder
  10. C#计算MD5结果不一致
  11. Google Play App送审被拒-违反权限政策
  12. 用c++实现蓝桥杯超级玛丽
  13. php滑动拼图验证,如何在PHP环境下实现滑动拼图验证
  14. 基于51单片机的体脂检测系统设计(51+oled+hx711+us100)
  15. awl 多线程SYN***工具0.2版[转]
  16. GWAS计算BLUE值4--联合方差分析演示
  17. ios 渐变透明背景_这不是玩笑!只用线条和背景图来搞定汇报类PPT封面?
  18. 电影售票管理c语言课程设计,C语言课设--电影院售票
  19. 【界面设计系列】关于工业控制界面
  20. ENVI经验|基于多源遥感影像的红树林范围提取3-监督分类

热门文章

  1. java填空题_Java语言基础知识填空题
  2. android listview快速定位,【转】android中ListView的定位:使用setSelectionFromTop实现ListView的position的保持...
  3. Two Paths CodeForces - 14D(暴力+树的直径)
  4. 如何防止token被拦截_Spring Boot+Redis+拦截器+自定义Annotation实现接口自动幂等
  5. 【系统架构设计师】2020-08-06
  6. case when用法java,sql之case when用法详解
  7. JDBC概念快速入门工具类Util的写法
  8. java pdf表单域实现_Java 创建PDF表单域 - 文本框、复选框、列表框、组合框、按钮等...
  9. uC/OS-II源码分析(总体思路 二)
  10. 特征提取(Feature Detection)之——HOG (Histogram of Oriented Gradient)特征