当前位置: 首页 > news >正文

Spring 核心知识点梳理 1

目录

Spring

Spring是什么?

Spring中重要的模块

Spring中最重要的就是IOC(控制反转)和AOP(面向切面编程)

什么是IOC

DI和IOC之间的区别

为什么要使用IOC呢?

IOC的实现机制

什么是AOP

Aop的核心概念

AOP的环绕方式

AOP发生的时期

AOP和OOP的关系

SpringIOC和AOP的核心设计模式?

Spring中的常用注解

Spring用了哪些设计模式?

Spring如何实现的单例模式?

 Spring中的单例bean是线程安全的吗

如何解决单例bean线程不安全的问题呢

Spring容器和Web容器的区别

BeanFactory和ApplicationContext的区别

项目启动时Spring的IOC会做什么

Spring的Bean实例化的方法

你是怎么理解bean的

Spring中Bean的配置方式

@Component和@Bean的区别

Bean的生命周期

Aware类型的接口作用

init-method和destroy-method的时机

@Autowired和@Resource 的区别

为什么不推荐使用@Autowired

什么是自动装配

Bean的作用域

循环依赖

Spring能解决哪些情况的循环问题呢

如何解决呢?循环依赖问题

为什么三级,二级不可以吗?

JDK动态代理和Cglib动态代理

选择JDK动态代理和Cglib代理


Spring

Spring是什么?

Spring 是一个 Java 后端开发框架,其最核心的作用是帮我们管理 Java 对象。

Spring中重要的模块

  • Spring Core: 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。
  • Spring Aspects : 该模块为与AspectJ的集成提供支持。
  • Spring AOP :提供了面向切面的编程实现。
  • Spring JDBC : Java数据库连接。
  • Spring JMS :Java消息服务。
  • Spring ORM : 用于支持Hibernate等ORM工具。
  • Spring Web : 为创建Web应用程序提供支持。
  • Spring Test : 提供了对 JUnit 和 TestNG 测试的支持。

Spring中最重要的就是IOC(控制反转)和AOP(面向切面编程)

什么是IOC

IoC 控制反转是一种设计思想,它的主要作用是将对象的创建和对象之间的调用过程交给 Spring 容器来管理。

DI和IOC之间的区别

IoC 的思想是把对象创建和依赖关系的控制权由业务代码转移给 Spring 容器。这是一个比较抽象的概念,告诉我们应该怎么去设计系统架构。

而 DI,也就是依赖注入,它是实现 IoC 这种思想的具体技术手段。在 Spring 里,我们用 @Autowired 注解就是在使用 DI 的字段注入方式。

为什么要使用IOC呢?

在日常开发中,我们可能需要多个对象来协助完成,这就要在自己new一个,A要使用B,A就对B产生了依赖,也就产生了一种耦合关系,有了Spring,那么创建工作就交给Spring,这种耦合度就降低了。

IOC的实现机制

第一步,扫描整个包,找到所有配置了@Component@Service@Repository 这些注解的类,然后把这些类的元信息封装成 BeanDefinition 对象。

第二步 Bean 工厂的准备。Spring 会创建一个 DefaultListableBeanFactory 作为 Bean 工厂来负责 Bean 的创建和管理。

第三步是 Bean 的实例化和初始化。这个过程比较复杂,Spring 会根据 BeanDefinition 来创建 Bean 实例。

对于单例 Bean,Spring 会先检查缓存中是否已经存在,如果不存在就创建新实例。创建实例的时候会通过反射调用构造方法,然后进行属性注入,最后执行初始化回调方法。

依赖注入的实现主要是通过反射来完成的。比如我们用 @Autowired 标注了一个字段,Spring 在创建 Bean 的时候会扫描这个字段,然后从容器中找到对应类型的 Bean,通过反射的方式设置到这个字段上。

什么是AOP

AOP 面向切面编程,简单点说就是把一些通用的功能从业务代码里抽取出来,统一处理。

Aop的核心概念
  • 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
  • 连接点(Join Point):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中,连接点指的是被拦截到的方法,实际上连接点还可以是字段或者构造方法
  • 切点(Pointcut):对连接点进行拦截的定位
  • 通知(Advice):指拦截到连接点之后要执行的代码,也可以称作增强
  • 目标对象 (Target):代理的目标对象
  • 引介(introduction):一种特殊的增强,可以动态地为类添加一些属性和方法
  • 织入(Weabing):织入是将增强添加到目标类的具体连接点上的过程。
AOP的环绕方式

AOP 一般有 5 种环绕方式:

  • 前置通知 (@Before)
  • 返回通知 (@AfterReturning)
  • 异常通知 (@AfterThrowing)
  • 后置通知 (@After)
  • 环绕通知 (@Around)
AOP发生的时期

运行时发生,这意味着 Spring AOP 是在运行时通过动态代理生成的,而不是在编译时或类加载时生成的。

AOP和OOP的关系

AOP 和 OOP 是互补的编程思想:

  1. OOP 通过类和对象封装数据和行为,专注于核心业务逻辑。
  2. AOP 提供了解决横切关注点(如日志、权限、事务等)的机制,将这些逻辑集中管理。

SpringIOC和AOP的核心设计模式?

IOC是工厂模式

AOP是代理模式

Spring中的常用注解

首先是 Bean 管理相关的注解。@Component 是最基础的,用来标识一个类是 Spring 组件。像 @Service@Repository@Controller 这些都是 @Component 的特化版本,分别用在服务层、数据访问层和控制器层。

依赖注入方面,@Autowired 是用得最多的,可以标注在字段、setter 方法或者构造方法上。@Qualifier 在有多个同类型 Bean 的时候用来指定具体注入哪一个。@Resource 和 @Autowired 功能差不多,不过它是按名称注入的。

配置相关的注解也很常用。@Configuration 标识配置类,@Bean 用来定义 Bean,@Value 用来注入配置文件中的属性值。@PropertySource 用来指定配置文件的位置。

@RestController 相当于 @Controller 加 @ResponseBody,用来做 RESTful 接口。

@RequestMapping 及其变体@GetMapping@PostMapping@PutMapping@DeleteMapping 用来映射 HTTP 请求。@PathVariable 获取路径参数,@RequestParam 获取请求参数,@RequestBody 接收 JSON 数据。

AOP 相关的注解,@Aspect 定义切面,@Pointcut 定义切点,@Before@After@Around 这些定义通知类型。

@Transactional,保证事务。

生命周期相关的,@PostConstruct 在 Bean 初始化后执行,@PreDestroy 在 Bean 销毁前执行。测试的时候 @SpringBootTest 也经常用到。

@SpringBootApplication 这个启动类注解,@ConditionalOnProperty 做条件装配,@EnableAutoConfiguration 开启自动配置等等。

Spring用了哪些设计模式?

首先是工厂模式,这个在 Spring 里用得非常多。BeanFactory 就是一个典型的工厂,它负责创建和管理所有的 Bean 对象。我们平时用的 ApplicationContext 其实也是 BeanFactory 的一个实现。当我们通过 @Autowired 获取一个 Bean 的时候,底层就是通过工厂模式来创建和获取对象的。 

 单例模式也是 Spring 的默认行为。默认情况下,Spring 容器中的 Bean 都是单例的

可以通过 @Scope 注解来改变 Bean 的作用域,比如设置为 prototype 就是每次获取都创建新实例。

代理模式在 AOP 中用得特别多。Spring AOP 的底层实现就是基于动态代理的,对于实现了接口的类用 JDK 动态代理,没有实现接口的类用 CGLIB 代理。

模板方法模式在 Spring 里也很常见,比如 JdbcTemplate。它定义了数据库操作的基本流程:获取连接、执行 SQL、处理结果、关闭连接,但是具体的 SQL 语句和结果处理逻辑由我们来实现。

观察者模式在 Spring 的事件机制中有所体现。我们可以通过 ApplicationEvent 和 ApplicationListener 来实现事件的发布和监听。

Spring如何实现的单例模式?

传统的单例模式是在类的内部控制只能创建一个实例,比如用 private 构造方法加 static getInstance() 这种方式。但是 Spring 的单例是容器级别的,同一个 Bean 在整个 Spring 容器中只会有一个实例。

具体的实现机制是这样的:Spring 在启动的时候会把所有的 Bean 定义信息加载进来,然后在 DefaultSingletonBeanRegistry 这个类里面维护了一个叫 singletonObjects 的 ConcurrentHashMap,这个 Map 就是用来存储单例 Bean 的。key 是 Bean 的名称,value 就是 Bean 的实例对象。

当我们第一次获取某个 Bean 的时候,Spring 会先检查 singletonObjects 这个 Map 里面有没有这个 Bean,如果没有就会创建一个新的实例,然后放到 Map 里面。后面再获取同一个 Bean 的时候,直接从 Map 里面取就行了,这样就保证了单例。

 

还有一个细节就是 Spring 为了解决循环依赖的问题,还用了三级缓存。除了 singletonObjects 这个一级缓存,还有 earlySingletonObjects 二级缓存和 singletonFactories 三级缓存。这样即使有循环依赖,Spring 也能正确处理。

 Spring中的单例bean是线程安全的吗

分两个层面

Spring管理bean是线程安全的。Spring 在容器启动阶段
使用 ConcurrentHashMap(一级缓存 singletonObjects)来存放并查找已经创建好的单例实例,确实保证了“多个线程同时去获取同一个 Bean 时不会重复创建实例”。

单例 Bean 的业务代码
容器把同一个实例注入到所有需要的地方后,如果该实例内部有可变的共享状态(字段、缓存、集合等),Spring 不会帮你加任何锁。并发读写这些字段时,线程安全问题照样会出现。

如何解决单例bean线程不安全的问题呢

第一种,使用局部变量,也就是使用无状态的单例 Bean

第二种,当确实需要维护线程相关的状态时,可以使用ThreadLcoal 来保存状态。

第三种,如果需要缓存数据或者计数,使用 JUC 包下的线程安全类,比如说 AtomicInteger、CCHashMap 等。

第四种,对于复杂的状态操作,可以使用 synchronized 或 Lock

第五种,如果 Bean 确实需要维护状态,可以考虑将其改为 prototype 作用域

Spring容器和Web容器的区别

Spring 容器是一个 IoC 容器,主要负责管理 Java 对象的生命周期和依赖关系。而 Web 容器,比如 Tomcat、Jetty 这些,是用来运行 Web 应用的容器,负责处理 HTTP 请求和响应,管理 Servlet 的生命周期。

从功能上看,Spring 容器专注于业务逻辑层面的对象管理,比如我们的 Service、Dao、Controller 这些 Bean 都是由 Spring 容器来创建和管理的。而 Web 容器主要处理网络通信,比如接收 HTTP 请求、解析请求参数、调用相应的 Servlet,然后把响应返回给客户端。

生命周期上:Web 容器的生命周期跟 Web 应用程序的部署和卸载相关,而 Spring 容器的生命周期是在 Web 应用启动的时候初始化,应用关闭的时候销毁。

BeanFactory和ApplicationContext的区别

BeanFactory 算是 Spring 的“心脏”,而 ApplicantContext 可以说是 Spring 的完整“身躯”。

BeanFactory 提供了最基本的 IoC 能力。它就像是一个 Bean 工厂,负责 Bean 的创建和管理。他采用的是懒加载的方式,也就是说只有当我们真正去获取某个 Bean 的时候,它才会去创建这个 Bean。

它最主要的方法就是 getBean(),负责从容器中返回特定名称或者类型的 Bean 实例。

ApplicationContext 是 BeanFactory 的子接口,在 BeanFactory 的基础上扩展了很多企业级的功能。它不仅包含了 BeanFactory 的所有功能,还提供了国际化支持、事件发布机制、AOP、JDBC、ORM 框架集成等等。

ApplicationContext 采用的是饿加载的方式,容器启动的时候就会把所有的单例 Bean 都创建好,虽然这样会导致启动时间长一点,但运行时性能更好。

生命周期管理。ApplicationContext 会自动调用 Bean 的初始化和销毁方法,而 BeanFactory 需要我们手动管理。

项目启动时Spring的IOC会做什么

首先会进行扫描和注册bean。Ioc会根据我们的配置以及注解,然后把这些带注解的类的信息包装成BeanDefinition对象,注册到BeanDefinitionRegistry 中。但还么有创建。

第二件事是 Bean 的实例化和注入。这是最核心的过程,IoC 容器会按照依赖关系的顺序开始创建 Bean 实例。对于单例 Bean,容器会通过反射调用构造方法创建实例,然后进行属性注入,最后执行初始化回调方法。

在依赖注入时,容器会根据 @Autowired@Resource 这些注解,把相应的依赖对象注入到目标 Bean 中。

Spring的Bean实例化的方法

第一种是通过构造方法实例化

第二种是通过静态工厂方法实例化

第三种是通过实例工厂方法实例化。这种方式是先创建工厂对象,然后通过工厂对象的方法来创建Bean

第四种是通过 FactoryBean 接口实例化。

你是怎么理解bean的

Bean 本质上就是由 Spring 容器管理的 Java 对象

Spring中Bean的配置方式

第一种:根据XML配置(已经不怎么用了)

第二种:根据java配置类方式

第三种:基于注解的方式

@Component和@Bean的区别

@Component是标注在类上的,而 @Bean 是标注在方法上的。@Component 告诉 Spring 这个类是一个组件,请把它注册为 Bean,而 @Bean 则告诉 Spring 请将这个方法返回的对象注册为 Bean。

Bean的生命周期

第一个阶段是实例化。Spring 容器会根据 BeanDefinition,通过反射调用 Bean 的构造方法创建对象实例。

第二阶段是属性赋值。这个阶段 Spring 会给 Bean 的属性赋值

第三阶段是初始化。

第四阶段是使用 Bean。

最后是销毁阶段。当容器关闭或者 Bean 被移除的时候

Aware类型的接口作用

它们的作用是让 Bean 能够感知到 Spring 容器的一些内部组件。比如ApplicationContextAware,它可以让 Bean 获取到 ApplicationContext 容器本身。

init-method和destroy-method的时机

init-method 指定的初始化方法会在 Bean 的初始化阶段被调用,具体的执行顺序是:

  • 先执行 @PostConstruct 标注的方法
  • 然后执行 InitializingBean 接口的 afterPropertiesSet() 方法
  • 最后再执行 init-method 指定的方法

destroy-method 会在 Bean 销毁阶段被调用。

@Autowired和@Resource 的区别

@Autowired 是 Spring 框架提供的注解,而 @Resource 是 Java EE 标准提供的注解。换句话说,@Resource 是 JDK 自带的,而 @Autowired 是 Spring 特有的。

@Autowired 默认按照类型,也就是 byType 进行注入,而 @Resource 默认按照名称,也就是 byName 进行注入。

容器中存在多个相同类型的 Bean, 比如说有两个 UserRepository 的实现类,直接用 @Autowired 注入 UserRepository 时就会报错,因为 Spring 容器不知道该注入哪个实现类。

为什么不推荐使用@Autowired

第一个是字段注入不利于单元测试。字段注入需要使用反射或 Spring 容器才能注入依赖,测试更复杂;而构造方法注入可以直接通过构造方法传入 Mock 对象,测试起来更简单。

第二个是字段注入会隐藏循环依赖问题,而构造方法注入会在项目启动时就去检查依赖关系,能更早发现问题。

第三个是构造方法注入可以使用 final 字段确保依赖在对象创建时就被初始化,避免了后续修改的风险。

什么是自动装配

自动装配的本质就是让 Spring 容器自动帮我们完成 Bean 之间的依赖关系注入,而不需要我们手动去指定每个依赖。

自动装配的工作原理简单来说就是,Spring 容器在启动时自动扫描 @ComponentScan 指定包路径下的所有类,然后根据类上的注解,比如 @Autowired@Resource 等,来判断哪些 Bean 需要被自动装配。之后分析每个 Bean 的依赖关系,在创建 Bean 的时候,根据装配规则自动找到合适的依赖 Bean,最后根据反射将这些依赖注入到目标 Bean 中。

Bean的作用域

singleton 是默认的作用域。整个 Spring 容器中只会有一个 Bean 实例。

prototype每次从容器中获取 Bean 的时候都会创建一个新的实例。

如果作用于是 request,表示在 Web 应用中,每个 HTTP 请求都会创建一个新的 Bean 实例,请求结束后 Bean 就被销毁。

如果作用于是 session,表示在 Web 应用中,每个 HTTP 会话都会创建一个新的 Bean 实例,会话结束后 Bean 被销毁。 

循环依赖

AB 循环依赖,A 实例化的时候,发现依赖 B,创建 B 实例,创建 B 的时候发现需要 A,创建 A1 实例……无限套娃。。。。

Spring能解决哪些情况的循环问题呢

  • AB 均采用构造器注入,不支持
  • AB 均采用 setter 注入,支持
  • AB 均采用属性自动注入,支持
  • A 中注入的 B 为 setter 注入,B 中注入的 A 为构造器注入,支持
  • B 中注入的 A 为 setter 注入,A 中注入的 B 为构造器注入,不支持

第四种可以,第五种不可以的原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建。

简单总结下,当循环依赖的实例都采用 setter 方法注入时,Spring 支持,都采用构造器注入的时候,不支持;构造器注入和 setter 注入同时存在的时候,不确定。

如何解决呢?循环依赖问题

Spring 通过三级缓存机制来解决循环依赖:

  1. 一级缓存:存放完全初始化好的单例 Bean。
  2. 二级缓存:存放正在创建但未完全初始化的 Bean 实例。
  3. 三级缓存:存放 Bean 工厂对象,用于提前暴露 Bean。
为什么三级,二级不可以吗?

不可以! 因为需要生成代理对象,如果没有代理,二级也是可以的。

JDK动态代理和Cglib动态代理

①、JDK 动态代理是基于接口的代理,只能代理实现了接口的类。

使用 JDK 动态代理时,Spring AOP 会创建一个代理对象,该代理对象实现了目标对象所实现的接口,并在方法调用前后插入横切逻辑。优点:只需依赖 JDK 自带的 java.lang.reflect.Proxy 类,不需要额外的库;缺点:只能代理接口,不能代理类本身。

②、CGLIB 动态代理是基于继承的代理,可以代理没有实现接口的类。

使用 CGLIB 动态代理时,Spring AOP 会生成目标类的子类,并在方法调用前后插入横切逻辑。

优点:可以代理没有实现接口的类,灵活性更高;缺点:需要依赖 CGLIB 库,创建代理对象的开销相对较大。

选择JDK动态代理和Cglib代理

  • 如果目标对象没有实现任何接口,则只能使用 CGLIB 代理。如果目标对象实现了接口,通常首选 JDK 动态代理。
  • 虽然 CGLIB 在代理类的生成过程中可能消耗更多资源,但在运行时具有较高的性能。对于性能敏感且代理对象创建频率不高的场景,可以考虑使用 CGLIB。
  • JDK 动态代理是 Java 原生支持的,不需要额外引入库。而 CGLIB 需要将 CGLIB 库作为依赖加入项目中。

http://www.xdnf.cn/news/1169893.html

相关文章:

  • Jmeter使用 - 2
  • 第十一章 用Java实现JVM之异常处理
  • 使用 Ansys Fluent 软件参数化工作流程对搅拌罐中的稳态涡流进行仿真
  • 质量即服务:从测试策略到平台运营的全链路作战手册
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(7):自動詞 & 他動詞
  • es6中的symbol基础知识
  • Lua语言
  • web登录页面
  • Elasticsearch Java 8.x 的聚合 API 及子聚合的用法
  • 外网访问内部私有局域网方案,解决运营商只分配内网IP不给公网IP问题
  • iOS加固工具有哪些?从零源码到深度混淆的全景解读
  • DearMom以“新生儿安全系统”重塑婴儿车价值,揽获CBME双项大奖
  • vue2.0 + elementui + i18n:实现多语言功能
  • fuse低代码工作流平台概述【已开源】-自研
  • Java中关于线程池的解析
  • Qt 事件处理机制深入剖析
  • 厌氧菌数据挖掘可行性评估报告
  • 深入理解 Qt 中的 QImage 与 QPixmap:底层机制、差异、优化策略全解析
  • PyQt5在Pycharm上的环境搭建 -- Qt Designer + Pyuic + Pyrcc组合,大幅提升GUI开发效率
  • stm32 智能小车
  • [2025CVPR-小目标检测方向]基于特征信息驱动位置高斯分布估计微小目标检测模型
  • AI视频-剧本篇学习笔记
  • LeetCode 633.平方数之和
  • Leetcode力扣解题记录--第73题(矩阵置零)
  • RabbitMQ-交换机(Exchange)
  • 【大模型记忆实战Demo】基于SpringAIAlibaba通过内存和Redis两种方式实现多轮记忆对话
  • Arraylist与LinkedList区别
  • STM32-SPI全双工同步通信
  • LWIP学习记录2——MAC内核
  • mybatis多对一一对多的关联及拼接操作以及缓存处理