Spring AOP是一个实现面向切面编程思想的框架,可以实现对功能统一且使用地方较多的功能进行统一处理。
AOP(Aspect Oriented Programming):面向切面编程,是一种思想,是对某一类事情的集中处理。Spring AOP是一个框架,是对AOP思想的具体实现。类似于IoC和DI的关系。
主要学习:
1.AOP是如何组成的。
2.Spring AOP的使用。
3.Spring AOP的实现原理。
切面由切点和连接点组成。在程序中就是一个处理某方面具体问题的一个类,类里面包含了很多方法,这些方法就是切点和通知。
用来进行主动拦截的规则(配置)。
切面的工作,程序中被拦截请求触发的具体动作,就是在通知中实现的具体业务代码。
(1)前置通知:在执行目标方法之前执行的方法就叫做前置通知。
(2)后置通知:在执行了目标方法之后执行的方法就叫做后置通知。
(3)异常通知:在执行了目标方法出现异常时,执行的通知。
(4)返回通知:目标方法执行了返回数据(return)时,执行的通知。
(5)环绕通知:在目标方法执行的周期范围内(执行之前、执行中、执行后)都可以执行的方法叫做环绕通知。
应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
可能会触发AOP规则的所有点(所有请求)。
1.添加Spring AOP依赖;
创建Spring Boot项目时是没有Spring AOP框架可以选择的,创建好项目之后,再在pom.xml中添加Spring AOP的依赖即可。
org.springframework.boot spring-boot-starter-aop 2.7.9
2.定义切面(创建切面类);
3.定义切点(配置拦截规则);
4.定义通知的实现。
环绕通知:
//环绕通知@Around("pointcut()")public Object aroundAdvice(ProceedingJoinPoint jointPoint) throws Throwable {System.out.println("进入环绕通知了~");Object obj = null;//执行目标方法obj = jointPoint.proceed();System.out.println("退出环绕通知了~");return obj;}
Spring AOP是构建在动态代理基础上,因此Spring对AOP的支持局限于方法级别的拦截。
Spring的切面包裹了目标对象的代理类实现,代理类处理方法的调用,执行额外的切面逻辑,并调用额外方法。
(1)原生Spring AOP实现统一拦截的难点:
a.定义拦截的规则(表达式)非常难;
b.在切面类中拿到HttpSession比较难。
(2)实现一个普通拦截器
关键步骤:
a.实现HandlerInterceptor接口;
b.重写preHeadler方法,在方法中编写自己的业务代码。
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//用户登录业务逻辑HttpSession session = request.getSession(false);if (session!=null && session.getAttribute("userinfo") !=null) {//说明用户以及登录return true;}//可以调整到登录页面 或者 返回一个 401/403 没有权限码response.sendRedirect("/login.html");return false;}
}
(3)将拦截器添加到配置文件中,并且设置拦截规则
@Configuration
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //拦截所有请求.excludePathPatterns("/user/login") //排除URL地址(不拦截的URL地址).excludePathPatterns("/user/reg").excludePathPatterns("/**/*.html");}
}
给当前项目配置统一的前缀:
(1)在系统的配置文件中设置;
@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("/zhangsan",c->true);}
(2)在application.properties/.yml中配置。
异常统一封装:
(1)创建一个类,并在类上标识@ControllerAdvice;
(2)添加方法@ExceptionHandler来订阅异常。
@ControllerAdvice
public class MyExHandler {//拦截所有的空指针异常,并统一处理@ExceptionHandler(NullPointerException.class)public HashMap nullException(NullPointerException e) {HashMap result = new HashMap<>();result.put("code","-1");result.put("msg","空指针异常:"+e.getMessage());result.put("data",null);return result;}
}
优点:
(1)方便前端更好地接收和解析后端数据接口返回的数据;
(2)降低前端与后端的沟通成本,按照某个格式实现就行;
(3)有利于项目统一数据的维护和修改;
(4)有利于后端技术的统一规范的标准制定。
实现:
(1)创建一个类,并添加@ControllerAdvice;
(2)实现ResponseBodyAdvice接口,并重写supports和beforeBodyWrite(统一对象就是此方法实现的)
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return false;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {HashMap result = new HashMap<>();result.put("code",200);result.put("msg","");result.put("data",body);//String在转换时会出错 需要特殊处理,if (body instanceof String) {try {return objectMapper.writeValueAsString(result);} catch (JsonProcessingException e) {e.printStackTrace();}}return result;}
}