走进Spring的世界 —— Spring底层核心原理解析(一)

文章目录

  • 前言
  • 一、Spring中是如何创建一个对象
  • 二、Bean的创建过程
  • 三、推断构造方法
  • 四、AOP大致流程
  • 五、Spring事务


前言

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();

这是学习Spring的hello world。可是,这三行代码底层都做了什么,比如:

  • 第一行代码,会构造一个ClassPathXmlApplicationContext对象,ClassPathXmlApplicationContext该如何理解,调用该构造方法除开会实例化得到一个对象,还会做哪些事情?
  • 第二行代码,会调用ClassPathXmlApplicationContext的getBean方法,会得到一个UserService对象,getBean()是如何实现的?返回的UserService对象和我们自己直接new的UserService对象有区别吗?
  • 第三行代码,就是简单的调用UserService的test()方法

光看这三行代码,其实并不能体现出来Spring的强大之处,也不能理解为什么需要ClassPathXmlApplicationContext和getBean()方法
对于这三行代码,你现在可以认为:如果你要用Spring,你就得这么写。就像你要用Mybatis,你就得写各种Mapper接口。

但是用ClassPathXmlApplicationContext其实已经过时了,在新版的Spring MVC和Spring Boot的底层主要用的都是AnnotationConfigApplicationContext,比如:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();

可以看到AnnotationConfigApplicationContext的用法和ClassPathXmlApplicationContext是非常类似的,只不过需要传入的是一个class,而不是一个xml文件。

而AppConfig.class和spring.xml一样,表示Spring的配置,比如可以指定扫描路径,可以直接定义Bean,比如:

spring.xml中的内容为:

<context:component-scan base-package="xxx"/>
<bean id="userService" class="xxxxx"/>

AppConfig中的内容为:

@ComponentScan("com.xx")
public class AppConfig {@Beanpublic UserService userService(){return new UserService();}}

所以spring.xml和AppConfig.class本质上是一样的。

目前,我们基本很少直接使用上面这种方式来用Spring,而是使用Spring MVC,或者Spring Boot,但是它们都是基于上面这种方式的,都需要在内部去创建一个ApplicationContext的,只不过:

  • Spring MVC创建的是XmlWebApplicationContext,和ClassPathXmlApplicationContext类似,都是基于XML配置的
  • Spring Boot创建的是AnnotationConfigApplicationContext

因为AnnotationConfigApplicationContext是比较重要的,并且AnnotationConfigApplicationContext和ClassPathXmlApplicationContext大部分底层都是共同的

一、Spring中是如何创建一个对象

不管是AnnotationConfigApplicationContext还是ClassPathXmlApplicationContext,目前,我们都可以简单的将它们理解为就是用来创建Java对象的,比如调用getBean()就会去创建对象(此处不严谨,getBean可能也不会去创建对象)

在Java语言中,肯定是根据某个类来创建一个对象的。我们在看一下实例代码:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

当我们调用context.getBean(“userService”)时,就会去创建一个对象,但是getBean方法内部怎么知道"userService"对应的是UserService类呢?

所以,我们就可以分析出来,在调用AnnotationConfigApplicationContext的构造方法时,也就是第一行代码,会去做一些事情:

  • 解析Config.class,得到扫描路径
  • 遍历扫描路径下的所有Java类,如果发现某个类上存在@Component、@Service等注解,那么Spring就把这个类记录下来,存在一个Map中,比如Map<String, Class>
  • Spring会根据某个规则生成当前类对应的beanName,作为key存入Map,当前类作为value

这样,但调用context.getBean(“userService”)时,就可以根据"userService"找到UserService类,从而就可以去创建对象了

二、Bean的创建过程

那么Spring到底是如何来创建一个Bean的呢,这个就是Bean创建的生命周期,大致过程如下:

  1. 利用该类的构造方法来实例化得到一个对象(但是如何一个类中有多个构造方法,Spring则会进行选择,这个叫做推断构造方法)
  2. 得到一个对象后,Spring会判断该对象中是否存在被@Autowired注解了的属性,把这些属性找出来并由Spring进行赋值(依赖注入)
  3. 依赖注入后,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数(Aware回调)
  4. Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法(初始化前)
  5. 紧接着,Spring会判断该对象是否实现了InitializingBean接口,如果实现了,就表示当前对象必须实现该接口中的afterPropertiesSet()方法,那Spring就会调用当前对象中的afterPropertiesSet()方法(初始化)
  6. 最后,Spring会判断当前对象需不需要进行AOP,如果不需要那么Bean就创建完了,如果需要进行AOP,则会进行动态代理并生成一个代理对象做为Bean(初始化后)

这里是我基本写的
在这里插入图片描述通过最后一步,我们可以发现,当Spring根据UserService类来创建一个Bean时

  1. 如果不用进行AOP,那么Bean就是UserService类的构造方法所得到的对象。
  2. 如果需要进行AOP,那么Bean就是UserService的代理类所实例化得到的对象,而不是UserService本身所得到的对象。

Bean对象创建出来后:

  1. 如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map<String, Object>,Map的key为beanName,value为Bean对象。这样下次getBean时就可以直接从Map中拿到对应的Bean对象了。(实际上,在Spring源码中,这个Map就是单例池)
  2. 如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次getBean时会再次执行上述创建过程,得到一个新的Bean对象。

三、推断构造方法

Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类存在多个构造方法,Spring会使用哪个呢?

Spring的判断逻辑如下:

  1. 如果一个类只存在一个构造方法,不管该构造方法是无参构造方法,还是有参构造方法,Spring都会用这个构造方法
  2. 如果一个类存在多个构造方法
    a. 这些构造方法中,存在一个无参的构造方法,那么Spring就会用这个无参的构造方法
    b. 这些构造方法中,不存在一个无参的构造方法,那么Spring就会报错

Spring的设计思想是这样的:

  1. 如果一个类只有一个构造方法,那么没得选择,只能用这个构造方法
  2. 如果一个类存在多个构造方法,Spring不知道如何选择,就会看是否有无参的构造方法,因为无参构造方法本身表示了一种默认的意义
  3. 不过如果某个构造方法上加了@Autowired注解,那就表示程序员告诉Spring就用这个加了注解的方法,那Spring就会用这个加了@Autowired注解构造方法了

需要重视的是,如果Spring选择了一个有参的构造方法,Spring在调用这个有参构造方法时,需要传入参数,那这个参数是怎么来的呢?

Spring会根据入参的类型和入参的名字去Spring中找Bean对象(以单例Bean为例,Spring会从单例池那个Map中去找):

  1. 先根据入参类型找,如果只找到一个,那就直接用来作为入参
  2. 如果根据类型找到多个,则再根据入参名字来确定唯一一个
  3. 最终如果没有找到,则会报错,无法创建当前Bean对象

确定用哪个构造方法,确定入参的Bean对象,这个过程就叫做推断构造方法。

四、AOP大致流程

AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步会去判断当前正在创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。

如何判断当前Bean对象需不需要进行AOP:

  1. 找出所有的切面Bean
  2. 遍历切面中的每个方法,看是否写了@Before、@After等注解
  3. 如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
  4. 如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP

利用cglib进行AOP的大致流程:

  1. 生成代理类UserServiceProxy,代理类继承UserService
  2. 代理类中重写了父类的方法,比如UserService中的test()方法
  3. 代理类中还会有一个target属性,该属性的值为被代理对象(也就是通过UserService类推断构造方法实例化出来的对象,进行了依赖注入、初始化等步骤的对象)
  4. 代理类中的test()方法被执行时的逻辑如下:
    a. 执行切面逻辑(@Before)
    b. 调用target.test()

当我们从Spring容器得到UserService的Bean对象时,拿到的就是UserServiceProxy所生成的对象,也就是代理对象。

UserService代理对象.test()—>执行切面逻辑—>target.test(),注意target对象不是代理对象,而是被代理对象。

其实代理代理就是创建一个类的子类,去基础它,然后在内部创建一个类(类似:UserService target;),然后把target = 传进来普通UserService

五、Spring事务

当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。

Spring事务的代理对象执行某个方法时的步骤:

  1. 判断当前执行的方法是否存在@Transactional注解
  2. 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
  3. 修改数据库连接的autocommit为false
  4. 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
  5. 执行完了之后如果没有出现异常,则提交,否则回滚

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148696.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cannot resolve MVC view ‘xxx‘

这是在springboot下通过controller访问templates目录下的静态文件&#xff08;Hello.html)报的错误 原因&#xff1a;缺少thymeleaf依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</ar…

SSM - Springboot - MyBatis-Plus 全栈体系(十八)

第四章 SpringMVC SpringMVC 实战&#xff1a;构建高效表述层框架 一、SpringMVC 简介和体验 1. 介绍 Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从一开始就包含在 Spring Framework 中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08…

【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述

前言 本文基础知识部分来自于b站&#xff1a;分享笔记的好人儿的思维导图与王道考研课程&#xff0c;感谢大佬的开源精神&#xff0c;习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析&#xff0c;本人技术…

剑指offer——JZ35 复杂链表的复制 解题思路与具体代码【C++】

一、题目描述与要求 复杂链表的复制_牛客题霸_牛客网 (nowcoder.com) 题目描述 输入一个复杂链表&#xff08;每个节点中有节点值&#xff0c;以及两个指针&#xff0c;一个指向下一个节点&#xff0c;另一个特殊指针random指向一个随机节点&#xff09;&#xff0c;请对此链…

QT商业播放器

QT商业播放器 总体架构图 架构优点&#xff1a;解耦&#xff0c;采用生产者消费者设计模式&#xff0c;各个线程各司其职&#xff0c;通过消息队列高效协作 这个项目是一个基于ijkplayer和ffplayer.c的QT商业播放器, 项目有5部分构成&#xff1a; 前端QT用户界面 后端是集成了…

成都建筑模板批发市场在哪?

成都作为中国西南地区的重要城市&#xff0c;建筑业蓬勃发展&#xff0c;建筑模板作为建筑施工的重要材料之一&#xff0c;在成都也有着广泛的需求。如果您正在寻找成都的建筑模板批发市场&#xff0c;广西贵港市能强优品木业有限公司是一家值得关注的供应商。广西贵港市能强优…

数组(数据结构)

优质博文&#xff1a;IT-BLOG-CN 一、简介 数组Array是一种线性表数据结构&#xff0c;它用一组连续的内存空间&#xff0c;存储一组具有相同类型的数据。 数组因具有连续的内存空间的特点&#xff0c;数据拥有非常高效率的“随机访问”&#xff0c;时间复杂度为O(1)。但因要保…

高中生自学Python,这里给大家一些建议

高一学业压力比较重&#xff0c;如果你还是选择自学Python&#xff0c;每天可以抽出一两个小时来学习的话&#xff0c;也是可以的。下面是我给你的5点建议&#xff1a; 找浅显易懂&#xff0c;例子比较好的教程&#xff0c;从头到尾看下去。不要看很多本&#xff0c;专注于一本…

算法通过村第十一关-位运算|黄金笔记|位运算压缩

文章目录 前言用4kb内存寻找重复元素总结 前言 提示&#xff1a;如果谁对你说了地狱般的话&#xff0c;就代表了他的心在地狱。你不需要相信那样的话&#xff0c;就算对方是你的父母也一样。 --高延秀《远看是蔚蓝的春天》 位运算有个很重要的作用就是能用比较小的空间存储比较…

DHCPsnooping 配置实验(2)

DHCP报文泛洪攻击 限制接收到报文的速率 vlan 视图或者接口视图 dhcp request/ dhcp-rate dhcp snooping check dhcp-request enable dhcp snooping alarm dhcp-request enable dhcp snooping alarm dhcp-request threshold 1 超过则丢弃报文 查看[Huawei]dis dhcp statistic…

【已解决】RuntimeError Java gateway process exited before sending its port number

RuntimeError: Java gateway process exited before sending its port number 问题 思路 &#x1f3af;方法一 在代码前加入如下代码&#xff08;如图&#xff09;&#xff1a; import os os.environ[‘JAVA_HOME’] “/usr/local/jdk1.8.0_221” # 记得把地址改成自己的 …

CV经典任务(二)目标检测 |单目标,多目标 非极大值抑制等

文章目录 1 目标检测1.1 单目标检测1.2 多目标检测3.2.1 阶段一 单像素点采样目标检测3.2.2 阶段二 多像素点采样目标检测3.2.3 阶段三 RNN3.2.4 阶段四 一阶段的目标检测 Yolo/SSD 1 目标检测 目标检测的重要任务是 目标定位&#xff1a;目标检测的首要任务是确定图像中对象…

大促节奏:速卖通黑五接力双十一,如何打造产品权重瓜分活动流量

双十一和黑五作为一种独特的消费文化现象&#xff0c;已经逐渐成为了消费领域中的一块“金字招牌”。无论是消费者还是商家&#xff0c;都非常期待这一天的到来&#xff0c;因为它不仅代表着购物的欲望和刺激&#xff0c;更重要的是&#xff0c;双十一和黑五已经成为了一种全新…

云安全之等级保护介绍

网络安全部门概述 网络安全部门 1. 公安部 网安/网警/网监:全称“公共信息网络安全监察”&#xff0c;后改为“网络安全保卫部门”。 简称网监&#xff0c;是中华人民共和国公安部门的一项职责&#xff0c;具体实施这一职责的机构称为网监机关或网监部门(公共信息网络安全监…

解决高分屏DPI缩放PC端百度网盘界面模糊的问题

第一步 更新最新版本 首先&#xff0c;在百度网盘官网下载最新安装包&#xff1a; https://pan.baidu.com/download 进行覆盖安装 第二步 修改兼容性设置 右键百度网盘图标&#xff0c;点击属性&#xff0c;在兼容性选项卡中点击更改所有用户的设置 弹出的选项卡中选择更改高…

案例题--Web应用考点

案例题--Web应用考点 负载均衡技术微服务XML和JSON无状态和有状态真题 在选择题中没有考察过web的相关知识&#xff0c;主要就是在案例分析题中考察 负载均衡技术 应用层负载均衡技术 传输层负载均衡技术 就近的找到距离最近的服务器&#xff0c;并进行分发 使用户就近获取…

K8S网络原理

文章目录 一、Kubernetes网络模型设计原则IP-per-Pod模型 二、Kubernetes的网络实现容器到容器的通信Pod之间的通信同一个Node内Pod之间的通信不同Node上Pod之间的通信 CNI网络模型CNM模型CNI模型在Kubernetes中使用网络插件 开源的网络组件FlannelFlannel实现图Flannel特点 Op…

Mysql技术文档--慢mysql的优化--工作流--按步排查

这里是用来发现慢sql的一个好方法 --by.阿丹 Prometheus-监控Mysql进阶用法(1)&#xff08;安装配置&#xff09;_一单成的博客-CSDN博客 阿丹&#xff1a; 知道了慢sql的语句那么就开始按照优化步骤对sql进行排查和优化。 1、阅读sql逻辑 首先观察sql语句的书写&#xff0…

什么是 MyBatis?与 Hibernate 的区别

引言 在现代的应用程序开发中&#xff0c;与数据库的交互是至关重要的。为了简化数据库访问&#xff0c;许多开发者选择使用ORM&#xff08;对象-关系映射&#xff09;框架。MyBatis和Hibernate都是流行的ORM框架&#xff0c;它们可以帮助开发者更轻松地将Java对象映射到数据库…

OOTD | 美式复古穿搭耳机,复古轻便的头戴式耳机推荐

复古耳机更能带来年代感的复古数码产品&#xff0c;头戴式耳机就好似是时光滤镜的时髦配饰&#xff0c;不说功能实用性&#xff0c;在造型上添加就很酷。 随着时代的发展&#xff0c;时尚有了新的定义。对如今的消费者来说&#xff0c;时尚不仅是美学与个性的展现&#xff0c;…