前言
本篇博客主要介绍Spring AOP在实际开发中的应用,也就是统一功能的处理,这里主要介绍统一的登录校验,统一前缀添加,统一的异常处理以及统一的数据格式返回。这些统一的功能处理在我们的项目中还是非常有用的。
一、统一登录校验
所谓的统一登录校验其实就是拦截器,主要的功能就是判断用户是否登录了,对于未登录的用户进行一些页面的拦截。对于拦截器的实现,我们可以采用Spring AOP中的环绕通知或者前置通知来实现,但是那样的实现还是比较麻烦的。要克服的问题主要是获取HttpSession对象和定义拦截规则的表达式是比较难写的,因为我们只对一个类中的部分方法拦截,不是全部拦截。所以我们这里就采用Spring中提供的拦截器(HandlerInterceptor)来实现。
1.自定义拦截器:实现HandlerInterceptor接口,并实现里面的preHandle(执⾏具体⽅法之前的预处理)方法;
2.将自定义拦截器配置到系统配置中,并设置拦截规则:实现WebMvcConfigurer接口,实现里面的addInterceptors(增加拦截规则的方法)方法。
package com.example.blog_interceptor.common;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 在preHandle方法中,我们返回true,就代表可以继续往后执行后续方法
* 返回false就代表拦截器拦截成功,不再执行后续方法
*/
@Component//加此注解主要是为了该类能在spring启动时加载到容器中
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if(session != null){//拿到session是否为空,为空就是未登录
return true;
}
response.sendRedirect("/user/login");//访问失败就跳转到登录页
return false;
}
}
package com.example.blog_interceptor.config;
import com.example.blog_interceptor.common.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 首先传入我们自定义的拦截器,接着拦截所有接口,再根据需求进行放行部分接口
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")//先拦截所有的接口和方法,再根据需求放行部分接口
.excludePathPatterns("/user/login")//放行登录接口
.excludePathPatterns("/user/reg");//放行注册接口
}
}
接下来我们就在UserController类中来验证一下我们的拦截器是否生效了。UserCOntroller类代码如下:
我们在代码中写到了,当访问除了login和reg方法之外,都会被拦截,然后跳转至Login方法,所以当我们访问/user/center时依旧看到的是login方法的内容。
二、统一的异常处理
在我们一个正常的程序中,总会有运行出现异常的情况,如果我们忘记使用try–catch去处理或者没有声明异常,那么程序级很有可能会崩溃,对于这种情况,我们就需要对我们无法处理到的异常做一手准备,也就是这里的异常的统一处理。
统一异常处理的实现方法:
创建一个统一异常处理类,添加@ControllerAdvice + @ExceptionHandler注解,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知, 也就是执⾏某个⽅法事件,具体实现代码如下:
package com.example.blog_interceptor.common;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice//不加这个注解的话该类就不会随着项目启动而启动了
public class ExceptionAdvice {
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public String nullPointException(){
return "出现了空指针异常";
}
@ExceptionHandler(Exception.class)
@ResponseBody
public String allException(){
return "操作出现异常了";
}
}
对于以上代码我们需要注意的是: 我们需要在@ExceptionHandle()中指定对应异常的类对象,这个方法才会根据相应的类对象来捕获相应的异常,但是我们的异常种类非常多,如果让我们一一列举出来是不现实的。所以我们就可以列出一些常见的异常,然后最后再加上所有异常的父类Exception,这样就可以保证所有异常都能被捕获了。
当没有加统一异常处理功能,出现异常时出现的情况:
加了统一异常处理功能后,出现空指针异常时:
加了统一异常处理功能后,出现其他异常时:
通过运行结果我们就可以看出我们已经可以成功的捕获异常了,这里我们只是简单的将结果显示页面上而已,在项目中我们是需要返回状态码、错误信息、等等给前端的。这里只是简单的演示效果而已。
三、统一数据格式返回
1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。
在实现统一的数据格式返回之前我们一般是先规定数据返回的格式(其实就是一个类),然后每次都返回相同的对象给前端;定义的类如下所示:
package com.example.blog_interceptor.common;
import lombok.Data;
/**
* 定义返回的数据格式的类
* 包括状态码,以及状态码描述信息,返回的数据对象
* 类中还有success和fail方法,当我们能正确处理前端发来的请求时,就调用success方法,
* 当无法正确处理前端发来的请求时,就调用
*/
@Data
public class ResultAjax {
private int code;//状态码
private String msg;//描述信息
private Object data;//返回数据
public static ResultAjax success(Object data){
ResultAjax resultAjax = new ResultAjax();
resultAjax.setCode(200);
resultAjax.setData(data);
return resultAjax;
}
public static ResultAjax fail(int code){
ResultAjax resultAjax = new ResultAjax();
resultAjax.setCode(code);
return resultAjax;
}
public static ResultAjax fail(int code,String msg){
ResultAjax resultAjax = new ResultAjax();
resultAjax.setCode(code);
resultAjax.setMsg(msg);
return resultAjax;
}
public static ResultAjax fail(int code,String msg,Object data){
ResultAjax resultAjax = new ResultAjax();
resultAjax.setCode(code);
resultAjax.setMsg(msg);
resultAjax.setData(data);
return resultAjax;
}
}
统⼀的数据返回格式可以使⽤ @ControllerAdvice 注解的⽅式实现,再实现ResponseBodyAdvice接口,重写的其中的supports()和beforeBodyWrite()方法,具体实现代码如下:
package com.example.blog_interceptor.common;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* supports方法中只有返回true,才会走这里的统一格式返回
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Autowired
ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof ResultAjax){//如果是ResultAjax格式就直接返回
return body;
}
if(body instanceof String){
try {
ResultAjax.success(objectMapper.writeValueAsString(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return ResultAjax.success(body);
}
}
对于以上代码的一些说明:统一的数据格式返回可以说是一个保底策略,当我们不小心返回了非ResultAjax类型的数据时,我们就对数据包装成ResultAjax进行返回,这也就是统一数据格式返回的意义所在。对于统一的数据格式返回,我们的实现思路就是:先判断要返回的对象是不是ResulAjax类型的,如果是,就直接返回就可以,如果不是就将要返回的数据设置为ResultAjax中的data,接着再返回就可以。
为什么要对String类型进行特殊的处理呢?
当我们不对String类型进行特殊处理时,也不进行统一异常处理时,返回String类型出现的结果:
从上面的结果中我们可以看出,服务器报的异常就是类型转换异常,说ResultAjax转换成String类型时出错了。但是这里我们“好像没有做”把ResultAjax转换成String类型这件事,那为什么会报这个错误呢?我们可以继续往下看:
出现上面情况的具体原因如下:
1.首先是方法是需要返回一个String类型的数据;
2.统一数据返回之前:我们将String类型转成了ResultAjax中的Object类型;
3.将ResultAjax转换成application/json字符串返回给前端,在这一步就涉及到了将ResultAjax转换成String这一过程了。
到这里我们就知道了问题是出在了将ResultAjax转换成application/json字符串返回给前端这一步了。那么在这里为什么会转换失败呢?其实是Spring的原因导致的,原本String类型在Java中就是比较特殊的存在,所以Spring在处理HTTP请求和响应时,就有两种消息转换器分别是StringHttpMessageConverter和HttpMessageConverter,前一种是针对String类型的。在这里Spring是会根据原本body的类型进行转换,这里body原本类型是String,所以就会调用StringHttpMessageConverter进行转换,但是由于我们现在的类型是ResultAjax,所以就不能调用StringHttpMessageConverter的方法进行转换了,一旦调用了,程序就会报错。所以这里报错的原因也正是这个。
1.一是我们手动的将ResultAjax数据转成json格式数组进行返回;
2.二是我们在项目中指定不使用StringHttpMessageConverter进行转换。
我们上面的代码中是使用了第一种方式,使用了Spring Boot中内置的jackson,将ResultAjax转换成了json格式数组进行返回。
第二种方式代码如下:
package com.example.blog_interceptor.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyConfig implements WebMvcConfigurer {
/**
* 移除 StringHttpMessageConverter
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
}
}
原文地址:https://blog.csdn.net/m0_64986499/article/details/133745242
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_10231.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!