牛客论坛01-Spring常见面试题

Spring中的IoC和AOP是什么?它们的作用是什么?

IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。所以说,控制反转是一种思想,目的是为了降低了代码耦合度,提升了系统的灵活性和可维护性

没有IoC容器的情况下,比如类A通过new关键字直接实例化类B,对象创建和依赖关系由开发者手动控制,代码耦合度高,难以维护和扩展。而在有IoC容器的情况下,IoC容器负责实例化类A和类B,并管理它们的依赖关系,主函数只需从容器中获取所需对象,无需手动创建。

所谓控制,就是指的是对象的创建(实例化,管理什么的),反转指的是将控制权交给外部框架(spring框架,IoC容器)。IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

其实在spring中IoC就是一个实现IoC的载体,是一个Map,其中存放着各种对象,早期是通过XML来配置的Bean,后面SpringBoot注解配置更好,只需加上 @Autowired@Resource,Spring 就会自动注入依赖对象慢慢流行起来。


AOP是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK 动态代理,去创建代理对象,而对于没有实现接口的对象,就无法进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

Spring MVC

其流程大概是以下几个步骤:

img

流程说明(重要):

  1. 客户端(浏览器)发送请求, DispatcherServlet拦截请求。(dis po cher servlet)
  2. DispatcherServlet 根据请求信息调用 HandlerMappingHandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter适配器执行 Handler
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServletModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View
  5. ViewResolver 会根据逻辑 View 查找实际的 View
  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  7. View 返回给请求者(浏览器)

上述流程是传统开发模式(JSP,Thymeleaf 【time leaf】等)的工作原理。然而现在主流的开发方式是前后端分离,这种情况下 Spring MVC 的 View 概念发生了一些变化。由于 View 通常由前端框架(Vue, React 等)来处理,后端不再负责渲染页面,而是只负责提供数据,因此:

  • 前后端分离时,后端通常不再返回具体的视图,而是返回纯数据(通常是 JSON 格式),由前端负责渲染和展示。
  • View 的部分在前后端分离的场景下往往不需要设置,Spring MVC 的控制器方法只需要返回数据,不再返回 ModelAndView,而是直接返回数据,Spring 会自动将其转换为 JSON 格式。相应的,ViewResolver 也将不再被使用。

怎么做到呢?

  • 使用 @RestController 注解代替传统的 @Controller 注解,这样所有方法默认会返回 JSON 格式的数据,而不是试图解析视图。
  • 如果你使用的是 @Controller,可以结合 @ResponseBody 注解来返回 JSON。

“在传统 Spring MVC 中,DispatcherServlet 拦截请求后,通过 HandlerMapping 查找 Controller,执行处理并返回 ModelAndView,最后由 ViewResolver 解析视图并渲染返回。而在前后端分离模式下,后端通过 @RestController@ResponseBody 直接返回 JSON 数据,由前端负责渲染,ViewResolver 和视图渲染流程不再需要。这种模式更符合现代开发需求,提升了开发效率和系统灵活性。”

Spring, Spring MVC, SpringBoot直接有什么关系?

Spring 是一个开源的 Java 企业级开发框架,主要提供 IoC(控制反转)和 AOP(面向切面编程)功能,简化 Java 开发。Spring MVC 是 Spring 框架中的一个子模块,专门用于构建 Web 应用,它基于 MVC(Model-View-Controller)设计模式,提供了强大的请求处理、数据绑定、视图解析等功能,适用于开发传统的 Java Web 应用。而 Spring Boot 是在 Spring 基础上发展而来的,它简化了 Spring 的配置,提供了一套开箱即用的默认配置,并且内置了 Tomcat 等 Web 服务器,让开发者可以快速构建独立运行的 Spring 应用,而无需手动配置 XML 或复杂的 Bean 管理。简单来说,Spring 是一个大框架,Spring MVC 负责 Web 层,而 Spring Boot 进一步简化了 Spring 的使用,提升了开发效率。

Spring框架中用了哪些设计模式?

工厂设计模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。

代理设计模式 : Spring AOP 功能的实现。

单例设计模式 : Spring 中的 Bean 默认都是单例的。

模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。

包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

什么是spring的bean?

Bean 代指的就是那些被 IoC 容器所管理的对象。

我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。

Bean的注解有哪些?

@Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。

@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。

@Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。

@Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

spring的事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

Spring 事务传播机制有哪些

propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下

image.png

@Transactional的其他属性有哪些?

timeout 属性

timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

readOnly 属性

readOnly :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
isolation 属性

isolation :事务的隔离级别,默认值为Isolation.DEFAULT

Isolation.DEFAULT:使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED:读未提交
Isolation.READ_COMMITTED:读已提交
Isolation.REPEATABLE_READ:可重复读
Isolation.SERIALIZABLE:串行化rollbackFor 属性

rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

noRollbackFor属性

noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

MyBatis中的#{}和${}的区别是什么?如何防止SQL注入?

在 MyBatis 中,#{}${} 是两种不同的参数占位符,它们的主要区别在于处理方式和安全性。#{} 是预编译占位符,MyBatis 会将其替换为 ?,并通过 PreparedStatement 设置参数值,这种方式会对参数值进行转义,从而有效防止 SQL 注入,适合用于传递参数值,例如 WHERE id = #{id}。而 ${} 是字符串替换,MyBatis 会直接将值拼接到 SQL 语句中,这种方式不会对参数值进行转义,因此存在 SQL 注入风险,通常用于动态拼接表名、列名等场景,例如 ORDER BY ${columnName}

为了防止 SQL 注入,推荐优先使用 #{},因为它能够自动处理参数值的转义,确保安全性。如果必须使用 ${},应确保拼接的值是可信的,例如动态表名或列名,而不是用户输入的直接拼接。此外,还可以通过以下方式进一步增强安全性:严格校验用户输入,确保其符合预期格式;使用 MyBatis 的动态 SQL 标签(如 <if><choose>)生成安全的 SQL 语句,避免手动拼接 SQL;遵循最小权限原则,限制数据库用户的权限,避免使用高权限账号执行 SQL 操作。通过这些方法,可以有效防止 SQL 注入,确保应用的安全性。

@Transactional失效场景与解决办法

链接

1、@Transactional 应用在非 public 修饰的方法上

2、@Transactional 注解属性 propagation 设置错误

3、@Transactional 注解属性 rollbackFor 设置错误

4、同一个类中方法调用,导致@Transactional失效

6、数据库引擎不支持事务

同一Service内方法A(无@Transactional)调用方法B(有@Transactional)是否生效?

好的,面试官!我来具体回答这个问题。

在同一 Service 内,如果方法 A(没有 @Transactional)调用方法 B(有 @Transactional),那么方法 B 的事务不会生效。原因是 Spring 的事务管理是基于代理机制实现的,当方法 A 直接调用方法 B 时,实际上是目标对象内部的方法调用,而不是通过代理对象调用,因此事务拦截器不会起作用。

为什么不会生效?

• Spring 的事务是通过 AOP 动态代理实现的,代理对象会在方法调用前后添加事务管理的逻辑。
• 当方法 A 调用方法 B 时,是直接调用目标对象的方法,而不是通过代理对象调用,所以事务拦截器不会生效。

如何解决?

  1. 将方法 A 和方法 B 拆分到不同的类中:通过代理对象调用方法 B,确保事务生效。
  2. 使用 AopContext.currentProxy() 获取当前代理对象:在方法 A 中通过代理对象调用方法 B。
    1
    ((YourService) AopContext.currentProxy()).methodB();
  3. 在方法 A 上添加 @Transactional 注解:让整个调用链处于事务管理之下。

总结:

同一 Service 内方法 A 调用方法 B 时,方法 B 的事务不会生效,因为直接调用绕过了代理机制。需要通过拆分类、使用代理对象或在方法 A 上加事务注解来解决。

以上就是我的回答,谢谢!

HTTP

超文本传输协议,用于传输HTML等内容的应用层协议,规定了浏览器和服务器之间如何通信的,以及通信时的数据格式。

好的,面试官!我来回答这个问题。


1. HTTP 报文结构及内容类型标识?

HTTP 报文分为请求报文响应报文,结构如下:

请求报文

  1. 请求行:包含请求方法(如 GETPOST)、请求 URL 和 HTTP 版本。
    1
    GET /index.html HTTP/1.1
  2. 请求头:包含客户端信息、请求内容类型等。
    1
    2
    Host: www.example.com
    Content-Type: application/json
  3. 请求体:可选,用于传递数据(如 POST 请求中的表单或 JSON 数据)。

响应报文

  1. 状态行:包含 HTTP 版本、状态码和状态描述。
    1
    HTTP/1.1 200 OK
  2. 响应头:包含服务器信息、响应内容类型等。
    1
    2
    Content-Type: text/html
    Content-Length: 1234
  3. 响应体:可选,包含返回的数据(如 HTML 页面或 JSON 数据)。

内容类型标识

• 通过 Content-Type 头字段标识报文体的格式,例如:
text/html:HTML 文档
application/json:JSON 数据
application/x-www-form-urlencoded:表单数据
multipart/form-data:文件上传


2. 如何判断请求体格式?

判断请求体格式主要通过 Content-Type 头字段:

  1. 检查 Content-Type 头字段:例如:
    application/json:请求体是 JSON 格式。
    application/x-www-form-urlencoded:请求体是键值对形式的表单数据。
    multipart/form-data:请求体包含文件上传。
  2. 解析请求体:根据 Content-Type 的值,使用相应的解析方式:
    • JSON 格式:使用 JSON 解析库(如 JacksonGson)解析。
    • 表单数据:通过框架(如 Spring MVC)自动解析为 Map 或对象。
    • 文件上传:通过 MultipartFile 处理。

示例:

• 如果 Content-Typeapplication/json,请求体可能是:

1
{"name": "John", "age": 30}

• 如果 Content-Typeapplication/x-www-form-urlencoded,请求体可能是:
1
name=John&age=30