本文节选自《Spring 5核心原理》

在之前的源码分析中我们已经了解到,依赖注入(DI)的入口是getBean()方法,前面的IoC手写部分基本流程已通。先在GPApplicationContext中定义好IoC容器,然后将GPBeanWrapper对象保存到Map中。在GPApplicationContext中设计两个Map:factoryBeanObjectCache保存单例对象的缓存,factoryBeanInstanceCache保存GPBeanWrapper的缓存,变量命名也和原生Spring一致,这两个对象的设计其实就是注册式单例模式的经典应用。


public class GPApplicationContext extends GPDefaultListableBeanFactory implements GPBeanFactory { private String [] configLocations; private GPBeanDefinitionReader reader; //用来保证注册式单例的容器
private Map<String,Object> factoryBeanObjectCache = new HashMap<String, Object>(); //用来存储所有的被代理过的对象
private Map<String,GPBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, GPBeanWrapper>(); ... }

1 从getBean()方法开始

下面我们从完善getBean()方法开始:


@Override
public Object getBean(String beanName) { GPBeanDefinition beanDefinition = super.beanDefinitionMap.get(beanName); try{ //生成通知事件
GPBeanPostProcessor beanPostProcessor = new GPBeanPostProcessor(); Object instance = instantiateBean(beanDefinition);
if(null == instance){ return null;} //在实例初始化以前调用一次
beanPostProcessor.postProcessBeforeInitialization(instance,beanName); GPBeanWrapper beanWrapper = new GPBeanWrapper(instance); this.factoryBeanInstanceCache.put(beanName,beanWrapper); //在实例初始化以后调用一次
beanPostProcessor.postProcessAfterInitialization(instance,beanName); populateBean(beanName,instance); //通过这样调用,相当于给我们自己留有了可操作的空间
return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}catch (Exception e){
// e.printStackTrace();
return null;
}
}

2 instantiateBean()方法反射创建实例


//传一个BeanDefinition,就返回一个实例Bean
private Object instantiateBean(GPBeanDefinition beanDefinition){
Object instance = null;
String className = beanDefinition.getBeanClassName();
try{ //因为根据Class才能确定一个类是否有实例
if(this.factoryBeanObjectCache.containsKey(className)){
instance = this.factoryBeanObjectCache.get(className);
}else{
Class<?> clazz = Class.forName(className);
instance = clazz.newInstance(); this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance);
} return instance;
}catch (Exception e){
e.printStackTrace();
} return null;
}

3 populateBean()方法完成依赖注入


private void populateBean(String beanName,Object instance){ Class clazz = instance.getClass(); if(!(clazz.isAnnotationPresent(GPController.class) ||
clazz.isAnnotationPresent(GPService.class))){
return;
} Field [] fields = clazz.getDeclaredFields(); for (Field field : fields) {
if (!field.isAnnotationPresent(GPAutowired.class)){ continue; } GPAutowired autowired = field.getAnnotation(GPAutowired.class); String autowiredBeanName = autowired.value().trim(); if("".equals(autowiredBeanName)){
autowiredBeanName = field.getType().getName();
} field.setAccessible(true); try { field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName). getWrappedInstance()); } catch (IllegalAccessException e) {
// e.printStackTrace();
} } }

4 GPBeanPostProcessor后置处理器

原生Spring中的BeanPostProcessor是为对象初始化事件设置的一种回调机制。这个Mini版本中只做说明,不做具体实现,感兴趣的“小伙伴”可以继续深入研究Spring源码。


package com.tom.spring.formework.beans.config; public class GPBeanPostProcessor { //为在Bean的初始化之前提供回调入口
public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
return bean;
} //为在Bean的初始化之后提供回调入口
public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
return bean;
}
}

至此,DI部分就手写完成了,也就是说完成了Spring的核心部分。“小伙伴们”是不是发现其实还是很简单的?

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!

如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

原创不易,坚持很酷,都看到这里了,小伙伴记得点赞、收藏、在看,一键三连加关注!如果你觉得内容太干,可以分享转发给朋友滋润滋润!

30个类手写Spring核心原理之依赖注入功能(3)的更多相关文章

  1. 30个类手写Spring核心原理之MVC映射功能(4)

    本文节选自<Spring 5核心原理> 接下来我们来完成MVC模块的功能,应该不需要再做说明.Spring MVC的入口就是从DispatcherServlet开始的,而前面的章节中已完成 ...

  2. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  3. 30个类手写Spring核心原理之环境准备(1)

    本文节选自<Spring 5核心原理> 1 IDEA集成Lombok插件 1.1 安装插件 IntelliJ IDEA是一款非常优秀的集成开发工具,功能强大,而且插件众多.Lombok是开 ...

  4. 30个类手写Spring核心原理之AOP代码织入(5)

    本文节选自<Spring 5核心原理> 前面我们已经完成了Spring IoC.DI.MVC三大核心模块的功能,并保证了功能可用.接下来要完成Spring的另一个核心模块-AOP,这也是最 ...

  5. 30个类手写Spring核心原理之自定义ORM(上)(6)

    本文节选自<Spring 5核心原理> 1 实现思路概述 1.1 从ResultSet说起 说到ResultSet,有Java开发经验的"小伙伴"自然最熟悉不过了,不过 ...

  6. 30个类手写Spring核心原理之Ioc顶层架构设计(2)

    本文节选自<Spring 5核心原理> 1 Annotation(自定义配置)模块 Annotation的代码实现我们还是沿用Mini版本的,保持不变,复制过来便可. 1.1 @GPSer ...

  7. 手写web框架之实现依赖注入功能

    我们在Controller中定义了Service成员变量,然后在Controller的Action方法中调用Service成员变量的方法,那么如果实现Service的成员变量? 之前定义了@Injec ...

  8. 手写webpack核心原理,再也不怕面试官问我webpack原理

    手写webpack核心原理 目录 手写webpack核心原理 一.核心打包原理 1.1 打包的主要流程如下 1.2 具体细节 二.基本准备工作 三.获取模块内容 四.分析模块 五.收集依赖 六.ES6 ...

  9. Spring读书笔记-----Spring核心机制:依赖注入

    spring框架为我们提供了三种注入方式,分别是set注入,构造方法注入,接口注入.今天就和大家一起来学习一下 依赖注入的基本概念 依赖注入(Dependecy Injection),也称为IoC(I ...

随机推荐

  1. Java学习(二十)

    今天学习了Java中的package和import 在包中写了一点作为练习 如果把Test02放到别的包,就需要import到别的包,就像这样,Test02在HelloWorld包 如果删掉impor ...

  2. Java包装类,以及Integer与int之间的比较

    一.Java的基本类型 Java语言中提供了八种基本类型,包括六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型. 整数型,包括byte.short.int.long,默认初始值是0 ...

  3. 一次奇怪的的bug排查过程

    公司对底层基础库进行了重构,线上稳定跑了几天,在查看订单系统的log时,有几条error信息非常的奇怪, orderID:80320180 statemachine error: no event [ ...

  4. 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

    大家好,我是小林. Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. ...

  5. 【POJ1845】Sumdiv【算数基本定理 + 逆元】

    描述 Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine ...

  6. Codeforces 1225G - To Make 1(bitset+状压 dp+找性质)

    Codeforces 题目传送门 & 洛谷题目传送门 还是做题做太少了啊--碰到这种题一点感觉都没有-- 首先我们来证明一件事情,那就是存在一种合并方式 \(\Leftrightarrow\) ...

  7. C/C++内存几大分区和存储空间的布局

    先看一下可执行文件加载进内存后形成的进程在内存中的结构,如下图: 代码区:存放CPU执行的机器指令,代码区是可共享,并且是只读的. 数据区:存放已初始化的全局变量.静态变量(全局和局部).常量数据. ...

  8. python项目——新闻管理系统

    DAO(Data Access Object) 数据访问对象是一个面向对象的数据库接口 控制台的输入输出都是再app.py里面完成的  mysql_db.py import mysql.connect ...

  9. nohup使用

    nohup:不挂断运行 在忽略挂起信号的情况下运行给定的命令,以便在注销后命令可以在后台继续运行. 可以这么理解:不挂断的运行,注意并没有后台运行的功能,就是指,用nohup 运行命令可以是命令永远运 ...

  10. Excel-电话号码隐藏某几个数为*,起到保护信息作用;

    9.电话号码隐藏某几个数为*,起到保护信息作用: 方法一: =SUBSTITUTE(AG2,MID(AG2,4,5),"*****") 解释函数: MID(目标字符串,裁剪起始位置 ...