手撸spring源码分析IOC实现原理
文章出处:https://github.com/fuzhengwei/small-spring
根据小付哥的手撸spring核心源码一步步学习出来的结果收货总结。

spring容器Ioc初次体验

BeanDefinition :Object对象。

public class BeanDefinition {private Object bean;public BeanDefinition(Object bean) {this.bean = bean;}public Object getBean() {return bean;}}

beanFactory:容器工厂对象。一个map集合用于存储实例对象。

为什么使用map集合进行存储?
ArrayList、LinkedList、HashSet等,但在 Spring Bean 容器的场景下,
我们需要一种可以用于存放和名称索引式的数据结构,所以选择 HashMap 是最合适不过的。
public class BeanFactory {private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();public Object getBean(String name) {return beanDefinitionMap.get(name).getBean();}public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {beanDefinitionMap.put(name, beanDefinition);}}
public class UserService {public void queryUserInfo(){System.out.println("111");}public  UserService(){System.out.println("默认无参构造创造");}
}
@Test
public void test_BeanFactory(){// 1.初始化 BeanFactoryBeanFactory beanFactory = new BeanFactory();// 2.注册 beanBeanDefinition beanDefinition = new BeanDefinition(new UserService());beanFactory.registerBeanDefinition("userService", beanDefinition);// 3.获取 beanUserService userService = (UserService) beanFactory.getBean("userService");userService.queryUserInfo();
}

spring初显身手,运用设计模式,实现 Bean 的定义、注册、获取

本章将 Spring Bean 容器完善起来,首先非常重要的一点是在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息注册到 Spring 容器中。那么就需要修改 BeanDefinition 中的属性 Object 为 Class,接下来在需要做的就是在获取 Bean 对象时需要处理 Bean 对象的实例化操作以及判断当前单例对象在容器中是否已经缓存起来了

BeanDefinition 定义

public class BeanDefinition {private Class beanClass;public BeanDefinition(Class beanClass) {this.beanClass = beanClass;}// ...get/set
}

单例注册接口定义和实现

//获取单例对象的接口
public interface SingletonBeanRegistry {Object getSingleton(String beanName);}
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {private Map<String, Object> singletonObjects = new HashMap<>();@Overridepublic Object getSingleton(String beanName) {return singletonObjects.get(beanName);}protected void addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);}}
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Overridepublic Object getBean(String name) throws BeansException {Object bean = getSingleton(name);if (bean != null) {return bean;}BeanDefinition beanDefinition = getBeanDefinition(name);return createBean(name, beanDefinition);}protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {Object bean = null;try {bean = beanDefinition.getBeanClass().newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new BeansException("Instantiation of bean failed", e);}addSingleton(beanName, bean);return bean;}}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName, beanDefinition);}@Overridepublic BeanDefinition getBeanDefinition(String beanName) throws BeansException {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) throw new BeansException("No bean named '" + beanName + "' is defined");return beanDefinition;}}
@Test
public void test_BeanFactory(){// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2.注册 beanBeanDefinition beanDefinition = new BeanDefinition(UserService.class);beanFactory.registerBeanDefinition("userService", beanDefinition);// 3.第一次获取 beanUserService userService = (UserService) beanFactory.getBean("userService");userService.queryUserInfo();// 4.第二次获取 bean from SingletonUserService userService_singleton = (UserService) beanFactory.getBean("userService");userService_singleton.queryUserInfo();
}

执行流程分析

实例化一个容器

需要实例化对象的class,然后注册到容器中

注意:默认调用无参构造进行创建,如果重新了有参构造后会实例失败。

基于Cglib实现含构造函数的类实例化策略

public interface BeanFactory {Object getBean(String name) throws BeansException;Object getBean(String name, Object... args) throws BeansException;}
public interface InstantiationStrategy {Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;}

基于jdk的实例化

public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Class clazz = beanDefinition.getBeanClass();try {if (null != ctor) {return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);} else {return clazz.getDeclaredConstructor().newInstance();}} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);}}}

基于Cglib的实例化

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanDefinition.getBeanClass());enhancer.setCallback(new NoOp() {@Overridepublic int hashCode() {return super.hashCode();}});if (null == ctor) return enhancer.create();return enhancer.create(ctor.getParameterTypes(), args);}}

如果实例的:采用cglib

对象属性值填充

从Spring.xml解析和注册Bean对象

优化从配置文件中实例化bean

step1:实例化beanFactory。
step2:



会判断当前传入的location是属于哪些类型的,classpathResource还是fileSystemResource还是Url 判断方式如下图

根据返回的Resource转为输入流,

把当前输入流转为xml对象进行读取属性赋值操作后 放入到DefaultListableBeanFactory中的private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>()中去。此时DefaultListableBeanFactory中的map中已经保存了bean的定义信息,在去进行实例化,从而完成冲配置文件中实例化bean。

step3:如何实例化:

先冲缓存中根据key(bean的name名称)取,如果没有则创建后添加到缓存中并返回,如果有直接返回。

实现应用上下文,自动识别、资源加载、扩展机制

BeanFactoryPostProcessor(bean的定义信息加载之后,bean实例化之前执行改接口实现方法,用于BeanDefinition的修改)

BeanPostProcessor(实例化前的提前处理:准确来说是提前于bean实例化,利用在实例化之前检查是否存在合适的BeanPostProcessor对象,去拦截某些需要被处理的bean提前完成bean的实例化过程,不会去调用init方法,也没有数据的填充)

流程分析图:

准备实体:

public class UserService {private String uId;private String company;private String location;private UserDao userDao;//setter and  getter
}

MyBeanFactoryPostProcessor:用于测试BeanFactoryPostProcessor

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");PropertyValues propertyValues = beanDefinition.getPropertyValues();propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));}}

MyBeanPostProcessor:用于测试BeanPostProcessor

public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("userService".equals(beanName)) {UserService userService = (UserService) bean;userService.setLocation("改为:北京");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}

准备就绪开干:

测试方法:

@Test
public void test_xml() {// 1.初始化 BeanFactoryClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");// 2. 获取Bean对象调用方法UserService userService = applicationContext.getBean("userService", UserService.class);String result = userService.queryUserInfo();System.out.println("测试结果:" + result);
}

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(“classpath:springPostProcessor.xml”)

  1. 装载所有的Bean对象。
  2. 具体流程如下图所示

**核心方法:refresh()

refreshBeanFactory();方法(作用: 创建 BeanFactory,并加载 BeanDefinition)



public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {super(registry);}public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {super(registry, resourceLoader);}@Overridepublic void loadBeanDefinitions(Resource resource) throws BeansException {try {try (InputStream inputStream = resource.getInputStream()) {doLoadBeanDefinitions(inputStream);}} catch (IOException | ClassNotFoundException e) {throw new BeansException("IOException parsing XML document from " + resource, e);}}@Overridepublic void loadBeanDefinitions(Resource... resources) throws BeansException {for (Resource resource : resources) {loadBeanDefinitions(resource);}}@Overridepublic void loadBeanDefinitions(String location) throws BeansException {ResourceLoader resourceLoader = getResourceLoader();Resource resource = resourceLoader.getResource(location);loadBeanDefinitions(resource);}@Overridepublic void loadBeanDefinitions(String... locations) throws BeansException {for (String location : locations) {loadBeanDefinitions(location);}}protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {Document doc = XmlUtil.readXML(inputStream);Element root = doc.getDocumentElement();NodeList childNodes = root.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {// 判断元素if (!(childNodes.item(i) instanceof Element)) continue;// 判断对象if (!"bean".equals(childNodes.item(i).getNodeName())) continue;// 解析标签Element bean = (Element) childNodes.item(i);String id = bean.getAttribute("id");String name = bean.getAttribute("name");String className = bean.getAttribute("class");// 获取 Class,方便获取类中的名称Class<?> clazz = Class.forName(className);// 优先级 id > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义BeanBeanDefinition beanDefinition = new BeanDefinition(clazz);// 读取属性并填充for (int j = 0; j < bean.getChildNodes().getLength(); j++) {if (!(bean.getChildNodes().item(j) instanceof Element)) continue;if (!"property".equals(bean.getChildNodes().item(j).getNodeName())) continue;// 解析标签:propertyElement property = (Element) bean.getChildNodes().item(j);String attrName = property.getAttribute("name");String attrValue = property.getAttribute("value");String attrRef = property.getAttribute("ref");// 获取属性值:引入对象、值对象Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;// 创建属性信息PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");}// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}}

注册 BeanDefinition: getRegistry().registerBeanDefinition(beanName, beanDefinition);

以上操作完成bean的加载和注册


ConfigurableListableBeanFactory beanFactory = getBeanFactory();


invokeBeanFactoryPostProcessors(beanFactory);

registerBeanPostProcessors(beanFactory);


上图只是拿到了banFactory中有哪些是BeanPostProcessor的子类。并不会立即处理。

beanFactory.preInstantiateSingletons(); 实例化对象








到此完成了BeanDefinition的的修改。完成了BeanPostProcessor和BeanFactoryPostProcessor的对bean的不同时机的修改操作!

1.整体流程说明:根据传入的路径。判断出具体的解析方式是classpath,url,FileSystem。然后读取配置中的Bean定义信息。完成对BeanDefinition第一次的初始化操作并放入DefaultListableBeanFactory中的beanDefinitionMap中去。

2.获取到DefaultListableBeanFactory对象。(这个对象包含bean的定义信息)

3.执行BeanFactoryPostProcessor。说白了就拿到实现了BeanFactoryPostProcessor接口的类,然后拿到需要修改的属性。放入List propertyValueList = new ArrayList<>();集合中去。

4,在实例化bean之前提前拿到实现了BeanPostProcessor的类。然后放入AbstractBeanFactory 中的List beanPostProcessors = new ArrayList();集合中去。

5.创建实例:实例化对象(cglib或jdk),给 Bean 填充属性时执行List propertyValueList = new ArrayList<>();循环然后覆盖。执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法。

龙行有风,向虚拟机注册钩子,实现Bean对象的初始化和销毁方法

定义初始化和销毁方法的接口(init-method、destroy-method)加入bean的生命周期








Aware接口作用。

让所有继承Aware接口的接口都具有感知功能。

让所有实现Aware接口的接口的类都能获取对应的属性。

spring三级缓存解决循环依赖问题(暂未看完)

A 创建时需要依赖 B 创建,而B 的创建又依赖于 A 创建,就这样死循环了

分类:


一级缓存解决方式:

package cn.bugstack.springframework.test;import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class CircleTest {private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);public static void main(String[] args) throws Exception {System.out.println(getBean(B.class).getA());System.out.println(getBean(A.class).getB());}private static <T> T getBean(Class<T> beanClass) throws Exception {String beanName = beanClass.getSimpleName().toLowerCase();if (singletonObjects.containsKey(beanName)) {return (T) singletonObjects.get(beanName);}// 实例化对象入缓存Object obj = beanClass.newInstance();singletonObjects.put(beanName, obj);// 属性填充补全对象Field[] fields = obj.getClass().getDeclaredFields();for (Field field : fields) {field.setAccessible(true);Class<?> fieldClass = field.getType();String fieldBeanName = fieldClass.getSimpleName().toLowerCase();field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass));field.setAccessible(false);}return (T) obj;}}class A {private B b;public B getB() {return b;}public void setB(B b) {this.b = b;}
}class B {private A a;public A getA() {return a;}public void setA(A a) {this.a = a;}
}

Spring BeanFactory和FactoryBean的区别

BeanFactory 提供了一系列的获取bean对象的方法。Object getBean(String name), T getBean(String name, Class requiredType)
FactoryBean 实现了 FactoryBean 接口的类有能力改变 bean,具有代理作用.

BeanPostProcessor和BeanFactoryPostProcessor的区别

BeanFactoryPostProcessor 它的实现类可以在当前BeanFactory初始化(spring容器加载bean定义文件)后,bean实例化之前修改bean的定义属性,达到影响之后实例化bean的效果
BeanPostProcessor postProcessBeforeInitialization(Object bean, String beanName)和postProcessAfterInitialization(Object bean, String beanName)方法 作用:BeanPostProcessor能在spring容器实例化bean之后,在执行bean的初始化方法前后,修改bean的定义属性,达到影响之后实例化bean的效果

ResourceLoader和Resource的区别

Resource 提供了统一的获取bean的定义方式。xml方式,url方式等
ResourceLoader 进一步封装了用于获取Resource的方式。使得用户只需要传入路径,然后判断出是用那种方式获取。

ApplicationContext和BeanFactory获取bean的区别

ApplicationContext ApplicationContext,如果配置的bean是singleton,那么不管你有没有或想不想用它,它都会被实例化(立即加载)
BeanFactory BeanFactory实例化对象时,配置的bean不会马上被实例化,而是等到你使用该bean的时候(getBean)才会被实例化(懒加载)

Spring IOC流程分析



创建BeanFactory,并加载BeanDefinition



获取BeanFactory

添加 ApplicationContextAwareProcessor,让继承自ApplicationContextAwar的 Bean 对象都能感知所属的 ApplicationContext

未完待续-------------------------------

手撸spring源码分析IOC实现原理相关推荐

  1. Spring源码分析——IOC容器

    1.IOC容器的概念 理解IOC容器的概念之前首先需要了解依赖翻转(又称依赖倒置)的概念 许多复杂的应用都是通过多个类之间的彼此合作实现业务逻辑的,这使得每个对象都需要管理自己与其合作对象的依赖,而如 ...

  2. 【spring源码分析】IOC容器初始化(二)

    前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...

  3. Spring源码分析(三)

    Spring源码分析 第三章 手写Ioc和Aop 文章目录 Spring源码分析 前言 一.模拟业务场景 (一) 功能介绍 (二) 关键功能代码 (三) 问题分析 二.使用ioc和aop重构 (一) ...

  4. spring源码分析第六天------spring经典面试问题

    spring源码分析第六天------spring经典面试问题 1.Spring5 新特性及应用举例 2.Spring 经典的面试问题 a.什么是 Spring 框架?Spring 框架有哪些主要模块 ...

  5. spring源码分析第一天------源码分析知识储备

    spring源码分析第一天------源码分析知识储备 Spring源码分析怎么学? 1.环境准备: 2.思路    看:是什么? 能干啥    想:为什么?     实践:怎么做?         ...

  6. Spring 源码分析(三) —— AOP(五)创建代理

    2019独角兽企业重金招聘Python工程师标准>>> 创建代理 代理的定义其实非常简单,就是改变原来目标对象方法调用的运行轨迹.这种改变,首先会对这些方法进行拦截,从而为这些方法提 ...

  7. Spring 源码分析 (一)——迈向 Spring 之路

    一切都是从 Bean 开始的 在 1996 年,Java 还只是一个新兴的.初出茅庐的编程语言.人们之所以关注她仅仅是因为,可以使用 Java 的 Applet 来开发 Web 应用.但这些开发者很快 ...

  8. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  9. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

最新文章

  1. 修改 ShiroUser 缓存用户
  2. springMVC实现文件下载(附带Servlet方式)
  3. .mat,.txt,.csv 数据转换为weka中的arff格式及matlab和Weka之间相互转换格式
  4. Java并发编程实战_[Java并发编程实战] 简介
  5. Python脚本完成VCS文件的file_list到.prj文件的转化
  6. elasticsearch两年学习资料整理分享
  7. MongoDB学习笔记(五) MongoDB文件存取操作
  8. obj文件(1):obj文件用txt打开并且了解v,f,vn,vt的含义
  9. 车载USB DVR(行车记录仪)的源码架构浅析(基于AndroiidM)
  10. 遥感影像去背景 之 数据裁剪
  11. Wireshark抓包页面的登录信息
  12. 三种典型电气减压比例阀线性度和短期重复性的对比考核试验
  13. jenkins简介及自动发包
  14. java字符动画badapple_学完IO 做了点小东西 控制台动画BadApple 感兴趣的进来~
  15. Chrome 配置允许跨域访问
  16. inspur浪潮服务器重做RAID:
  17. java课程体系_Java学习课程体系
  18. minimum required php,今天部署zabbix所遇到问题
  19. Firebird FOR LINUX
  20. python在物理中怎么用_大学物理中Python的应用

热门文章

  1. 修改Linux下只读文件的权限
  2. 【android睡眠唤醒 二】MTK平台唤醒框架分解
  3. cpp导入excel到mysql_将EXCEL表格中的数据导入mysql数据库表中
  4. mysql 数据库主从
  5. 常用icon以及color颜色RGB值和对应颜色效果图
  6. 补天漏洞平台:让更多的白帽子脱离黑产
  7. Drupal < 7.32 “Drupalgeddon” SQL注入漏洞(CVE-2014-3704)漏洞复现
  8. 27年台湾珍珠鲜奶茶品牌Sharetea将引进上海
  9. golang官网可以打开了 go语言
  10. 微信公众平台针对欺诈等违规行为处理结果公示