一. 基本常识
1.全局异常处理器——@ControllerAdvice
@ControllerAdvice
是Spring框架中用于全局异常处理的注解。通过在一个类上添加@ControllerAdvice
注解,可以将其定义为全局异常处理器,用来统一处理应用程序中抛出的异常。
具体来说,@ControllerAdvice
通常与@ExceptionHandler
配合使用。@ExceptionHandler
注解用于捕获特定的异常类型,并进行相应的处理,而@ControllerAdvice
则标识了一个类,指示其中的方法可以处理应用程序中抛出的异常。
例子:
package com.whd.common.exception; import com.whd.common.R; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; /** * @desc: 全局异常类处理器 * @author wangheda * @createTime 2023/10/12 16:53 */ @ControllerAdvice @ResponseBody @Slf4j public class GlobalException { /** * @desc 空指针异常 * @param response * @param e * return com.whd.common.R * @author wangheda * @date 2023/10/18 **/ @ExceptionHandler(value = {java.lang.NullPointerException.class}) public R nullPointerExceptionHandler(HttpServletResponse response, Exception e) { e.printStackTrace(); log.info("打印异常日志[{}]",e); return R.error("空指针异常!",777); } /** * @desc 其他异常 * @param e * @return com.whd.common.R */ @ExceptionHandler(value = Exception.class) public R exceptionHandler(Exception e) { e.printStackTrace(); log.info("打印异常日志[{}]",e); return R.error("其他异常"+e.getMessage(),778); } /** * @desc exceptionHandlerBiz自定义异常类 * @param e * @return com.whd.common.R * @author wangheda * @date 2023/10/15 **/ @ExceptionHandler(value = BizException.class) public R exceptionHandlerBiz(BizException e) { e.printStackTrace(); log.info("打印异常日志[{}]",e); return R.error("BizException"+e.getMsg(),e.getCode()); } /** * @desc 其他异常 * @param e * @return R类型 */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public R exceptionBindException(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); StringBuilder sb = new StringBuilder("校验失败:"); for (FieldError fieldError : bindingResult.getFieldErrors()) { sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", "); } String msg = sb.toString(); e.printStackTrace(); log.info("打印异常日志[{}]",e); return R.error("参数异常"+msg,780); } }
在上面的例子中,GlobalException类被标注为@ControllerAdvice
,表示它是一个全局异常处理器。该类中的方法使用了@ExceptionHandler
注解,用于处理所有类型的异常。当应用程序中抛出异常时,如果没有找到对应的局部异常处理器,将会使用全局异常处理器中的方法来处理异常。
通过使用@ControllerAdvice
注解,可以实现全局的异常处理逻辑,例如记录日志、返回统一格式的错误信息等。这样可以避免在每个Controller中都编写异常处理逻辑,提高了代码的复用性和可维护性。
需要注意的是,全局异常处理器通常用于处理未被捕获的异常,因此在设计时需要仔细考虑异常处理的逻辑,以确保应用程序对各种异常情况有合理的响应。
下面是自定义的异常处理类:
package com.whd.common.exception; import lombok.Data; /** * @desc: 自定义异常 * @author wangheda * @createTime 2023/10/12 17:33 */ @Data public class BizException extends RuntimeException{ private int code=500; private String msg; //对该异常类的构造方法进行补充,不写的化会默认只有一个无参构造 public BizException(String msg) { super(msg); this.msg = msg; } public BizException(SysErrorEnum sysErrorEnum) { this.msg = sysErrorEnum.getMsg(); this.code = sysErrorEnum.getCode(); } public BizException(String msg, int code) { super(msg); this.msg = msg; this.code = code; } public BizException(String msg, Throwable e) { super(msg, e); this.msg = msg; } public BizException(String msg, int code, Throwable e) { super(msg, e); this.msg = msg; this.code = code; } }
这段代码是一个名为 BizException
的自定义异常类,用于在程序中抛出业务异常,并提供了多个构造方法来创建不同类型的异常对象。
2. e.printStackTrace() 是什么
在Java中,e.printStackTrace()
是用来打印异常堆栈信息的常见方式。当程序执行到这行代码时,如果前面有异常对象 e
被捕获或者抛出,那么它会打印出异常的堆栈信息,包括异常出现的位置、调用链等详细信息。
这个方法会将异常的详细信息输出到标准错误流(stderr),通常会在控制台上或者日志文件中看到这些信息。这对于在开发和调试阶段追踪异常非常有帮助,但在生产环境中,通常不建议直接使用 e.printStackTrace()
,因为它会将异常信息打印到控制台,可能会暴露系统的内部结构和敏感信息。
在生产环境中,更常见的做法是将异常信息记录到日志文件中,可以使用日志框架(比如Log4j、Logback等)的相关方法,将异常信息写入日志,以便后续排查问题。例如:
javaCopy Codelogger.error("发生了异常:", e);
这样可以将异常信息以错误级别记录到日志文件中,而不会直接打印到控制台上。同时,日志框架还提供了丰富的配置选项,可以灵活地控制日志的格式、输出位置等,更适合于生产环境下的异常处理和监控。
3.常用日志级别有哪些
日志级别是用来描述日志信息严重程度的概念,不同的级别对应不同的日志信息类型。在软件开发中,合理地设置日志级别可以帮助开发人员更好地理解系统的运行状态,从而进行故障排查和性能优化。
常见的日志级别包括以下几种:
-
TRACE(跟踪): 最详细的日志级别,通常用于记录程序执行过程中的详细信息,如方法的调用、变量的取值等。在开发和调试阶段使用,目的是追踪程序的执行流程。
-
DEBUG(调试): 用于输出调试信息,比较详细,一般用于在开发和调试阶段输出一些调试信息,例如变量的取值、方法的执行情况等。
-
INFO(信息): 用于输出程序正常运行时的重要信息,比如系统启动、数据初始化等。这些信息对于理解系统的运行状态很有帮助,但不会输出太多无关紧要的信息。
-
WARN(警告): 用于输出一些警告信息,表明程序可能出现了潜在的问题,但并不会影响系统的正常运行。比如某个方法的参数不合法、接口的调用超时等情况。
-
ERROR(错误): 用于输出错误信息,表示程序出现了一些严重的问题,但并未导致系统崩溃。比如数据库连接失败、文件读取错误等。
-
FATAL(致命): 最高级别的日志,表示程序出现了严重错误,可能导致系统崩溃或无法继续运行。这类错误通常会导致系统的异常退出或不可恢复的问题。
通常情况下,可以根据实际需求和系统的运行状态来选择合适的日志级别。在开发和调试阶段,可以将日志级别设置为 DEBUG 或 TRACE,以便更好地理解程序的执行流程;在生产环境中,则可以将日志级别设置为 INFO 或更高的级别,避免输出过多无关紧要的信息,影响系统性能和日志文件大小。
4.controller,service,mapper每一层的任务
-
控制器层(Controller):
-
任务:控制器层负责接收客户端的请求,并将请求转发给相应的服务进行处理。它还负责处理请求的参数解析、调用适当的服务方法,并将结果返回给客户端。
-
具体工作:
-
接收HTTP请求,并解析其中的参数。
-
调用服务层的方法,传递参数并获取处理结果。
-
将处理结果封装为HTTP响应并返回给客户端。
-
-
-
服务层(Service):
-
任务:服务层包含应用程序的核心业务逻辑,负责处理具体的业务需求和业务规则。
-
具体工作:
-
实现业务逻辑,包括各种复杂的业务计算、数据处理、事务管理等。
-
协调不同的持久层操作(例如数据库访问操作)来满足业务需求。
-
可以包含一些事务控制,确保业务操作的原子性和一致性。
-
-
-
持久层(Mapper 或 DAO):
-
任务:持久层负责与数据存储进行交互,通常是数据库。它负责执行对数据库的查询、插入、更新和删除操作。
-
具体工作:
-
定义数据访问接口或者映射器(Mapper),通常使用类似于 MyBatis 或 Hibernate 这样的持久化框架。
-
实现对数据存储的具体操作,包括 SQL 查询、ORM 映射等。
-
-
5.枚举中为何没有set方法。可以有,但是为何不建议有?
在一些编程语言中,如Java,枚举类型的字段通常是不可变的。这意味着一旦枚举常量被定义,它们的值就不能被修改。因此,在枚举中通常不会提供公开的 setter 方法来修改枚举常量的值。
这种设计决策有以下几个原因:
-
保持不可变性:枚举的主要目的是表示一组固定的常量,这些常量在程序运行时应保持不变。通过不提供 setter 方法,可以确保枚举常量的值不会被误修改,增加了代码的稳定性和可预测性。
-
限制选项:枚举类型通常用于表示一组有限的选项或状态。通过限制只能在定义时设置枚举常量的值,可以确保这些选项是固定且有限的,并且不会被随意修改。
-
简化代码逻辑:枚举常量的不可变性使得代码更加简洁和易于理解。由于枚举常量的值无法更改,不需要关注状态的变化和复杂的状态转换逻辑,从而减少了潜在的错误和维护成本。
尽管枚举中不建议提供公开的 setter 方法,但仍然可以在枚举内部使用私有的方法来进行修改和操作枚举常量的值。这样可以在确保不破坏不可变性的前提下,实现一些特定的行为或状态转换。
总而言之,不提供公开的 setter 方法是为了保持枚举常量的不可变性、简化代码逻辑以及增强程序的可靠性和稳定性。
6. 什么是断言
在编程中,断言(assertion)是一种用于检查程序中某个条件是否为真的机制。它通常在代码中的特定位置被插入,用于验证程序运行过程中的假设,并在不满足条件时引发异常或错误。断言用于帮助开发者在调试和测试过程中发现潜在的问题和错误。
断言语句通常包含一个布尔表达式,如果该表达式的结果为假(即条件不满足),则断言失败,并且会产生一个断言异常。这可以帮助开发者快速发现程序中的逻辑错误或意外情况,并在开发过程中提供更好的可靠性和调试能力。
7.什么是敏捷开发
敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。在敏捷开发中,软件项目在构建初期被切分成多个子项目,各个子项目的成果都经过测试,具备可视、可集成和可运行使用的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。
8.生产异常怎么解决
在软件开发中,"生产环境中的异常"通常指的是在实际运行或部署的生产系统中发生的错误或异常情况。这些异常可能由于代码缺陷、外部依赖关系、环境问题或其他因素而引起。在生产环境中,异常可能会导致系统功能异常、性能问题甚至系统崩溃
-
收集异常信息:在遇到异常时,首先需要记录下发生异常时的时间、异常类型、异常信息、堆栈跟踪等信息。你可以使用日志框架记录异常信息,并将其保存到日志文件中。
-
分析异常信息:收集异常信息后,需要仔细分析异常信息以理解异常的源头。查看异常信息的堆栈跟踪,找到异常发生的代码位置。把注意力集中在与异常相关的代码和数据上,尽量缩小异常范围。
-
修复异常:确定异常原因后,需要采取相应措施来修复异常。这可能涉及修改代码、配置或数据库等操作。对于生产环境问题,需要谨慎行事,确保修复方案可以准确地解决异常,同时不会引入其他问题。
-
更新代码:在修复异常后,需要将代码部署到生产环境中。确保在部署前进行一次全面的测试,并验证修复是否成功。建议采用自动化部署工具来简化部署过程,并确保代码更新的正确性。
-
监控和预防:在生产环境中,需要定期监控应用程序的运行情况,以及异常发生的频率和类型。同时,还需要采取预防措施来尽可能减少异常的发生。例如,加入额外的数据验证、限流和异常捕获机制等。
9.post get put delete 什么时候用什么类型 如何区分
参考文章:总结get、put、post、delete的区别和用法_云庄clouder的博客-CSDN博客
1.put和post区别 put和post操作都是向服务器端发送数据,但是put是被定义为idempotent(幂等)的方法,而post是非幂等的,即多次发送同一个请求时候,产生的结果是一样的就是幂等性原则,这是为了当网络出现延迟等,服务器(客户端)之间发送请求没有收到回应,再重新发一次,实际上另一端已经接收到了第一次的请求,接着有接收第二次同一请求,如果是交易的业务没有幂等就会造成交易两次却只结算一次交易额。 put请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以PUT用来改资源)。 Post请求:后一个请求不会把第一个请求覆盖掉。(所以Post用来增资源)。
2.get和post差别比较多 1.关于安全性:post的安全性要相对比get的高,因为get的参数都是放在url中的,可以被缓存,截取直接就能获取数据,所以一般登录密码这些信息不会明文放在url中使用get请求发送。而post的数据都是放在RequestBody,可以进行一次加密,相对安全些。使用get请求的,直接将地址复制粘贴就可以原样访问,而post一般不行,我们在浏览器输入一个网址访问网站都是get请求。
2.请求数据上限:get请求的数据会放在url后面,使用?A=B格式(A是名称,B是参数)发送,这个url一般是有长度限制的,http协议没有对url长度进行限制,这个限定主要是浏览器和服务器的限制,一般是1024字节长度。而post可以将数据放在RequesetBody中传送,这里就没有数据量的上限了,get是无请求体的,所以RequestBody只能使用post方式提交。(后面会讲一下RequesetBody)。
3.关于tcp数据包:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。实际上get会产生一个tcp数据包,post会产生两个数据,这就会造成响应时间问题,但并不是所有的浏览器都是post发两次包,firefox不是。
10.空字符串是什么?
空字符是指一个没有任何可见字符的字符序列。在编程中,空字符通常表示为两个单引号 ''
或两个双引号 ""
之间没有任何内容的字符串。
空字符可以用于表示一个没有值的情况,也可以用于表示一个占位符或待填充的空白位置。具体意义取决于上下文和使用方式。
在条件判断中,''
表示空字符,通过判断一个字符串是否为空字符,可以检查该字符串是否没有任何可见字符。这在数据处理、输入验证以及逻辑控制等场景中很常见。
需要注意的是,空字符与空格字符是不同的。空格字符是一个可见的字符,而空字符表示一个没有可见字符的字符序列。
11.什么是json类型
1、简述json
json的全称为:JavaScript Object Notation,是一种轻量级的数据交互格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
2、用途是什么呢?
主要用于前端和后端的数据传输,因为前端语言和后端语言并不一样,json相当于媒人,可以被双方识别,以此做到数据互通。
3、那么JSON是什么呢?
JSON就是一串字符串 只不过元素会使用特定的符号标注。一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互。
12.如何使用注解进行非空校验
导入maven依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency><dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
1.@NotNull
不能为 null,但可以为 empty,一般用在 Integer 类型的基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 对字段数值进行大小的控制
2.@NotEmpty
不能为 null,且长度必须大于 0,一般用在集合类上或者数组上
3.@NotBlank
只能作用在接收的 String 类型上,注意是只能,不能为 null,而且调用 trim() 后,长度必须大于 0即:必须有实际字符
二.关于springboot
1. @autowire 和@resource的区别
1.来源不同
@Autowired 和 @Resource 来自不同的“父类”,其中 @Autowired 是 Spring 定义的注解,而 @Resource 是 Java 定义的注解,它来自于 JSR-250(Java 250 规范提案)。
2.依赖查找顺序不同
@Autowired 先根据类型(byType)查找,如果存在多个(Bean)再根据名称(byName)进行查找;
@Resource 先根据名称(byName)查找,如果(根据名称)查找不到,再根据类型(byType)进行查找。
3.支持的参数不同
二者支持的参数以及参数的个数完全不同,其中 @Autowired 只支持设置一个 required 的参数,而 @Resource 支持 7 个参数
4.依赖注入的支持不同
@Autowired 支持属性注入、构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入,
5.编译器提示不同
当使用 IDEA 专业版在编写依赖注入的代码时,如果注入的是 Mapper 对象,那么使用 @Autowired 编译器会提示报错信息,
2. Spring框架中用于读取配置参数的语句
@Value("${spring.profiles.active:dev}")
private String lockName;
spring:
profiles:
active: dev:demo:studentUpdate
dictionary: dev:demo:dictionary
用于将配置文件中名为 "spring.profiles.active" 的属性值注入到变量 lockName
中。如果配置文件中不存在该属性,则会默认使用 "dev" 作为 lockName
的值。
项目中锁的命名规范为 (环境: 项目缩写: 业务:key)
3. springboot 事物失效场景有哪些 如何避免
在 Spring Boot 中,事务默认是开启的,但是有些情况下可能会导致事务失效。以下是一些常见导致事务失效的情况以及如何避免它们:
-
方法没有被声明为事务:确保需要开启事务的方法使用
@Transactional
注解进行标记,以便将其纳入事务管理。 -
异常未被捕获或处理:当方法中抛出异常时,事务会回滚;但是如果异常被捕获或处理了,那么事务将无法回滚。因此,在业务方法中应该避免捕获异常,或者在 catch 块中重新抛出异常。
-
事务传播属性设置不正确:事务传播属性用于定义事务的边界范围。如果多个方法都参与同一个事务,那么它们的事务传播属性应该一致。常用的传播属性包括
REQUIRED
、REQUIRES_NEW
、NESTED
等,根据业务需求选择合适的传播属性。 -
事务方法内调用非事务方法:如果在事务方法内部调用了另一个方法,而该方法没有被声明为事务,则整个事务会失效。确保事务方法内部调用的方法也要加上
@Transactional
注解。 -
数据库引擎不支持事务:某些数据库引擎可能不支持事务,或者配置不正确,导致事务无法生效。请确保数据库的引擎支持事务,并且正确配置。
-
方法调用不是通过代理方式:Spring 使用 AOP 代理技术来管理事务,在一个类的内部方法调用将无法触发代理,从而导致事务失效。解决办法是通过注入自身的方式,从容器中获取代理对象,然后调用代理对象的方法。
为了避免事务失效,可以采取以下措施:
-
确保所有需要事务管理的方法都标记上
@Transactional
注解。 -
避免在事务方法内部捕获异常,或者在捕获异常后重新抛出异常。
-
根据业务需求设置正确的事务传播属性。
-
确保事务方法内部调用的方法也被声明为事务方法。
-
确认数据库引擎是否支持事务,并进行正确的配置。
-
在需要通过代理方式调用方法时,通过注入自身的方式获取代理对象。
注意事务的生效与否还可能会受到其他因素的影响,如数据源配置、Spring Boot 版本等,确保相关配置正确且一致也是很重要的。