Spring框架及源码学习---手写IOC和AOP
创始人
2025-05-30 17:51:09
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 使用动态代理模式增强事务

相关内容

热门资讯

与梦想有关的哲理说说短语 梦想...   01、 我就是我是颜色不一样的烟火天空海阔要做最坚强的泡沫。  02、 不论你在什么时候开始,重...
激励人生的励志签名致自己 激励...   01、 农村三驴子,进城。‰  02、 ≮问世间情为何物,一物降一物。≯  03、 上学的心情,...
青春励志个性签名正能量 励志个...   01、 还喜欢 还在意 但不渴望在一起  02、 努力吧一意孤行的公主  03、 让人耗尽心力的...
样本-合同范本-中外专有技术许... 中外专有技术许可合同签约时间:签字地点:合同号:中国,北京,×××公司(以下简称“受让方”)为一方,...
人生格言个性励志签名 微信励志...   01、 如果迩是棉花糖机,莪愿融化真心,旋转成手心旳云。  02、 人活着不是要用眼泪博得同情,...
2023年顶级编程语言趋势 对于开发人员和软件工程师来说,选择更优秀的编程语言使编写可以在任何地方运行的软件变得更...
[VUE 问题]crbug/1... [VUE 问题]crbug/1173575, non-JS module files depreca...
古风唯美诗意个性签名 古风个性...   01、 为了几世夙愿,踏过一程山水,寻你却终陌路难相见。  02、 浅墨淡彩,扉卷半开,痴儿叹。...
霸气励志个性签名女生 男人个性...   01、 我不怕苦不怕累不怕失败不怕鼻青脸肿不怕遍体鳞伤我只怕自己有梦却没从努力过。  02、 漫...
古风有诗意的说说短语 关于古风...   01、 灯火星星,人声杳杳,歌不尽乱世烽火。  02、 静水流深,沧笙踏歌;三生阴晴圆缺,一朝悲...
努力奋斗的励志说说短语 孩子努...   01、 如果自己不努力,谁也给不了你想要的生活;梦想不会逃跑,逃跑的永远是自己。  02、 相对...
Redis中的List列表 List 文章目录List常用命令lpush/rpush/lrangelpop/rpoplindex...
自我激励的qq个性签名 自我激...   01、 如果被太阳越晒越白就好了 吃得越多越瘦就好了 想你越多就能见面就好了  02、 如果不能...
女生古风伤感个性签名 女生唯美...   01、 相思难追 转动时光的椅背 旧日错过的年华与他来一杯  02、 那时回忆已成痂 初雪染白了...
中学生励志演讲稿范例 演讲稿三... 中学生励志演讲稿范例1  各位老师,各位同学,大家好。今天,我很荣幸站在这个舞台,来表达我从开学到现...
高冷霸气的qq个性签名 高冷霸...   01、 他和她住在同一栋楼 遗憾的是爱擦身而过。  02、 学历或专业而沾沾自喜,也不要因为你的...
高冷伤感个性签名经典 游戏个性... 01、 总归有人看不惯我,我能怎样,百般讨好还是杀人灭口.  02、 下雨了 我想起伞 也想起了你 ...
古风签名女生唯美句子 女生个性...   01、 一杯酒 一世意 了却长相思  02、 纵使相逢应不识  03、 相顾无言  04、 此生...
银行信贷员竞聘演讲稿范文 信贷... 银行信贷员竞聘演讲稿  竞聘人XX,我竞聘的岗位是信贷岗,竞聘理由如下:  我有一定的文字表达能力和...
关爱他人优秀演讲稿范文 关爱他... 关爱他人优秀演讲稿篇1  得到他人的的关爱是一种幸福,关爱他人更是一种美德。在我成长的岁月中,常常得...