Spring框架及源码学习---手写IOC和AOP
创始人
2025-05-30 18:53:28
0

Spring手写IOC和AOP

  • 一主要内容
  • 第⼀部分 Spring 概述
    • 第1节 Spring 简介
    • 第2节 Spring 发展历程
    • 第3节 Spring 的优势
    • 第4节 Spring 的核⼼结构
  • 第⼆部分 核⼼思想
    • 第1节 IoC
      • 1.1 什么是IoC?
      • 1.2 IoC解决了什么问题
      • 1.3 IoC和DI的区别
    • 第2节 AOP
      • 2.1 什么是AOP
      • 2.2 AOP在解决什么问题
      • 2.3 为什么叫做⾯向切⾯编程
  • 第三部分 ⼿写实现 IoC 和 AOP
    • 第1节 银⾏转账案例界⾯
    • 第2节 银⾏转账案例表结构
    • 第3节 银⾏转账案例代码调⽤关系
    • 第4节 银⾏转账案例关键代码
    • 第5节 银⾏转账案例代码问题分析
    • 第6节 问题解决思路
      • 针对问题⼀思考:
      • 针对问题⼆思考:
    • 第7节 案例代码改造
      • (1)针对问题⼀的代码改造
      • (2)针对问题⼆的改造

一主要内容

  • Spring 概述(基本情况)
  • 核⼼思想 IoC 和 AOP
  • ⼿写实现 IoC 和 AOP(⾃定义spring框架)
  • Spring IoC ⾼级应⽤
    基础知识
    ⾼级特性
  • Spring IoC 源码深度剖析
    设计⾮常优雅
    设计模式
    注意:原则、⽅法和技巧
  • Spring AOP ⾼级应⽤
    声明式事务控制
  • Spring AOP 源码深度剖析
    必要的笔记、必要的图、通俗易懂的语⾔化解知识难点

第⼀部分 Spring 概述

第1节 Spring 简介

Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 Spring MVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三⽅框架和类库,已 经成为使⽤最多的 Java EE 企业应⽤开源框架。

Spring 官⽅⽹址:

我们经常说的 Spring 其实指的是Spring Framework(spring 框架)。

第2节 Spring 发展历程

  • 1997年 IBM 提出了EJB的思想; 1998年,SUN 制定开发标准规范EJB1.0; 1999年,EJB 1.1发 布;
    2001年,EJB 2.0发布; 2003年,EJB 2.1发布; 2006年,EJB 3.0发布;
  • Rod Johnson(spring之⽗)
  • Expert One-to-One J2EE Design and Development(2002)
    阐述了J2EE使⽤EJB开发设计的优 点及解决⽅案
  • Expert One-to-One J2EE Development without EJB(2004)
    阐述了J2EE开发不使⽤EJB的解决 ⽅式(Spring雏形)

2017 年 9 ⽉份发布了 Spring 的最新版本 Spring 5.0 通⽤版(GA)

第3节 Spring 的优势

整个 Spring 优势,传达出⼀个信号,Spring 是⼀个综合性,且有很强的思想性框架,每学习⼀ 天,就能体会到它的⼀些优势。

  • ⽅便解耦,简化开发

    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的 过度程序耦合。⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更 专注于上层的应⽤。

  • AOP编程的⽀持

    通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过 AOP轻松应付。

  • 声明式事务的⽀持

    @Transactional
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼ 开发效率和质量。

  • ⽅便程序的测试

    可以⽤⾮容器依赖的编程⽅式进⾏⼏乎所有的测试⼯作,测试不再是昂贵的操作,⽽是随⼿可做的 事情。

  • ⽅便集成各种优秀框架

    Spring可以降低各种框架的使⽤难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、 Quartz等)的直接⽀持。

  • 降低JavaEE API的使⽤难度

    Spring对JavaEE API(如JDBC、JavaMail、远程调⽤等)进⾏了薄薄的封装层,使这些API的使⽤ 难度⼤为降低。

  • 源码是经典的 Java 学习范例

    Spring的源代码设计精妙、结构清晰、匠⼼独⽤,处处体现着⼤师对Java设计模式灵活运⽤以及对 Java技术的⾼深造诣。它的源代码无疑是Java技术的最佳实践的范例。

第4节 Spring 的核⼼结构

Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块Web模块AOP(Aspect Oriented Programming)/Aspects模块Core Container模块 和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零 侵⼊的轻量级框架。
在这里插入图片描述

  • Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中
    bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。
    基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼ 容器之上。

  • ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应
    ⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。

  • 数据访问与集成(Data Access/Integration)
    Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专 注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问 提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由 JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。

  • Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。
    SpringMVC框架在Web层提升了应⽤的松耦合⽔平。

  • Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测 试。
    通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。

第⼆部分 核⼼思想

**注意:**IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向于理论化,spring在技 术层次把这两个思想做了⾮常好的实现(Java)

第1节 IoC

1.1 什么是IoC?

==IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现 ==
描述的事情: Java开发领域对象的创建,管理的问题
传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象
IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对 象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可。

我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列 事情)

为什么叫做控制反转?
控制: 指的是对象创建(实例化、管理)的权利
反转: 控制权交给外部环境了(spring框架、IoC容器)

在这里插入图片描述

1.2 IoC解决了什么问题

IoC解决对象之间的耦合问题
在这里插入图片描述

1.3 IoC和DI的区别

DI:Dependancy Injection(依赖注⼊)
怎么理解:
IOC和DI描述的是同⼀件事情,只不过⻆度不⼀样罢了
在这里插入图片描述

第2节 AOP

2.1 什么是AOP

AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程 AOP是OOP的延续,从OOP说起。
OOP三⼤特征:封装、继承和多态 oop是⼀种垂直继承体系
在这里插入图片描述
OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如下⾯的在顶级⽗类 Animal中的多个⽅法中相同位置出现了重复代码,OOP就解决不了。
在这里插入图片描述
横切逻辑代码
在这里插入图片描述
横切逻辑代码存在什么问题:

  • 横切代码重复问题
  • 横切逻辑代码和业务代码混杂在⼀起,代码臃肿,维护不⽅便

AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分析

在这里插入图片描述
代码拆分容易,那么如何在不改变原有业务逻辑的情况下,悄⽆声息的把横切逻辑代码应⽤到原有的业务逻辑中,达到和原来⼀样的效果,这个是⽐较难的

2.2 AOP在解决什么问题

在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

2.3 为什么叫做⾯向切⾯编程

「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑
「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个 ⾯的概念在⾥⾯

第三部分 ⼿写实现 IoC 和 AOP

上⼀部分我们理解了 IoC 和 AOP 思想,我们先不考虑 Spring 是如何实现这两个思想的,此处准备了⼀个『银⾏转账』的案例,请分析该案例在代码层次有什么问题 ?分析之后使⽤我们已有知识解决这些问题(痛点)。其实这个过程我们就是在⼀步步分析并⼿写实现 IoC 和 AOP。

第1节 银⾏转账案例界⾯

在这里插入图片描述

第2节 银⾏转账案例表结构

在这里插入图片描述

第3节 银⾏转账案例代码调⽤关系

在这里插入图片描述

第4节 银⾏转账案例关键代码

在这里插入图片描述

第5节 银⾏转账案例代码问题分析

在这里插入图片描述
(1)问题⼀: 在上述案例实现中,service层实现类在使⽤ dao 层对象时,直接在 TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对象,然⽽⼀个 new 关键字却将 TransferServiceImpl 和 dao 层具体的⼀个实现类 JdbcAccountDaoImpl 耦合在了⼀起,如果说技术架构发⽣⼀些变动,dao 层的实现要使⽤其它技术, ⽐如 Mybatis,思考切换起来的成本?每⼀个 new 的地⽅都需要修改源代码,重新编译,⾯向接⼝开发 的意义将⼤打折扣?
(2)问题⼆: service 层代码没有竟然还没有进⾏事务控制 ?如果转账过程中出现异常,将可能导致 数据库数据错乱,后果可能会很严重,尤其在⾦融业务。

第6节 问题解决思路

针对问题⼀思考:

  • 实例化对象的⽅式除了 new 之外,还有什么技术?

    反射 (需要把类的全限定类名配置在xml 中)

  • 考虑使⽤设计模式中的⼯⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在⼯⼚中使⽤反射技术实例化对象,⼯⼚模式很合适

在这里插入图片描述

  • 更进⼀步,代码中能否只声明所需实例的接⼝类型,不出现 new 也不出现⼯⼚类的字眼,如下图?

能!声明⼀个变量并提供 set ⽅法,在反射的时候将所需要的对象注⼊进去吧
在这里插入图片描述

针对问题⼆思考:

  • service 层没有添加事务控制,怎么办?

没有事务就添加上事务控制,⼿动控制 JDBCConnection事务,但要注意将Connection和当前线程绑定(即保证⼀个线程只有⼀个 Connection,这样操作才针对的是同⼀个Connection,进⽽控制的是同⼀个事务

在这里插入图片描述

第7节 案例代码改造

(1)针对问题⼀的代码改造

lagou-transfer-sxIoc 由new对象的方式升级为bean.xml解析对象

  • beans.xml




  • 增加 BeanFactory.java
/*** 读取bean.xml文件,解析里面的所有标签,* 进行对象实例化后放入到map中,后续使用可以直接从map中取即可* 工厂类,生产对象(使用反射技术)*/
public class BeanFactory {/*** 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)* 任务二:对外提供获取实例对象的接口(根据id获取)*/// 存储对象private static Map map = new HashMap<>();/*** XPath 使用路径表达式来选取 XML 文档中的节点或节点集**      经常使用到的路径表达式,如下**          nodename	选取此节点的所有子节点。*          /	        从根节点选取。*          //	        从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。*          .	        选取当前节点。*          ..	        选取当前节点的父节点。*          @	        选取属性。**/static {// 任务一:读取解析xml,通过反射技术实例化对象并且存储待用(map集合)// 加载xmlInputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");// 解析xmlSAXReader saxReader = new SAXReader();try {Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();//查找所有的bean标签List beanList = rootElement.selectNodes("//bean");for (int i = 0; i < beanList.size(); i++) {Element element = beanList.get(i);// 处理每个bean元素,获取到该元素的id 和 class 属性String id = element.attributeValue("id");        // accountDaoString clazz = element.attributeValue("class");  // com.lagou.edu.dao.impl.JdbcAccountDaoImpl// 通过反射技术实例化对象Class aClass = Class.forName(clazz);// 实例化之后的对象Object o = aClass.newInstance();// 存储到map中待用,key-类名称  value--实例化后的类对象map.put(id, o);}// 实例化完成之后维护对象的依赖关系,检查哪些对象需要传值进入,根据它的配置,我们传入相应的值// 有property子元素的bean就有传值需求List propertyList = rootElement.selectNodes("//property");// 解析property,获取父元素for (int i = 0; i < propertyList.size(); i++) {Element element = propertyList.get(i);//String name = element.attributeValue("name");String ref = element.attributeValue("ref");// 找到当前需要被处理依赖关系的beanElement parent = element.getParent();// 调用父元素对象的反射功能String parentId = parent.attributeValue("id");Object parentObject = map.get(parentId);// 遍历父对象中的所有方法,找到"set" + nameMethod[] methods = parentObject.getClass().getMethods();for (int j = 0; j < methods.length; j++) {Method method = methods[j];if (method.getName().equalsIgnoreCase("set" + name)) {// 该方法就是 setAccountDao(AccountDao accountDao)method.invoke(parentObject, map.get(ref));}}// 把处理之后的parentObject重新放到map中map.put(parentId, parentObject);}} catch (DocumentException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}// 任务二:对外提供获取实例对象的接口(根据id获取)public static Object getBean(String id) {return map.get(id);}}
  • 修改 TransferServlet
    在这里插入图片描述
  • 修改 TransferServiceImpl
    在这里插入图片描述
  • 改造代码分析图
    在这里插入图片描述

(2)针对问题⼆的改造

原问题对应代码分析
在这里插入图片描述

第一次改造后对应代码分析
lagou-transfer-shoudong-sw 为手动事务代码
在这里插入图片描述
虽然能够进行处理事务,但是我们发现service层中需要手写事务处理,如果我们有100个service那是不是就要手动写一百次事务处理。那不得疯了。这种事务代码和业务代码严重耦合在一起。
我们可以使用AOP动态代理来改造。

第二次改造分析
源码地址 lagou-transfer-procxy-sw
在这里插入图片描述
在这里插入图片描述


public class ProxyFactory {private TransactionManager transactionManager;public void setTransactionManager(TransactionManager transactionManager) {this.transactionManager = transactionManager;}/*** Jdk动态代理* @param obj  委托对象* @return   代理对象*/public Object getJdkProxy(Object obj) {// 获取代理对象return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try{// 开启事务(关闭事务的自动提交)transactionManager.beginTransaction();result = method.invoke(obj,args);// 提交事务transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滚事务transactionManager.rollback();// 抛出异常便于上层servlet捕获throw e;}return result;}});}/*** 使用cglib动态代理生成代理对象* @param obj 委托对象* @return*/public Object getCglibProxy(Object obj) {return  Enhancer.create(obj.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object result = null;try{// 开启事务(关闭事务的自动提交)transactionManager.beginTransaction();result = method.invoke(obj,objects);// 提交事务transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滚事务transactionManager.rollback();// 抛出异常便于上层servlet捕获throw e;}return result;}});}
}

lagou-transfer-sxIoc 由new对象的方式升级为bean.xml解析对象
lagou-transfer-shoudong-sw 为手动事务代码
lagou-transfer-procxy-sw 使用动态代理模式增强事务

相关内容

热门资讯

开源物联网平台推荐介绍 开源物联网平台调研 文章目录开源物联网平台调研一、 调研推荐开源物联网平台及背景介绍二、社区支持度与...
“端午”假期刚开始,无锡这里上... 迎着今年“端午”假期的开启,5月30日晚,位于无锡市惠山古镇景区的“宝善坊”正式开街,上演“人从众”...
学习Java——泛型 目录 什么是泛型 泛型带来的问题 1、当泛型遇到重载 2、当泛型遇到catch 3、当泛型内包含静态...
HTTP协议协议报文结构请求响... 目录 一. 何为HTTP 1. 简单理解HTTP协议的工作过程  2. Fiddler抓包工具  2...
[ 漏洞复现篇 ] Jooml... 🍬 博主介绍 👨‍🎓 博主介绍:大家好...
【类和对象的使用的知识点——案... 文章目录类和对象的使用的知识点封装封装的意义struct和class区别成员属性设置为私有对象的初始...
青瓜虾滑饼:清爽黄瓜与鲜嫩虾滑... 准备材料:黄瓜虾滑盐白胡椒粉具体步骤: 黄瓜刨丝:将黄瓜刨成细丝。点盐杀出水分:在黄瓜丝上撒适量的...
vue3 根据vue-qr生成... 官网地址 vue-qr - npm   vue有两种生成二维码的方式,qrcode、v...
四川九寨沟峨眉山旅游报团五日游... 标题:【亲测】四川九寨沟峨眉山五日游,跟着乐乐畅享川蜀之美 四川旅游推荐!当地导游-乐乐:185 8...
四川旅游攻略自由行攻略跟团五日... 标题:【四川旅游攻略自由行与跟团五日游全揭秘】驴友亲测,跟着乐乐畅游四川! 四川旅游推荐!当地导游-...
windows下远程连接aws... 一:安装或更新kubectlKubectl 是一个命令行工具,用于与 K...
选拔高等学校棒球大会 选拔高等学校棒球大会(日语:選抜高等学校野球大会/せんばつ...
面试错题本 目录2023.3.21 深信服哈夫曼树哈夫曼编码 2023.3.21 深信服 ​同一线程共享的有堆、...
武昌区多措并举护航端午假期,中... 极目新闻通讯员 费帆 葛倚兵 刘朵 栏题 5月31日,端午节首日。为了确保游客出行安全和顺畅,武昌...
畅享南宁至靖西自驾游攻略:路线... 南宁,这座广西的首府,是一个充满活力的城市,四周环绕着壮丽的自然风光。而靖西,作为广西的一颗璀璨明珠...
“龙舟水”期间出游安全提示 当前,广东省已进入“龙舟水”集中降雨期,极端天气频发,暴雨洪涝、山体滑坡、城乡内涝等风险显著增加。江...
现代数据栈MDS应用落地介绍—... Dazdata MDS 现代数据栈MDS的出现使得中小企业低成本获得大数据处理能力成为可能ÿ...
速看!面试高频题! 答案都给你... 互联网中高薪,门槛相对较低的产品经理,正成为越来越多人的职业选择。而“工...