手写spring笔记
手写spring笔记
《Spring 手撸专栏》笔记
IoC部分
Bean初始化和属性注入
Bean的信息封装在BeanDefinition
中
/*** 用于记录Bean的相关信息*/
public class BeanDefinition {/*** Bean对象的类型*/private Class beanClass;/*** Bean对象中的属性信息*/private PropertyValues propertyValues;/*** 初始化方法的名称*/private String initMethodName;/*** 销毁方法的名称*/private String destroyMethodName;/*** 默认为单例模式*/private String scope;
}
Bean注入过程中有一下几个主要接口:
BeanFactory
:Bean工厂接口,其中声明了获取Bean对象的方法SingletonBeanRegistry
: Bean的单例模式创建接口,声明了单例对象的创建方法和销毁方法InstantiationStrategy
:实例化Bean对象的策略接口,申明了Bean对象使用何种方式实例化对象的方法,在实现类中使用反射的方式获取构造方法来得到Bean对象,有JDK和Cglib两种实现BeanDefinitionRegistry
:管理BeanDefinition
的注册,其实现类中包含存有BeanDefinition
的Map
其具体流程为:
从BeanDefinitionRegistry
得到BeanDefinition
信息,调用BeanFactory
的getBean()
方法,判断是否为单例模式,若为单例模式则调用SingletonBeanRegistry
的getSingleton()
方法,如果容器中存在Bean则直接返回,不存在则调用InstantiationStrategy
的instantiate()
方法,使用反射的方式生成Bean对象
在属性注入中,需要注入的属性信息封装在BeanDefinition
的PropertyValues
中,其本质为一个PropertyValue
列表。
在创建Bean对象时,若存在无参构造方法,则使用无参构造方法,若没有,则从PropertyValues
去除构造方法需要的属性。
/*** Bean对象中的属性值*/
public class PropertyValue {/*** Bean对象属性的名称*/private String name;/*** Bean对象中属性的实例化对象*/private Object value;
}
资源加载器
Spring需要解析配置文件,对此,定义了一下接口:
Resource
:资源信息接,用于处理资源加载流,其实现类根据配置文件地址得到资源信息,并向外声明有得到输入流的接口,其包含XML文件配置和URL文件配置等方式ResourceLoader
:资源加载器接口,通过该接口获取路径对应的资源对象,用于获得资源对象BeanDefinitionReader
:BeanDefinition读取接口,用于读取配置文件并调用BeanDefinitionRegistry
加载BeanDefinition
Bean生命周期
Bean的初始化操作,提供了Bean的初始化和销毁等方法接口,其包括:
InitializingBean
:提供了初始化方法的Bean,如果Bean实现了该接口,则会在创建Bean时调用初始化方法DisposableBean
:提供了Bean销毁方法,若实现了该接口,则在容器销毁时调用其销毁方法
此外,在BeanDefinition
中包含有initMethodName
和destroyMethodName
两个属性,用于指派Bean的初始化方法和销毁方法,可以在不实现上述接口的情况下使用反射的方式实现初始化和销毁方法,在XML中配置init-method
和destroy-method
两个属性即可
同时,定义有一下两个接口,来管理Bean的初始化操作
/*** Bean实例化前对其进行预处理的接口,提供修改BeanDefinition的方法*/
public interface BeanFactoryPostProcessor {/*** 所有的BeanDefinition加载完成后而Bean对象实例化之前调用,提供修改BeanDefinition的机制** @param beanFactory* @throws BeansException*/void postProcessorBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
/*** 对Bean对象初始化前后进行处理的接口,提供Bean初始化前后对其进行操作的方法*/
public interface BeanPostProcessor {/*** 在Bean对象执行初始化前对Bean实例对象进行操作** @param bean 被操作的Bean对象* @param beanName 被操作的Bean对象的名称* @return* @throws BeansException*/Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;/*** 在Bean对象执行初始化后对Bean实例对象进行操作** @param bean 被操作的Bean对象* @param beanName 被操作的Bean对象的名称* @return* @throws BeansException*/Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
Bean工厂接口
对于复杂类型的Bean,其提供了FactoryBean<T>
接口,对于实现了该接口的类,spring在创建Bean对象时,不是直接生成该对象,而是调用getObject
方法来生成Bean对象
/*** Bean对象的工厂接口,声明了从工厂获得Bean对象的方法*/
public interface BeanFactory {/*** 有参方式获取Bean** @param name Bean名称* @return Bean对象实例* @throws BeansException Bean创建时异常*/Object getBean(String name) throws BeansException;/*** 有参方式获取Bean** @param name Bean名称* @param args 参数* @return Bean对象实例* @throws BeansException Bean创建时异常*/Object getBean(String name, Object... args) throws BeansException;/*** 按类型获取Bean对象** @param name Bean对象名称* @param requiredType Bean对象类型* @param <T> Bean对象类型* @return Bean对象实例*/<T> T getBean(String name, Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;
}
Aware感知接口
对于实现了Aware
接口的Bean,在spring创建实例时,会将对应的信息注入到Bean中,方便使用者对spring进行自定义扩展开发,其包括如下几类接口
BeanFactoryAware
BeanClassLoaderAware
BeanNameAware
ApplicationContextAware
事件监听机制
使用观察者模式,设置了事件监听机制
ApplicationEvent
:继承自EventObject
接口,Spring Event事件抽象类,后续的所有事件类都继承自该类ApplicationListener<E extends ApplicationEvent>
:继承自EventListener
接口,其泛型类型为该监听器关注的事件类型ApplicationEventMulticaster
:事件广播器接口,定义有添加和删除监听器以及广播监听事件的方法
在使用过程中,首先调用ApplicationEventMulticaster
的addApplicationListener
方法将自定义监听器加入到广播器中,当要发布事件时,调用multicastEvent
方法,该方法会便利已注册的所有监听器,并向关注该事件的监听器发送通知,调用其onApplicationEvent
方法
应用上下文
接口ApplicationContext
整合了上述的各种方法,并提供了向外的操作接口。其中,ApplicationContext
的实现类中定义了核心方法refresh()
,其具体内容如下:
@Overridepublic void refresh() throws BeansException {// 创建BeanFactory,并加载BeanDefinitionrefreshBeanFactory();// 获取BeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();// 添加ApplicationContextAwareProcessor对象处理ApplicationContextAwarebeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 在Bean实例化之前,执行BeanFactoryPostProcessor方法invokeBeanFactoryPostProcessors(beanFactory);// 将BeanPostProcessor提前注册到容器中registerBeanPostProcessors(beanFactory);// 提前实例化单例Bean对象beanFactory.preInstantiateSingletons();// 初始化事件发布者initApplicationEventMulticaster();// 注册事件监听器registerListeners();// 发布容器刷新完成事件finishRefresh();}
三级缓存机制
为了解决循环依赖,spring使用了三级缓存,如下所示:
/*** Bean容器一级缓存,用于存储Bean的完整实例化对象*/private Map<String, Object> singletonObjects = new HashMap<>();/*** Bean容器二级缓存,用于存储Bean的早期非完整实例化对象*/private Map<String, Object> earlySingletonObjects = new HashMap<>();/*** Bean容器三级缓存,用于存储Bean的代理对象*/private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();/**
其中,三级缓存存储的不是Bean对象,而是一个工厂对象,其目的是为了提早暴露Bean,等到后续的Bean依赖该Bean的时候,就会调用工厂的getObject
方法获取到Bean对象。三级缓存的顺序如下:
@Overridepublic Object getSingleton(String beanName) throws BeansException {Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null) {// 如果没有完整对象,则从二级缓存中获取未完整实例化的对象singletonObject = earlySingletonObjects.get(beanName);if (singletonObject == null) {// 如果二级缓存中也没有,则从代理对象处生成未完成的实例化对象,并删除代理对象ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();earlySingletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);}}}return singletonObject;}
注解配置信息
这里给出了一下几个注解功能的实现:
@Component
:将类注入到容器中@Autowired
:对Bean对象对应的该属性进行依赖注入@Scope
:配置该类的作用域为单例还是原型@Value
:注入配置文件中的信息
通过读取配置文件中<context:component-scan />
指定的路径,对其下所有的类进行扫描,若标注有@Component
注解,则生成对应的BeanDefinition
,并读取其属性,生成对应的PropertyValue
,若属性标注有@Value
注解,表示属性值为配置文件中的对应数值,若标注有@Autowired
注解,则表示属性值为Bean。生成好BeanDefinition
后,就和原本流程一样了
AOP
切点和匹配接口定义
AOP的核心是使用代理的方式生成代理对象,其核心接口包括:
ClassFilter
:定义类匹配类,用于切点找到给定的接口和目标类,提供了判断切入点是否应用在给定的接口或目标类中的方法MethodMatcher
:方法匹配类,找到表达式范围内匹配下的目标类和方法,提供了判断给定的方法是否匹配表达式的方法Pointcut
:切入点接口,定义用于获取ClassFilter
、MethodMatcher
的两个类AopProxy
:代理接口,用于获取代理类
使用了JDK和AspectJ两种方式实现了上述接口,并通过ProxyFactory
工厂类来对两种实现的选择进行了封装
AOP融入Bean生命周期
将AOP融入Bean的声明周期,其利用了BeanPostProcessor
接口,使用该接口的postProcessAfterInitialization
对实例化后的原对象进行包装,返回其代理对象
其定义了Advisor
访问者接口,整合切面pointcut和拦截器advice
对于反射要用的advice
接口,定义了继承自该接口的BeforeAdvcice
等接口,提供前置方法等AOP方法接口。同样地,对于拦截器MethodInterceptor
,也封装了对应的MethodBeforeAdviceInterceptor
等类,来实现各种AOP操作
相关文章:
手写spring笔记
手写spring笔记 《Spring 手撸专栏》笔记 IoC部分 Bean初始化和属性注入 Bean的信息封装在BeanDefinition中 /*** 用于记录Bean的相关信息*/ public class BeanDefinition {/*** Bean对象的类型*/private Class beanClass;/*** Bean对象中的属性信息*/private PropertyVal…...

shell的两种属性: 交互(interactive)与登录(login)
1. 背景 在看shell变量的时候引起了兴趣: 局部变量,全局变量,环境变量,shell的配置文件,参考博客: http://c.biancheng.net/view/773.html 2. 交互式与非交互式 参考博客: shell的两个属性:是否交互式(interactive), 是否登录…...

实现简单的element-table的拖拽效果
第一步,先随便创建element表格 <el-table ref"dragTable" :data"tableData" style"width: 100%" border fit highlight-current-row><el-table-column label"日期" width"180"><template slot-sc…...

Web网页浏览器远程访问jupyter notebook服务器【内网穿透】
文章目录 前言1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 前言 Jupyter Notebook,它是一个交互式的数据科学和计算环境,支持多种编程语言,如…...

干翻Dubbo系列第十一篇:Dubbo常见协议与通信效率对比
文章目录 文章说明 一:协议 1:什么是协议 2:协议和序列化关系 3:协议组成 (一):头信息 (二):体信息 4:Dubbo3中常见的协议 5:…...

春秋云镜 CVE-2020-17530
春秋云镜 CVE-2020-17530 S2-061 靶标介绍 对CVE-2019-0230的绕过,Struts2官方对CVE-2019-0230的修复方式是加强OGNL表达式沙盒,而CVE-2020-17530绕过了该沙盒。当对标签属性中的原始用户输入进行评估时,强制 OGNL 评估可能会导致远程代码执…...

【java毕业设计】基于Spring Boot+Vue+mysql的论坛管理系统设计与实现(程序源码)-论坛管理系统
基于Spring BootVuemysql的论坛管理系统设计与实现(程序源码毕业论文) 大家好,今天给大家介绍基于Spring BootVuemysql的论坛管理系统设计与实现,本论文只截取部分文章重点,文章末尾附有本毕业设计完整源码及论文的获取…...

华为在ospf area 0单区域的情况下结合pbr对数据包的来回路径进行控制
配置思路: 两边去的包在R1上用mqc进行下一跳重定向 两边回程包在R4上用mqc进行下一跳重定向 最终让内网 192.168.10.0出去的数据包来回全走上面R-1-2-4 192.168.20.0出去的数据包来回全走 下面R1-3-4 R2和R3就是简单ospf配置和宣告,其它没有配置&#…...

PyQt5登录界面跳转
目录 1、设计ui界面 2、设计逻辑代码,实现登录界面跳转 3、结果 1、设计ui界面 设计后的ui界面 在这里可以设置密码不显示 这里可以设置快捷键 最后将ui界面转为py文件后获得的逻辑代码为:(文件名为Login.py) # -*- coding: u…...
git add 用法
git add 是 Git 的一个命令,用于将更改的文件加入到暂存区(staging area),准备提交这些更改。以下是该命令的常见用法: 添加单个文件 git add 文件名添加多个文件 git add 文件名1 文件名2 ...添加所有当前目录下的更改…...
系统架构设计师---2018年下午试题1分析与解答(试题三)
系统架构设计师---2018年下午试题1分析与解答 试题三 阅读以下关于嵌入式实时系统相关技术的叙述,在答题纸上回答问题 1 和问题 2。 【说明】 某公司长期从事宇航领域嵌入式实时系统的软件研制任务。公司为了适应未来嵌入式系统网络化、智能化和综合化的技术发展需要,决定…...

面试时如何回答接口测试怎么进行
一、什么是接口测试 接口测试顾名思义就是对测试系统组件间接口的一种测试,接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。 …...
【LeetCode】647.回文子串
题目 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会…...
【Git】SSH到底是什么
一、SSH初探 1、SSH是什么? SSH是一个安全协议,类似有SSL、TSL Git有四种协议:本地协议、Git协议、HTTP协议、SSH协议 SSH协议的优缺点: 优点:SSH访问更加安全,有利于公司的开发维护,并且可…...

当你出差在外时,怎样轻松访问远程访问企业局域网象过河ERP系统?
文章目录 概述1.查看象过河服务端端口2.内网穿透3. 异地公网连接4. 固定公网地址4.1 保留一个固定TCP地址4.2 配置固定TCP地址 5. 使用固定地址连接 概述 ERP系统对于企业来说重要性不言而喻,不管是财务、生产、销售还是采购,都需要用到ERP系统来协助。…...

机器学习与模型识别1:SVM(支持向量机)
一、简介 SVM是一种二类分类模型,在特征空间中寻找间隔最大的分离超平面,使得数据得到高效的二分类。 二、SVM损失函数 SVM 的三种损失函数衡量模型的性能。 1. 0-1 损失: 当正例样本落在 y0 下方则损失为 0,否则损失为…...

在CentOS7.9上安装最新版本Docker安装步骤
1、查看系统版本: cat /etc/redhat-release 2、卸载原有版本(如有的话) sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ d…...

基于 spring boot 的动漫信息管理系统【源码在文末】
半山腰总是最挤的,你得去山顶看看 大学生嘛,论文写不出,代码搞不懂不要紧,重要的是,从这一刻就开始学习,立刻马上! 今天带来的是最新的选题,基于 spring boot 框架的动漫信息管理系…...

vue项目根据word模版导出word文件
一、安装依赖 //1、docxtemplaternpm install docxtemplater pizzip -S//2、jszip-utilsnpm install jszip-utils -S//3、pizzipnpm install pizzip -S//4、FileSaver npm install file-saver --save二、创建word模版 也就是编辑一个word文档,文档中需要动态取值的…...

PHP 从 URL(链接) 字符串中获取参数
PHP 从 URL(链接) 字符串中获取参数 //URL(链接)字符串 $url https://www.baidu.com/?name小洪帽i&sex男&age999; //parse_url 函数从一个 URL 字符串中获取参数 $urlparse_url($url); //输出获取到的内容 echo "<pre>"; pri…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...