牛客论坛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 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter适配器执行 Handler 。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。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 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。

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

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

模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 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. 请求行:包含请求方法(如 GET、POST)、请求 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 解析库(如 Jackson、Gson)解析。
    • 表单数据:通过框架(如 Spring MVC)自动解析为 Map 或对象。
    • 文件上传:通过 MultipartFile 处理。

示例:

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

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

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