Spring面试题
- 什么是Spring
- Spring是一个Java企业级应用开发框架,它提供了一系列的组件和工具,可以帮助开发者快死构建高效、可扩展、易维护的企业级应用。Spring的核心特性包括依赖注入(Dependency Injection)、面向切面编程(Aspect-Oriented Programming)、控制反转(Inversion of Control)等。同时,Spring还提供了多种插件和扩展,如Spring MVC用于Web开发、SpringBoot用于快速构建微服务等。Spring的成功在于它的灵活性和可扩展性,可以根据具体需求进行定义配置和集成
- 你的项目中为什么使用Spring框架
- 由于Spring框架具有强大的功能和良好的可扩展性,所以我的项目使用Spring框架来构建企业级应用。具体来说,我们使用Spring框架的依赖注入功能来实现松耦合的组件设计及,提高了应用的可维护性和可测试性。同时,我们还使用Spring的面向切面编程来实现日志记录、性能监控、事务管理等横切关注点,使得代码更加简洁和易于维护。此外,我们还使用SpringBoot来快速构建微服务应用,以提高应用的可扩展性和部署效率。综合来说,Spring框架为我们的项目带来了很多好处,使得我们能够更加高效地开发和维护企业及应用
- Autowired和Resource关键字的区别
- @Autowired和@Resource都是用于进行依赖注入的注解,区别如下
来源不同
:@Autowired
是Spring框架
提供的注解,而@Resource
是JavaEE
提供的注解,需要使用javax.annotation
包自动装配方式不同:
@Autowired
默认按照类型进行自动装配,如果有多个相同类型的Bean,则按照属性名进行匹配。1
2
3
4public class MyController {
private MyService myService;
}- 在上面的示例代码中,Spring 会自动寻找类型为
MyService
的 Bean,并将其注入到myService
属性中。如果有多个MyService
类型的 Bean,Spring 将根据属性名(即 myService)进行匹配。如果我们想按照名称来装配,需要结合@Qualifier
注解一起使用1
2
3
4
5public class MyController {
private MyService service;
} @Resource
按照名称自动装配,如果指定了name属性,则按照名称匹配,如果没有指定,则默认按照属性名匹配1
2
3
4public class MyController {
private MyService service;
}- 在上面的示例代码中,Spring 会自动寻找名称为
myService
的 Bean,并将其注入到service
属性中。如果没有指定name
属性,Spring 将根据属性名(即 service)进行匹配。
支持的注入类型不同
:@Autowired
支持的注入类型包括Bean、集合类型
- @Autowired和@Resource都是用于进行依赖注入的注解,区别如下
- 依赖注入的方式有几种,各是什么
- 依赖注入(Dependence Injection)是一种设计模式,它的主要思想是通过构造器、Setter方法或接口来向对象传递依赖关系,而不是让对象自己创建或查找依赖对象。在Spring框架中,依赖注入是实现松耦合、可测试和易维护的重要手段,通常有以下几种方式
构造器注入(Constructor Injection):
通过对象的构造器来注入依赖对象,通常在对象创建时就完成了依赖注入1
2
3
4
5
6
7
8
9
10
11// 使用构造器注入方式,将UserRepository类型的依赖项注入到UserService中
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// ...
}1
2
3
4
5<bean id="userRepository" class="com.example.UserRepositoryImpl" />
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository" />
</bean>Setter方法注入(Setter Injection):
通过对象的Setter方法来注入依赖对象,通常在对象创建后进行依赖注入1
2
3
4
5
6
7
8
9
10public class UserService {
private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// ...
}1
2
3
4
5<bean id="userRepository" class="com.example.UserRepositoryImpl" />
<bean id="userService" class="com.example.UserService">
<property name="userRepository" ref="userRepository" />
</bean>接口注入(Interface Injection):
依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。侵入性太强,不建议使用
- 关于构造器注入和Setter注入的详细叙述可以参考我这篇文章的第5小节
- 依赖注入(Dependence Injection)是一种设计模式,它的主要思想是通过构造器、Setter方法或接口来向对象传递依赖关系,而不是让对象自己创建或查找依赖对象。在Spring框架中,依赖注入是实现松耦合、可测试和易维护的重要手段,通常有以下几种方式
- 讲一下什么是Spirng
- Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发。Spring的核心思想是通过依赖注入和面向切面编程来实现松耦合、可测试和易维护的应用程序
- Spring框架提供了许多核心功能,包括
控制反转(Inversion of Control, IoC):
讲对象的创建和依赖关系的管理交给框架来完成,使得应用程序更加松耦合依赖注入(Dependency Injection, DI):
将对象之间的依赖关系通过注入的方式来实现,提高了应用程序的可测试性和可维护性面向切面编程(Aspect-Oriented Programming, AOP):
通过切面来解决横切关注点的问题,如事务控制、安全控制、日志记录等数据访问层(Data Access):
提供了一套方便的API来访问数据库和其他数据存储系统,包括JDBC、ORM、NoSQL等Web开发(Web Development):
提供了一套强大的Web开发框架,包括Spring MVC、Spring WebFlux等消息队列(Messaging):
提供了一套消息传递机制,支持JMC、RabbitMQ等
- 除此之外,Spring还提供了许多其他功能,如集成测试、批处理、缓存等。
- 说说你对SpringMVC的理解
SpringMVC
是Spring框架的一个模块,它提供了一种基于MVC(Model-View-Controller)
架构的Web应用程序开发模型,通过控制器(Controller)
来管理用户请求,通过模型(Model)来处理业务逻辑,通过视图(View)来呈现响应结果,从而实现了Web应用程序的分层架构和模块化开发- 在
SpringMVC
中,用户请求首先由前端控制器(DispatcherServlet)
接收,并将其分派给相应的处理器(Handler)
,处理器根据请求的URL
和请求方法
来选择具体的处理方法,并返回相应的数据模型和视图名称,前端控制器根据模型数据和视图名称来生成响应结果,并将其返回给用户 SpringMVC
提供了许多特性,如请求映射
、参数绑定
、数据验证
、异常处理
、国际化
、文件上传
等,同时也支持多种视图技术
,如JSP
、Thymeleaf
、Freemarker
等。SpringMVC
还支持RESTful
风格的Web服务
开发,并提供了一些Web相关的功能,如SpringWebSockets
、SpringWebFlux
等
- SpringMVC常用的注解有哪些
- SpringMVC中的注解是用来标识
处理器(Handler)
和请求映射(RequestMapping)
之间的关系的。下面是一些常用的注解@Controller:
标记一个类为处理器(Handler)@RequestMapping:
标记处理器的方法和请求之间的映射关系,可以指定请求的URL、请求方法、请求参数等@RequestParam:
用于将请求参数绑定方法参数上@PathVariable:
用于将URL路径变量绑定到方法参数上@ResponseBody:
用于将方法的返回值转换为HTTP响应的正文内容@RequestBody:
用于将HTTP请求的正文内容绑定到方法参数上。
- SpringMVC中的注解是用来标识
- 谈谈你对Spring的AOP的理解
AOP(Aspect-Oriented Programming, 面向切面编程)
是Spring框架的一个核心特性之一,它通过在程序运行期间动态地将额外的行为(例如日志、安全检查等)插入到代码中,以实现更加灵活和可维护的程序设计- 在Spring框架中,AOP主要通过以下几个概念来实现
切点(Pointcut)
;指定在哪些程序执行点上插入额外的行为,通常使用表达式来定义切点通知(Advice)
:插入到切点上的额外行为,通常包括前置通知、后置通知、异常通知、环绕通知等切面(Aspect)
:由切点和通知组成第一个横切逻辑单元,可以看做是一种特殊的类织入(Weaving)
:将切面与目标对象合并,生成一个新的代理对象,并在运行期间插入额外行为
- 在Spring框架中,AOP可以应用于各种场景,如事务管理、缓存、日志、权限控制等,可以大大简化程序设计和代码维护工作,同时Spring框架还提供了各种AOP相关的注解和工具类,使得AOP的使用变得更加简单和方便
Spring AOP
和AspectJ AOP
有什么区别SpringAOP
和AspectJ AOP
都是AOP实现的凡事,它们都可以在运行时动态地为程序添加额外的行为,但是他们之间有如下几个区别实现方式不同
:SpringAOP
是基于JDK动态代理和CGLIB的代理机制实现的,而AspectJ AOP
则是基于字节码操纵实现的支持的切点表达式不同
:AspectJ AOP
支持更加强大和灵活的切点表达式,例如正则表达式、类型模式匹配、注解匹配等,而SpringAOP
仅支持基于方法名、参数列表和异常类型的切点表达式适用范围不同
:SpringAOP
主要适用于Spring框架中的IOC容器和SpringMVC中的Web应用程序,而AspectJ AOP
则是一个独立的AOP框架,可以用于任何Java应用程序集成方式不同
:Spring AOP
可以与Spring框架的其他特性(如IOC、事务管理等)无缝集成,而AspectJ AOP
需要通过AspectJ编译器来实现
- 在Spring AOP中,关注点和横切关注的区别是什么
- 关注点(Concern)是指在软件系统中,需要关注和处理的某一方面或功能模块,例如日志、事务、安全等。在AOP中,关注点被表示为切点(Pointcut),用于指定在哪些代码位置需要执行额外的行为
- 横切关注(Cross-cutting Concern)是指在软件系统中,不同的关注点可能相互交叉或重叠,例如一个方法既需要进行日志记录,又需要进行事务管理。在AOP中,横切关注通常被表示为切面(Aspect),由切点和通知组成,用于将多个关注点的行为织入到代码中
- 什么是通知呢?有哪些类型
- 在SpringAOP中,通知(Advice)是指在横切关注中执行的额外代码块,它可以在方法调用前后、异常抛出时或者方法执行成功后执行。通知是实现AOP的核心机制之一,用于将横切关注的行为织入到目标对象的方法中
- SpringAOP提供了以下五种类型的通知
前置通知(Before Advice):
在目标方法执行前执行的通知后置通知(After Advice):
在目标方法执行后执行的通知,无论方法是正常返回还是抛出异常,都会执行返回通知(After Returning Advice):
在目标方法执行后执行的通知,只有在方法正常返回孩时才会执行异常通知(After Throwing Advice):
在目标方法抛出异常后执行的通知环绕通知(Around Advice):
在目标方法执行前后都可以执行的通知,可以控制目标方法的执行过程,甚至可以完全取代目标方法的执行
- 这些通知可以在切面中指定对应的切点,实现对目标对象方法的相应拦截和处理
- 说说你对Spring的IoC是怎么理解的
- Spring的IOC(Inversion of Control)是指将对对象的创建、组装、管理的责任交给容器,由容器负责对象之间的依赖关系。在IOC模式中,对象不再自己管理其他对象的依赖,而是将依赖关系反转给容器进行管理
- 在Spring中,IOC是通过依赖注入(Dependency Injection, DI)实现的,依赖注入是指容器通过自动或者手动的方式,将对象所依赖的其他对象或者资源注入到对象中,从而达到对象之间的松耦合。
- 通过使用IOC容器,可以实现对象之间的解耦,增加代码的灵活性和可维护性。同时IOC容器也提供了很多功能,例如对象的生命周期管理、AOP、事务管理等,可以大大减少开发者的工作量,提高开发效率
- 总的来说,Spring的IOC是通过依赖注入实现的,将对象之间的依赖关系交给容器进行管理,从而实现对象之间的松耦合和增强系统的可维护性和可扩展性
- 解释一下Spring的Bean的生命周期
- Spring的Bean生命周期包括以下阶段
实例化(Instantiation):
在这个阶段,Spring容器会通过Bean定义中指定的构造函数或者工厂方法来创建Bean的实例属性赋值(Populate Properties):
在这个阶段,Spring容器会将配置文件中指定的Bean属性或者引用注入到Bean实例中初始化(Initalization):
在这个阶段,Spring容器会调用Bean的初始化方法(如果有),并完成Bean的一些初始化操作使用(In Use):
在这个阶段,Bean可以被应用程序使用销毁(Destruction):
在这个阶段,Spring容器会调用Bean的销毁方法(如果有),并完成Bean的销毁操作w
- Spring的Bean生命周期包括以下阶段
- 解释Spring支持的几种bean的作用域
- Spring支持一下物种Bean作用域
Singleton(单例):
在整个应用程序中,只存在一个Bean实例。默认情况下,Spring中的Bean都是单例的Prototype(原型):
每次通过容器获取Bean时,都会创建一个新的Bean实例Request:
在一次HTTP请求中,Bean的实例是唯一的Session:
在一个HTTP Session中,Bean的实例是唯一的Global Session:
在一个全局的HTTP Session中,Bean的实例是唯一的
- Spring支持一下物种Bean作用域
- Spring基于XML注入的Bean有几种方式
- Setter注入
- 构造器注入
- 静态工厂注入
- 实例工厂
- 具体实现方式可以参考我的另一篇文章
- Spring框架中都用到了哪些设计模式
单例模式:
S`pring中的Bean默认是单例的,通过IOC容器对Bean进行管理,保证整个应用程序中只存在一个Bean实例工厂模式:
S`prig中的IOC容器负责创建和管理Bean实例,可以将IOC容器看做是一个工厂,通过工厂方法来创建Bean实例代理模式:
S`pring中的AOP基于代理实现,可以通过JDK动态代理或CGLIB动态代理来实现AOP功能观察者模式:
Spring中的事件驱动机制是基于观察者模式实现,Bean可以发布事件,其他Bean可以通过监听器来响应事件模板方法模式:
Spring中的JdbcTemplate和Hibernate Template等模板类,使用了模板方法模式,将重复性的操作封装到父类中,自雷只需要实现特定方法即可
- 说说Spring中ApplicationContext和BeanFactory的区别
- Spring中的ApplicationContext和BeanFactory都是IOC容器,负责管理和创建Bean实例,但他们之间有一些区别
- ApplicationContext是BeanFactory的子接口,它提供了更多的功能,例如国际化、事件驱动等
- ApplicationContext会在启动时预先实例化所有的单例Bean,而BeanFactory则是按需创建Bean
- ApplicationContext支持Bean的自动装配、AOP等功能,而BeanFactory需要手动配置
- ApplicationContext支持多种资源加载方式,例如XML、注解、属性文件等,而BeanFactory只支持XML格式的配置文件
- ApplicationContext提供了更多的扩展点,例如BeanPostProcessor、ApplicationListener等,可以对Bean的生命周期和时间进行监听和处理
- 如果需要使用Spring的更高级特性,例如AOP、国际化、事件驱动等,可以选择使用ApplicationContext;如果只需要简单地创建和管理Bean实例,可以选择使用BeanFactory
- Spring中的ApplicationContext和BeanFactory都是IOC容器,负责管理和创建Bean实例,但他们之间有一些区别
- Spring框架中的单例Bean是线程安全的吗
- 在Spring框架中,默认情况下,所有的Bean都是单例的,也就是说在整个应用中只有一个bean实例。对于单例Bean是否线程安全,需要根据具体的实现来判断
- 如果单例Bean是无状态的,即没有实例变量,或者所有的实例变量都是线程安全的(例如final修饰的常量),那么单例Bean就是线程安全的
- 如果单例Bean是有状态的,即包含实例变量,并且这些变量是非线程安全的,那么单例bean就不是线程安全的。在多线程并发访问单例Bean时,可能会出现数据竞争和并发安全问题
- 为了解决这个问题,可以考虑将单例Bean的实例变量使用ThreadLocal进行封装,保证每个线程访问时都拥有自己的实例变量。此外,也可以考虑将单例Bean的作用域改为原型(Prototype),每次请求都会创建一个新的Bean实例,避免并发访问问题
- 在Spring框架中,默认情况下,所有的Bean都是单例的,也就是说在整个应用中只有一个bean实例。对于单例Bean是否线程安全,需要根据具体的实现来判断
- Spring是怎样解决循环依赖的
- 整个流程大致如下:
- 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来;
- 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;
- 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;
- 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。
- 整个流程大致如下:
- 说说事务的隔离级别
- 事务隔离级别是指在并发情况下,不同事务之间的隔离程度。在数据库中一般有4种隔离级别
读未提交(Read Uncommitted):
事务中的修改即使没有提交,也能被其他事务看到,存在脏读、不可重复读和幻读的问题读已提交(Read Committed):
事务提交后才能被其他事务看到,解决了脏读的问题,但不可重复读和幻读问题仍然存在可重复读(Repeatable Read):
保证同一事务中多次读取同一数据的结果是一致的,解决了不可重复读的问题,但幻读问题仍然存在串行化(Serializable0):
事务串行执行,可以完全解决并发问题,但是效率最低
- 在Spring中,可以使用@Transactional注解来声明一个事务,通过设置isolation属性来指定事务的隔离级别,默认为Isolation.DEFAULT,表示使用数据库默认隔离级别(通常为Read Committed)。可以通过Isolation枚举类来设置隔离级别
1
2
3
4
public void doSomething() {
// 业务代码
}
- 事务隔离级别是指在并发情况下,不同事务之间的隔离程度。在数据库中一般有4种隔离级别
- 说说事务的传播级别
- 事务的传播级别是指当在一个事务范围内调用另一个带有事务的方法时,当前事务如何与调用方法的事务进行交互的一种方式。Spring中定义了7种事务传播级别
REQUIRED:
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务,这是默认传播级别SUPPORTS:
支持当前事务,如果当前存在事务,则加入改事务;如果当前没有事务,则以非事务的方式继续执行MANDATORY:
强制要求存在事务,如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则挂起当前事务NOT_WUPPORTED:
以非事务方式执行操作,如果当前存在事务,则挂起当前事务NEVER:
以非事务方式执行操作,如果当前存在事务,则抛出异常NESTEDD:
如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED传播级别类似的操作
- 事务的传播级别是指当在一个事务范围内调用另一个带有事务的方法时,当前事务如何与调用方法的事务进行交互的一种方式。Spring中定义了7种事务传播级别
- Spring事务实现方式
- Spring中事务的实现方式主要有两种:编程式事务和声明式事务
编程式事务
:是指通过编写代码来控制事务的提交和回滚。在编程式事务中,开发人员需要手动获取事务管理器,并在需要的地方进行事务的开启、提交和回滚等操作。编程式事务虽然灵活,但是代码侵入性较高,容易造成代码臃肿和难以维护,因此Spring推荐使用声明式事务来实现事务管理声明式事务
:是指通过配置来控制事务的提交和回滚。在声明式事务中,开发人员只需使用注解或XML配置文件来声明事务的属性和行为即可,具体的事务管理操作则由Spring框架自动完成
- 声明式事务的优点是代码简洁,易于维护,而且可以灵活切换事务管理方式。Spring中主要通过AOP技术实现声明式事务,其底层实现依赖于DataSourceTransactionManager和TransactionInterceptor等组件
- Spring中事务的实现方式主要有两种:编程式事务和声明式事务
- Spring框架管理的事务有哪些优点
简化代码:
使用Spring框架观念里事务可以大大简化代码量,使代码更易于维护降低耦合性:
使用Spring框架管理事务可以将业务逻辑与事务处理分离,降低了耦合性避免重复代码:
使用Spring框架管理事务可以避免在每个业务方法中重复编写事务管理代码支持声明式事务:
Spring框架支持声明式事务,可以通过注解或XML配置来定义事务的属性和行为支持多种事务管理器:
Spring框架支持多种事务管理器,包括JDBC事务、Hibernate事务、JPA事务、JTA事务等支持事务的回滚和提交:
Spring框架可以自动管理事务的提交和回滚,从而保证事务的一致性和完整性提高事务的性能:
Spring框架使用缓存和批处理等技术来提高事务的性能和效率
- 事务注解的本质是什么
- 事务注解的本质是通过Spring AOP机制,在方法执行前后动态创建代理对象,将方法包装在一个事务中。事务注解在执行方法前开启一个事务,如果方法执行成功,则提交事务;如果方法执行过程中发生异常,则回滚事务。通过事务注解,我们可以在方法上标记事务的属性,方便灵活地控制事务的执行行为。同时,由于Spring框架在底层处理事务,因此可以避免一些常见的事务处理问题,比如并发访问、死锁等问题。事务注解的使用简单,可以提高开发效率,降低代码复杂度。
评论