1.Spring
Spring,Spring MVC,Spring Boot 之间什么关系?
Spring 是一款开源的轻量级 Java 开发框架,Spring 提供的核心功能主要是 IoC 和 AOP。
Spring MVC 是 Spring 中的一个很重要的模块,主要赋予 Spring 快速构建 MVC 架构的 Web 程序的能力。MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
Spring Boot 旨在简化 Spring 开发
①Bean
什么是 Spring Bean?
Bean 指的就是那些被 IoC 容器所管理的对象。我们需要告诉 IoC 容器帮助我们管理哪些对象,通过配置来实现。配置方式可以是 XML 文件、注解或者 Java 配置类。
<bean id="book" class="com.jwt.ioc.Book">
<property name="bname" value="Java入门到精通"></property>
<property name="bauthor" value="小简"></property>
</bean>
简述 bean 的生命周期
Bean生命周期:从对象创建到对象销毁的过程
- 1、通过构造器创建 bean 实例(无参数构造)
- 2、为 bean 的属性设置值和对其他 bean 引用(调用set方法)
- 3、调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 4、bean 可以使用了(对象获取到了)
- 5、当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
如果有bean后置处理器,则bean的生命周期有七种
- 1、通过构造器创建 bean 实例(无参数构造)
- 2、为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 3、把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
- 4、调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 5、把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- 6、bean 可以使用了(对象获取到了)
- 7、当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
- 实例化(通过反射创建对象)
- 属性填充(属性值非自动装配)
- 初始化(如数据源赋值、校验属性)
- 销毁(ioc容器销毁关闭,关闭数据源)
什么是 Spring 容器
spring容器是spring的核心,一切的spring bean都存储在spring容器内,并通过ioc进行管理
spring容器就是一个bean工厂(BeanFactory),应用中bean的实例化、获取、销毁都是由这个bean工厂管理的。
ApplicationContext接口用于完成容器的配置,初始化,管理bean。一个Spring容器就是某个实现ApplicationContext
接口的类的实例。也就是说,从代码层面,Spring容器其实就是一个ApplicationContext。Spring容器的作用我觉得
是实现了控制反转这种思想
Spring自动装配方式有哪些?
bean装配:指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系
什么是自动装配:根据指定装配规则(属性名称或者属性类型),Spring容器能够自动注入相互合作的bean,这意味着容器不需要配置,能通过Bean工厂自动处理bean之间的协作。
Spring的自动装配有五种模式:
no:不进行自动装配,通过手工设置ref属性来进行装配bean。
byName:根据Bean的名字进行自动装配。
byType:根据Bean的类型进行自动装配。
constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
🌟@Autowired 和 @Resource 的区别是什么?
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为byName
(根据名称进行匹配),但也可以通过类型byType
进行匹配。- 当一个接口存在多个实现类的情况下,
@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过@Qualifier
注解来显式指定名称,@Resource
可以通过name
属性来显式指定名称。
举个例子,SmsService
接口有两个实现类: SmsServiceImpl1
和 SmsServiceImpl2
,且它们都已经被 Spring 容器所管理。
// 报错,无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;
---------------------------------------------------------------------------
// 报错,无法匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;
@Bean和@Component有什么区别?
@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。自动装配到 Spring 容器中
@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
@Bean 注解更加灵活。当需要将第三方类装配到 Spring 容器中,因为没办法源代码上添加@Component注解,只能使用@Bean 注解的方式,当然也可以使用 xml 的方式。
@Bean
注解使用示例:
@Component
public class UserServiceImpl {
private String name = "Tom";
public String getName() {
return name;
}
}
@Configuration
public class AppConfig {
@Bean
public UserService userService(){
return new UserServiceImpl();
}
}
上面的代码相当于下面的 xml 配置
<beans>
<bean id="userService" class="com.test.UserServiceImpl"/>
</beans>
BeanFactory和FactoryBean的区别?
BeanFactory:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。
FactoryBean:通常是用来创建比较复杂的bean,一般的bean直接用xml配置即可,但如果一个bean的
创建过程中涉及到很多其他的bean和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用
FactoryBean,可以隐藏实例化复杂Bean的细节。
🌟BeanFactory和ApplicationContext有什么区别?
BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器,管理Bean。其中
ApplicationContext 是 BeanFactory 的子接口。
功能上的区别
BeanFactory是Spring框架中最基本的容器,它提供了最基础的IOC和DI的支持 。它的主要功能是用于创建、管理和查找Bean对象。
ApplicationContext则是在BeanFactory基础上扩展而来的,它提供了更多的功能和特性。ApplicationContext不仅支持IOC和DI,还支持国际化、事件传递、资源加载、AOP等功能。
加载方式的区别
BeanFactory会延迟加载Bean对象,只有在第一次使用时才会进行实例化。这样做的好处是可以避免在程序启动时加载所有的Bean对象,从而提高了应用程序的启动速度和性能。
(懒汉思想生成Bean)
ApplicationContext会在应用程序启动时就立即实例化所有的Bean对象,同时也会提供更多的Bean生命周期管理功能,例如Bean的自动装配、Bean的自动注入、Bean的声明周期管理等。
(饿汉思想生成Bean)
总的来说,BeanFactory是Spring框架中最基本的容器,提供最基础的IOC和DI的支持;而ApplicationContext是在BeanFactory的基础上扩展而来的,提供了更多的功能和特性。ApplicationContext是Spring框架中使用较为广泛的容器。
Spring中bean的作用域
作用域 | 作用范围 | 描述 |
---|---|---|
singleton | 所用spring应用 | 单例,Spring中的bean默认都是单例的。 |
prototype | 所用spring应用 | 每次请求都会创建一个新的bean实例。即每次调用getBean()时,相当于执行newXxxBean()。 |
request | spring web 应用 | 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 |
session | spring web 应用 | 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean,该bean仅在当前HTTP session内有效。 |
application/global-session | spring web 应用 | 全局session作用域,每个 Web 应用在启动时创建一个 Bean,该 bean 仅在当前应用启动时间内有效。 |
websocket | spring web 应用 | 每一次 WebSocket 会话产生一个新的 bean。 |
配置 bean 的作用域呢?
xml 方式:
<bean id="..." class="..." scope="singleton"></bean>
注解方式:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
🌟Spring框架中的单例Bean是线程安全的么?
单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候是存在资源竞争的。所以最好在 Bean 中尽量避免定义可变的成员变量。
无状态bean:没有实例变量的bean,不能保存数据,是线程安全的。
有状态bean:有实例变量的bean,可以保存数据,是非线程安全的。
在Spring中无状态的Bean适合用单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下
不安全,一般用 Prototype
模式或者使用 ThreadLocal
(在类中定义一个 ThreadLocal
成员变量,将需要的可变成员变量保存在 ThreadLocal
中)解决线程安全问题。
🌟如何解决 Spring 的循环依赖问题
Spring循环依赖问题指的是在Spring容器中出现相互依赖的情况,即两个或多个Bean之间相互依赖,形成了一个循环依赖链。例如,Bean A依赖Bean B,Bean B又依赖Bean A,这就构成了一个循环依赖。
Spring是通过三级缓存
解决循环依赖问题的,三级缓存用来存放不同类型的Bean对象
第一级缓存里存储完整的Bean对象(完全初始化好的bean),这些Bean是可以被直接使用的
第二级缓存里存储原始的Bean对象(Bean里面的属性还没被赋值或没被依赖注入)
第三级缓存存储Bean工厂的一个对象,用来生成原始Bean对象,放入二级缓存中
三级缓存的核心思想就是把Bean的实例化和Bean里面的依赖注入进行分离,采用一级缓存存储完整的Bean对象,采用二级缓存存储不完整的Bean对象,通过不完整的Bean对象作为突破口,解决循环依赖问题,三级缓存主要是解决代理对象的循环依赖问题
Spring是通过三级缓存解决的循环依赖问题,创建bean的过程分为实例化、属性注入、初始化三步。假设A和B发生了循环依赖,A完成实例化后会放到缓存中,这时需要在A中注入B的bean。对B进行实例化,这时需要在B中注入A的bean,从缓存中获取A实例化后的bean,B的bean创建完成。A从缓存中获取B的bean,完成属性注入和初始化。
具体而言,Spring通过三级缓存解决循环依赖问题的步骤如下:
- Spring在创建Bean对象时,首先从一级缓存中查找是否存在已经创建完成的Bean对象,若存在则直接返回该Bean对象;
- 若一级缓存中不存在该Bean对象,则从二级缓存中查找是否存在该Bean对象的代理对象,若存在则返回代理对象;
- 若二级缓存中也不存在该Bean对象的代理对象,则将正在创建的Bean对象放入三级缓存中,并在创建过程中进行依赖注入。此时,如果其他Bean对象中依赖了正在创建的Bean对象,Spring将直接从三级缓存中获取正在创建的Bean对象,而不是重新创建一个新的Bean对象。
- 当Bean对象创建完成后,Spring将其从三级缓存中移除,并将其加入一级缓存中,以便下次获取该Bean对象时直接从一级缓存中获取。
需要注意的是,三级缓存并不是无限制地缓存Bean对象,而是限定在Bean对象创建过程中使用,Bean对象创建完成后将会从三级缓存中移除。此外,如果Bean对象的依赖关系存在循环依赖,则在创建过程中将会抛出异常,因为无法通过缓存解决循环依赖的问题
②IOC
🌟简述 Spring 的 IOC 机制
IOC(Inversion of Control,控制反转)是 Spring 框架的核心,是一种设计思想。基本思想就是将原本在程序中手动创建对象、管理对象的控制权,交由 Spring 框架来管理。通过反射实现对其他对象的控制,包括初始化、创建、销毁等。
- 控制 :指的是对象创建(实例化、管理)的权力
- 反转 :控制权交给外部环境(Spring 框架、IoC 容器)
Spring IOC 的主要实现方式是通过依赖注入(Dependency Injection,DI)来实现的。依赖注入是指在对象创建的过程中,自动注入该对象所依赖的其他对象,从而构建对象之间的依赖关系。Spring IOC 支持多种依赖注入的方式,如构造函数注入、Setter 方法注入、字段注入等。
IOC的好处
- 降低了类之间的耦合,对象创建和初始化交给Spring容器管理,在需要的时候只需向容器进行申请。
- 资源不由使用资源者管理,而由第三方管理,实现资源的可配置和易管理。
- 通过使用 Spring IOC,应用程序可以更加关注业务逻辑,而不需要过多关注对象的创建和管理,增加了可维护性且降低了开发难度。
🌟IOC的实现原理
Spring IOC 的实现原理可以分为两个步骤:
扫描和解析配置文件或注解信息,将其转换为内部的对象定义和依赖关系;
根据对象定义和依赖关系,使用反射机制动态创建和初始化对象,并将对象注入到需要使用它们的地方。
具体来说,Spring IOC 的实现过程如下:
- 读取配置文件或解析注解信息,将其转换为内部的对象定义和依赖关系。在 Spring 中,可以使用 XML 文件或注解来配置对象和依赖关系。Spring 通过解析配置文件或注解信息,将其转换为内部的对象定义和依赖关系(BeanDefinition)放到容器(BeanFactory)中。对象定义包括对象的类型、属性、构造函数等信息,依赖关系包括对象之间的依赖关系、依赖注入方式等信息。
- 使用反射机制创建对象。在 Spring 中,通过反射机制来创建对象。Spring 会根据对象定义的类型和构造函数信息,使用反射机制来创建对象。
- 初始化对象。在创建对象之后,Spring 会调用对象的初始化方法,如实现了 InitializingBean 接口的 afterPropertiesSet 方法或配置文件中指定的初始化方法。
- 注入依赖关系。在初始化对象之后,Spring 会自动注入对象之间的依赖关系。Spring 支持多种依赖注入方式,如构造函数注入、Setter 方法注入、字段注入等。
- 返回对象。在注入依赖关系之后,Spring 将创建并初始化完成的对象返回给调用方(通过BeanFactory.getBean方法)。
总的来说,Spring IOC 的实现原理是通过反射机制动态创建和初始化对象,并将对象注入到需要使用它们的地方。通过解耦对象之间的依赖关系,使得应用程序更加灵活、可维护、可扩展。
Spring依赖注入的方式有哪些
构造器注入、Setter方法注入、注解注入(@Autowired、@Resource、@Value)、接口注入、字段注入
③AOP
🌟简述 Spring AOP 的原理
AOP 就是在运行时通过动态代理技术对目标方法进行增强,可以在目标方法的调用前后或者调用过程中执行其他额外的逻辑。
AOP能够将那些与业务无关,却被业务模块所共同调用的逻辑(横切关注点:例如事务处理、日志管理、权限控制等)封装起来,减少系统的重复代码,降低模块间的耦合度,并有利于未来的拓展和维护(不通过修改源代码方式,在主干功能里面添加新功能)。
Spring AOP 基于动态代理模式实现,它通过在运行期间动态代理目标对象,将被业务模块所共同调用的逻辑(横切关注点)织入到系统中,从而实现了业务逻辑与横切关注点的分离。
- 1、有接口情况,使用 JDK 动态代理
- 创建接口实现类代理对象,增强类的方法
- 2、没有接口情况,使用 CGLIB 动态代理
- 创建子类的代理对象,增强类的方法
AOP 切面编程设计到的一些专业术语:
术语 | 含义 |
---|---|
目标(Target) | 被通知的对象 |
代理(Proxy) | 向目标对象应用通知之后创建的代理对象 |
连接点(JoinPoint) | 目标对象的所属类中,定义的所有方法均为连接点 |
切入点(Pointcut) | 实际增强的方法(切入点一定是连接点,连接点不一定是切入点) |
通知(Advice) | 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情 |
切面(Aspect) | 将通知应用到切入点的过程就叫切面,切入点(Pointcut)+通知(Advice) |
Weaving(织入) | 将通知应用到目标对象,进而生成代理对象的过程动作 |
Spring AOP 通过配置文件或注解的方式来定义切面、连接点、切入点和通知等信息,并使用代理模式将切面织入到目标对象中。通过 AOP 技术,可以有效地解耦业务逻辑和横切关注点,提高了系统的可维护性和可扩展性。
🌟动态代理了解吗?
Java动态代理是Java中一种重要的代理模式,它允许在运行时动态地生成代理类和对象,无需编写静态代理类。
在Java中,动态代理可以通过Java自带的两种方式实现:基于接口的动态代理和基于类的动态代理。
- 基于接口的动态代理—— JDK 动态代理
基于接口的动态代理是Java官方提供的一种动态代理实现方式。在这种实现方式中,代理类必须实现一个或多个接口,然后在运行时动态创建代理对象。
- 基于类的动态代理——CGLIB 动态代理
基于类的动态代理是通过CGLIB库(字节码生成技术)实现的。在这种实现方式中,代理类不需要实现接口,而是通过继承一个已有的类来实现代理功能。
CGLIB(Code Generation Library)是一个高性能的代码生成库,它可以在运行时动态生成字节码来实现类的增强功能。通过CGLIB库,可以直接在运行时创建目标对象的子类,从而实现基于类的动态代理。
基于类的动态代理相比于基于接口的动态代理,可以代理那些没有实现任何接口的类,更加灵活。但是它的实现原理比较复杂,需要在运行时动态生成字节码,会带来一定的性能开销。
④事务
Spring事务的实现方式和原理
- 编程式事务:在代码中硬编码(不推荐使用) : 通过
TransactionTemplate
或者TransactionManager
手动管理事务,实际应用中很少使用 - 声明式事务:在 XML 配置文件中配置或者直接基于注解(推荐使用) : 实际是通过 AOP 实现(基于
@Transactional
的全注解方式使用最多)
Spring事务的隔离级别?
TransactionDefinition.ISOLATION_DEFAULT
:- 使用后端数据库默认的隔离级别,MySQL 默认采用的
REPEATABLE_READ
隔离级别 Oracle 默认采用的READ_COMMITTED
隔离级别.
- 使用后端数据库默认的隔离级别,MySQL 默认采用的
TransactionDefinition.ISOLATION_READ_UNCOMMITTED
:- 最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED
:- 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ
:- 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE
:- 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
spring事务传播机制
1.TransactionDefinition.PROPAGATION_REQUIRED
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
(@Transactional
注解默认使用)
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。
3.TransactionDefinition.PROPAGATION_NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
。
4.TransactionDefinition.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
spring事务什么时候会失效?
⑤注解
简述 Spring 注解的实现原理
简述 Spring 的初始化流程
Spring 是怎么解析 JSON 数据的?
Spring 框架中都用到了哪些设计模式?
2.SpringMVC
说说自己对于 Spring MVC 了解?
Spring MVC是一个基于Java的实现了MVC设计模式的轻量级Web框架,通过把模型-视图-控制器分离,将web各层进行解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合
🌟Spring MVC 的核心组件有哪些?
DispatcherServlet
:前置控制器,负责接收请求、分发,并给予客户端响应。HandlerMapping
:处理器映射器,负责将请求映射到对应的 Handler 即控制器(Controller)。HandlerAdapter
:处理器适配器,负责调用处理器方法并封装处理结果,将其传递给 DispatcherServlet。Handler
:请求处理器,完成具体的业务逻辑。ViewResolver
:视图解析器,根据Handler
返回的逻辑视图,解析并渲染真正的视图,最终将渲染结果响应给客户端。
🌟Spring MVC 的原理和流程?
- 客户端(浏览器)发送请求,
DispatcherServlet
拦截请求。 DispatcherServlet
根据请求信息调用HandlerMapping
。将请求映射到对应的Handler
(即控制器Controller
),并会将请求涉及到的拦截器和Handler
一起封装。DispatcherServlet
调用HandlerAdapter
执行Handler
。Handler
完成对用户请求的处理后,会返回一个ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的View
。ViewResolver
会根据逻辑View
查找实际的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 把
View
返回给请求者(浏览器)
在这个过程中,DispatcherServlet是整个SpringMVC的核心,它负责协调各个组件的工作。HandlerMapping负责将请求映射到对应的Controller,而HandlerAdapter负责执行Controller。ViewResolver则根据逻辑视图名(如JSP文件名)解析出View对象,最后由View渲染出实际的页面内容。通过这种分工协作的方式,SpringMVC可以实现灵活、高效、可扩展的Web应用程序开发。
3.SpringBoot
- SpringBoot 是如何进行自动配置的?
4.注解
1. 声明bean
@Component
:通用的注解,可标注任意类为Spring
组件。- 如果一个 Bean 不知道属于哪个层,可以使用
@Component
注解标注。
- 如果一个 Bean 不知道属于哪个层,可以使用
@Controller
:对应 Spring MVC 控制层,即Controller层@Service
:对应服务层,即Service层@Repository
:对应持久层,即 Dao 层
2. 自动注入
@Autowired、@Resource、@Qualifier、@Value
- @Autowired按照类型(ByType)注入,当一个接口存在多个实现类的情况下,用@Qualifier制定具体的名称
- @Resource按照名称(ByName)注入,当一个接口存在多个实现类的情况下,用name属性指定具体的名称
3. 配置相关
@Configuration、@Bean、@ComponentScan(扫描Bean)
读取配置信息
AOP相关
@Aspect、@PointCut、@Before、@After、@Around
异步相关
@EnableAync、@Async
前后端传值
@PathVariable
:用于获取路径参数。用于将路径参数数据映射到控制器方法的参数上@RequestParam
:用于获取查询参数。用于将请求参数区数据映射到控制器方法的的参数上
如果请求的 url 是:/user/007/info?userName=Tom
@GetMapping("/user/{userId}/info")
public List<Teacher> getIdAndName(
@PathVariable("userId") Long userId,
@RequestParam(value = "userName", required = false,defaultValue = "anonymous") String userName ) {
...
}
服务获取到的数据就是:userId=123456,userName=Tom
请求相关
@RequestMapping
:映射请求url@RequestParam
:获取请求路径后面的参数值@PathVariable
:用于获取路径参数。@RequestBody
:获取请求体上的参数值@ResponseBody
:接口返回以json的形式@RestController
:@Controller
和@ResponseBody
的合集
异常处理相关
- @ControllerAdvice:定义全局异常处理类
- @ExceptionHandler:声明异常处理方法
参考
Sponsor❤️
您的支持是我不断前进的动力,如果您感觉本文对您有所帮助的话,可以考虑打赏一下本文,用以维持本博客的运营费用,拒绝白嫖,从你我做起!🥰🥰🥰
支付宝 | 微信 |