Spring知识点
框架知识点
Spring
1. 什么是Spring
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架。
目的是 用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。
常见的配置方式有三种:基于 XML的配置、基于注解的配置、基于Java的配置。
2. 说一下你对 Spring 的理解
Spring框架核心特性包括:
- IOC容器:Spring通过控制反转实现了对象的创建和对象间的依赖关系管理。开发者只需要定义好Bean及其依赖关系,Spring容器负责创建和组装这些对象。
- AOP:面向切面编程,运行开发者定义横切关注点,例如事务管理、安全控制等,独立于业务逻辑的代码。通过AOP,可以将这些关注的模板化,提高了代码的可维护性和可重用性。
- 事务管理:Spring提供了一致的事务管理接口,支持声明式和编程式事务。
- MVC框架:Spring MVC是一个基于Servlet API构建的Web框架,采用了模型-视图-控制器(MVC)架构。它支持灵活的URL到页面控制器的映射,以及多种视图技术。
3. Spring IoC和AOP 介绍一下
IOC:
- IOC(Inverse Of Controll,控制反转):就是原来代码里面需要自己手动创建的对象,依赖,反转给Spring来帮忙实现。我们需要创建⼀个容器,同时需要⼀种描述来让容器知道要创建的对象与对象之间的关系。
- 在Spring中BeanFactory就是IOC容器,在Spring初始化的时候,创建容器,并将需要创建对象和对象的关系 (xml,注解)通过BeanDefinitionReader加载到BeanDefinition中并保存在BeanDefinitionMap中,然后再 由IOC容器创建bean对象.
两种bean的注册方式
- 通过@Bean+@Configuration的方式直接定义要创建的对象与对象的关系
- 通过@Component定义类,这种方式必须使用@ComponetScan定位Bean扫描路径
AOP
- AOP 在面向对象编程(oop)思想中,我们将事物纵向抽成⼀个个的对象。而在面向切面编程中,我们将⼀个个的对象某些类似的方面横向抽成⼀个切面,对这个切面进行⼀些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP底层是动态代理,如果是接口采用JDK 动态代理,如果是类采用CGLIB方式实现动态代理。
4. Spring的aop介绍一下
我们知道,Java 就是一门面向对象编程的语言,在 OOP 中最小的单元就是“Class 对象”,但是在 AOP 中最小的单元是“切面”。一个“切面”可以包含很多种类型和对象,对它们进行模块化管理,例如事务管理。
在面向切面编程的思想里面,把功能分为两种
- 核心业务:登陆、注册、增、删、改、查、都叫核心业务
- 周边功能:日志、事务管理这些次要的为周边业务
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 是基于 JDK 动态代理和 Cglib 提升实现的,两种代理方式都属于运行时的一个方式,所以它没有编译时的一个处理,那么因此 Spring 是通过 Java 代码实现的。
5. IOC和AOP是通过什么机制来实现的?
Spring IOC实现机制
- 反射:Spring IOC容器利用Java的反射机制动态的加载类、创建对象实例以及调用对象方法,反射运行在运行时检测类、方法、属性的信息,从而实现灵活的对象实例化和管理。
- 依赖注入:IOC的核心概念就是依赖注入,即容器负责管理应用程序组件之间的依赖关系。Spring有三种注入方式:构造器注入、setter方法注入和根据注解注入。
- 工厂模式:Spring IOC容器通常采用工厂模式来管理对象的创建和生命周期。容器作为工厂负责实例化Bean并管理它们的生命周期,将Bean的实例化过程交给容器来管理。
- 容器实现:Spring IOC容器是实现IOC的核心,通常使用BeanFactory或ApplicationContext来管理Bean。BeanFactory是IOC容器的基本形式,提供基本的IOC功能;ApplicationContext是BeanFactory的扩展,并提供更多企业级功能。
Spring AOP 实现机制
Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。
Spring AOP支持两种动态代理:
- 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口。
- 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。
6. 怎么理解Spring IOC?
IOC(控制反转):就是原来代码里面需要自己手动创建的对象,依赖,反转给Spring来帮忙实现。我们需要创建⼀个容器,同时需要⼀种描述来让容器知道要创建的对象与对象之间的关系。
所谓控制就是对象的创建、初始化、销毁。
- 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
- 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。
- 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。
这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。
7. 依赖倒置,依赖注入,控制反转分别是什么?
控制反转:就是原来代码里面需要自己手动创建的对象,依赖,反转给Spring来帮忙实现。
依赖注入:我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
依赖倒置:(是软件设计六大之一原则)高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。
简单来说,就是一台电脑有很多组件,比如cpu+硬盘等等,我们可以用不同品牌的cpu和不同品牌的硬盘。但不能一台电脑只能用专门的cpu而不能换吧!
8. 如果让你设计一个SpringIoc,你觉得会从哪些方面考虑这个设计?
- Bean生命管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式实现。
- 依赖注入:需要实现依赖注入功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
- Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求,可以考虑使用Map来存储不同作用域的Bean的实例。
- AOP功能的实现:需要支持AOP功能,可以考虑使用动态代理机制和面向切面编程实现。
- 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
- 配置文件加载:需要支持从不同的配置文件中加载Bean的相关信息,可以考虑使用XML、注解或者Java配置类来实现。
9. SpringAOP的原理了解吗
Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。
支持两种代理模式:
- 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口。
- 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理类的子类作为代理。CGLIB是一个第三方代码生成库,通过继承方式实现代理。
10. 动态代理是什么?
Java的动态代理是一种在运行是动态创建代理对象的机制,主要用于在不修改原始类的情况下对方法调用进行拦截和增强。
动态代理主要分为两种类型
- 基于接口的代理:这种类型的代理要求目标对象必须实现至少一个接口。Java动态代理会创建一个实现了相同接口的代理类,然后在运行时动态生成该类的实例。
- 基于类的代理:CGLIB是一个强大的高性能的代码生成库,它可以在运行时动态生成一个目标类的子类。CGLIB代理不需要目标类实现接口,而是通过继承的方式创建代理类。
11. 动态代理和静态代理的区别
区别:
- 静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
- 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类。
12. AOP实现有哪些注解?
- @Aspect:用于定义切面,标注在切面类上。
- @Pointcut:定义切点,标注在方法上,用于指定连接点。
- @Before:在方法之前执行通知。
- @After:在方法执行之后执行通知。
- @Around:在方法执行前后都执行通知。
- @Advice:通用的通知类型。
13. 什么是反射?有哪些使用场景?
Java的反射机制是指在运行时获得类的信息,创建类的对象,调用其中的方法和属性。
优点
- 可以动态的获取类信息。
- 可以动态的创建类对象。
- 可以动态的调用类对象的方法以及属性。
缺点:
- 不安全,反射能够获取所有类对象,包括私有的,破坏了程序的封装性。
- 效率低,反射的效率较低。
应用场景:
- 动态代理,因为不确定需要代理的类,所以需要通过反射动态的获取
- RPC 框架,RPC 框架就是动态的生成类对象,然后调用方法的。
14. spring是如何解决循环依赖的?
循环依赖是指两个类中的属性相互依赖对方:例如A类中有B类的属性,B类有A类的属性。
循环依赖主要有三种情况
- 通过构造方法进行依赖注入时产生
- 通过setter方法进行依赖注入且是在多例模式下产生的循环依赖问题。
- 通过setter方法进行依赖注入而且是在单例模式下产生的循环依赖问题。
只有【第三种方式】
的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常。
Spring 解决单例模式下的setter循环依赖问题的主要方式是通过三级缓存解决循环依赖。
- A 和 B 两个 bean 出现循环依赖的执行流程
- 获取到需要进行生成 bean 的 BeanDefinition 集合
- 生成 A
- 将 A 放入正在创建的 bean 集合中
- 依次查看一二三缓存中是否有 A, 如果有则返回
- 没有则通过反射实例化 A 对象; 并将存入三级缓存中; key: beanId, value: 获取 Abean 的 lambda 方法
- 开始填充属性
- 发现需要 B 进行填充
- 生成 B
- 将 B 放入正在创建的 bean 集合中
- 从一二三级缓存中一次查找是否有 B
- 通过反射实例化 B, 并将存入三级缓存中
- 填充属性, 发现需要 A
- 从一二三级缓存中查找 A, 在三级缓存中查找到 A, 获取 A 的 value (即为 lambda 函数)
- 如果 A 被代理了, 那么就在这里提前代理 A, 并返回被代理后的 A bean (属性未完整填充)
- 若没有被代理, 那么就直接返回 A bean (属性未完整填充)
- 因为此时 A 的 bean 是属性不完整的所以将获取到的对象保存到二级缓存中, 将三级缓存中的 A 移除
- 用不完整的 A 对 B 进行字段填充, 得到完整的 B 对象
- 将 B 对象移动到一级缓存中
- 从一二三级缓存中查找 B, 成功查找到 B 为 A 进行字段赋值
- 得到完整的 A 对象将 A 对象移动到一级缓存中
- 生成 B
- 依次查找一二三级缓存中是否有 B
- B 已经存在返回
- 为什么要二级缓存, 一级缓存能实现吗?
理论上可以的, 只要在设计一级缓存的过程中能准确的标识当前 bean 是处于完成状态还是半成品状态即可; 但是如果通过二级缓存, 可以简化代码开发, 并且能提高每次的判断执行效率, 所以引入二级缓存
- 为什么要引入三级缓存, 不引入三级缓存可以吗?
如果不引入三级缓存的话会造成一下问题, 如果 B 通过二级缓存将 A 的值进行填充了那么 B 中 A 的对象就是 A 的原始 bean 对象; 因为 bean 的生命周期中 bean 的字段填充是发生在初始化之前的, 所以当 A 进行后续操作中被代理了功能得到增强了, 那么 B 中的 A 字段是无法被更新和感知的额; 所以引入三级缓存的概念, 如果 A 被代理了, 那么在 B 在进行赋值的时候就可以将代理提前
15. spring 常用注解有什么?
@Autowired注解
主要用于自动装配bean
@Component
这个注解用于标记一个类作为Spring的bean。当一个类被@Component注解标记时,Spring会将其实例化为一个bean,并将其添加到Spring容器中。
@Configuration
@Configuration,注解用于标记一个类作为Spring的配置类。配置类可以包含@Bean注解的方法,用于定义和配置bean,作为全局配置。
@Bean
@Bean注解用于标记一个方法作为Spring的bean工厂方法。当一个方法被@Bean注解标记时,Spring会将该方法的返回值作为一个bean,并将其添加到Spring容器中。
@Service
Repository
@Controller
16. Spring的事务什么情况下会失效?
- 未捕获异常:事务方法中发生了未捕获的异常,并且异常未被处理或传播到事务边界之外,那么事务会失效
- 事务传播属性设置不当:如果在多个事务之间存在事务嵌套,且事务传播属性配置不正确,可能导致事务失效。
- 跨方法调用事务问题:如果一个事务方法内部调用另一个方法,而这个被调用的方法没有 @Transactional 注解,这种情况下外层事务可能会失效。
- 事务在非公开方法中失效:如果 @Transactional 注解标注在私有方法上或者非 public 方法上,事务也会失效。
17. Spring的事务,使用this调用是否生效?
不能生效。
Spring事务是通过代理对象来控制的。
当使用this
直接调用时,是绕过了Spring的代理机制,因此不会应用事务设置。
18. Bean的生命周期说一下?
- 实例化Bean:Spring启动,查找并加载需要被Spring管理的bean
- 设置对象属性(依赖注入):Bean实例化后对将Bean的引入和值注入到Bean的属性中。
- 处理Aware接口:Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean
- 如果实现了BeanNameAware接口:Spring将Bean的Id传递给setBeanName()方法
- 如果实现了BeanFactoryAware接口:将BeanFactory容器实例传入
- 如果实现了ApplicationContextAware接口:调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
- 如果实现BeanPostProcessor
- 如果实现了InitializingBean
- 如果实现了BeanPostProcessor
- 此时Bean就已经被正确创建了。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法。
19. Bean是否单例?
Spring 中的 Bean 默认都是单例的。
但是,Spring也支持将Bean设置为多例模式,可以在Bean定义中通过设置scope属性为”prototype”来实现。
20. Bean的单例和非单例,生命周期是否一样
不一样的。Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype
的 Bean,Spring 在创建好交给使用者之后,则不会再管理后续的生命周期。
21. Spring bean的作用域有哪些?
- Singleton(单例):整个应用程序中只存在一个 Bean 实例,默认作用域。
- Prototype(原型):每次请求时都会创建一个新的 Bean 实例。
- Request(请求):每个 HTTP 请求都会创建一个新的 Bean 实例。
- Session(会话):Session 范围内只会创建一个 Bean 实例。
22. Spring容器里存的是什么?
在Spring容器中,存储的主要是Bean对象。
Bean是Spring框架中的基本组件,用于表示应用程序中的各种对象。
23. 在Spring中,在bean加载/销毁前后,如果想实现某些逻辑,可以怎么做?
可以使用Spring的生命周期回调接口或注解。这些接口和注解允许你定义在Bean生命周期的关键点执行的代码。
使用init-method和destory-method
在XML配置中,你可以通过init-method和destroy-method属性来指定Bean初始化后和销毁前需要调用的方法。
然后在Bean类中实现这个方法。
实现initializing和disposableBean接口
分别实现afterPropertiesSet和destroy方法。
1 | ublic class MyBeanClass implements InitializingBean, DisposableBean { |
使用@PostConstruct和@PreDestory注解
1 | public class MyBeanClass { |
24. Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的
XML注入
- Bean 定义解析:Spring 容器通过
XmlBeanDefinitionReader
类解析 XML 配置文件,读取其中<bean>
标签以获取Bean的定义信息。 - 注册Bean定义:解析后的 Bean 信息被注册到
BeanDefinitionRegistry
中。
- 实例化和依赖注入:首先,使用反射机制创建该 Bean 的实例。然后,根据 Bean 定义中的配置,通过 setter 方法、构造函数或方法注入所需的依赖 Bean。
注解注入
- 类路径扫描:当 Spring 容器启动时,它首先会进行类路径扫描,查找带有特定注解。
- 注册Bean定义:找到的类会被注册到
BeanDefinitionRegistry
中,Spring 容器将为其生成 Bean 定义信息。 - 依赖注入:Spring 在实例化 Bean 时,也会检查字段上是否有
@Autowired
、@Inject
或@Resource
注解。如果有,Spring 会根据注解的信息进行依赖注入。
25. 什么是SpringMVC
首先聊聊什么是 MVC,MVC 是一种编程思想, 他规定了 Model,view,Controller 三个层次; 而 SpringMVC 是 spring 生态在对 MVC 的一种实现, 他在传统的 MVC 框架上做了拓展, 将 Model 层拆分层了业务模型和数据模型分别是 Service 和 Repository, Contoller 才拆分成了前端控制器和后端控制器分别是 DispatcherServlet 和 Controller; 简单来说 SpringMVC 就是在 Spring 和 Sevlet 的基础上实现的 MVC 模式让我们更方便更清晰的开发网络服务
26. Spring的父子容器是什么
父容器
:负责管理应用程序中通用的bean,比如数据库连接、事务管理器等,生命周期与整个应用程序一致子容器
:负责特定模块或组件的容器,可以共享父容器中的bean并且子容器中的bean不会影响父容器,比如controller控制器,生命周期与模块或组件一致
SpringBoot
1. 为什么使用springboot
- 简化开发:Spring Boot通过提供一系列的组件和自动装配,简化了项目的配置和开发过程。
- 快速启动:Spring Boot提供了快速的应用程序启动方式,可以通过内嵌的Tomcat、Jetty等容器快速启动应用程序。
- 自动化配置:通过自动配置功能,根据项目中的依赖关系和约定俗成的规则来配置应用程序,减少了配置的复杂性。
2. SpringBoot比Spring好在哪里
- 自动配置:提供了自动化配置,大大简化了项目的配置过程。通过约定优于配置的原则,很多常用的配置可以自动完成。
- 快速启动:提供了快速的项目启动器,通过引入不同的 Starter,可以快速集成常用的框架和库,极大的提高了开发效率。
- Spring Boot 默认集成了多种内嵌服务器(如Tomcat、Jetty、Undertow),无需额外配置,即可将应用打包成可执行的 JAR 文件,方便部署和运行。
3. SpringBoot用到哪些设计模式?
- 代理模式:Spring的AOP通过动态代理方式实现方法级别的切面增强。
- 策略模式:Spring AOP 支持 JDK 和 Cglib 两种动态代理实现方式,通过策略接口和不同策略类,运行时动态选择,其创建一般通过工厂方法实现。
- 单列模式:Spring Bean 默认是单例模式。
- 简单工厂模式:Spring 中的 BeanFactory 是简单工厂模式的体现。
- 工厂方法模式:Spring中的 FactoryBean 体现工厂方法模式,为不同产品提供不同工厂。
- 观察者模式:Spring 观察者模式包含 Event 事件、Listener 监听者、Publisher 发送者,通过定义事件、监听器和发送者实现,观察者注册在 ApplicationContext 中,消息发送由 ApplicationEventMulticaster 完成。
- 模板方法模式:Spring Bean 的创建过程涉及模板模式,体现扩展性,类似 Callback 回调实现方式。
- 适配器模式:Spring MVC 中针对不同方式定义的 Controller,利用适配器模式统一函数定义,定义了统一接口 HandlerAdapter 及对应适配器类。
4. 怎么理解SpringBoot中的约定大于配置
- 自动化配置:Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。开发者无需显式地配置每个细节,大部分常用的配置都已经预设好了。
- 默认配置:Spring Boot 在没有明确配置的情况下,会使用合理的默认值来初始化应用程序。
- 约定优于配置:遵循了约定优于配置的设计哲学,即通过约定好的方式来提供默认行为,减少开发者需要做出的决策。例如,约定了项目结构、Bean 命名规范等,使得开发者可以更快地上手并保持团队间的一致性。
Spring Boot通过「自动化配置」和「起步依赖」实现了约定大于配置的特性。
- 自动化配置:根据项目的依赖和环境自动配置应用程序,无需手动配置大量的XML或Java配置文件。
- 起步依赖:Spring Boot提供了一系列起步依赖,这些依赖包含了常用的框架和功能,可以帮助开发者快速搭建项目。