AOP

news2024/11/17 9:27:19

Spring AOP

1.什么是AOP

AOP(Aspect Oriented Programming):面向切面编程,它是⼀种思想,它是对某⼀类事情的集中处理。 AOP 是⼀种思想,而 Spring AOP 是⼀个框架,提供了⼀种对 AOP 思想的实现,它们的关系和IoC 与 DI 类似。
比如
没学AOP之前,有一个项目,这个项目有8个页面,这个项目的每一个页面进去之前都需要判断用户是否登录
验证是否登录就可以归类为一类事情
学了AOP之后,就可以在AOP这个框架设置一下,把这个‘一类事情’通过AOP框架设置一下,就不需要每个页面进去之后都需要验证用户是否登录了,这样就是判断直接在AOP框架实现了,如果没登陆直接就进不了这几个页面,降低了耦合性,因为判断是一件事,进入页面也是一件事,为啥又要进页面又要判断呢,这不是复杂化了嘛
或者还是不懂的话,换一种方式理解
一个火车站有10个站台,假设安装一个小安保设备是10万,大安保设备是50万
不集中处理就是这10个站台每个站台都安装一个安保设备,花了100万,10个设备都需要保安检查,就需要花10队保安的钱,然后这时候有一队保安需要调走,于是就有一个车站空了,这时候就需要从另外9队保安调人过来,这就牵一发动全身了
集中处理就是在火车站进站口花50万安装一个大的安保设备,只需要花一队保安的钱,这时候这队保安需要走,可以再调一队过来,就可以把保安留还是调走这件事集中处理了

2.为什么要用AOP

想象⼀个场景,我们在做后台系统时,除了登录和注册等几个功能不需要做用户登录验证之外,其他几乎所有页面调用的前端控制器( Controller)都需要先验证用户登录的状态,那这个时候我们要怎么处理呢?
我们之前的处理方式是每个 Controller 都要写⼀遍用户登录验证,然而当你的功能越来越多,那么你要写的登录验证也越来越多,而这些方法又是相同的,这么多的方法就会代码修改和维护的成本。那有没有简单的处理方案呢?答案是有的,对于这种功能统⼀,且使用的地方较多的功能,就可以考虑 AOP来统⼀处理了
除了统⼀的用户登录判断之外,AOP 还可以实现:
统⼀日志记录
统⼀方法执行时间统计
统⼀的返回格式设置
统⼀的异常处理
事务的开启和提交等
也就是说使用AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP(Object Oriented Programming,面向对象编程)的补充和完善。

3.AOP组成

3.1切面(Aspect)

定义的是事件,也就是AOP针对哪一方面的(是用来检测登录的还是用来输出日志的)

切面相当于老板:定义公司的方向

3.2切点(Pointcut)

定义具体规则的。
比如用户登录,哪些接口需要进行用户登录,哪些不需要进行,这个就是个规则,需要让AOP按照这个规则进行

切点相当于公司中层:指定具体的方案

3.3通知(Advice)

AOP执行的具体方法
比如获取用户登录信息(session),获取到就是登录,接着往下进行,没获取到返回false

通知相当于公司的底层:具体业务执行者

通知又分为5个:
1.前置通知使用 @Before:通知方法会在目标方法调用之前执行。
2.后置通知使用 @After:通知方法会在目标方法返回或者抛出异常后调用。
3.返回之后通知使用 @AfterReturning:通知方法会在目标方法返回后调用。
4.抛异常后通知使用 @AfterThrowing:通知方法会在目标方法抛出异常后调用。
5.环绕通知使用 @Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

3.4连接点(JoinPoint)

有可能触发切点的点
比如用户登录这个例子,连接点就是每个接口,因为每个接口都可能触发用户验证登录这个条件

4.Spring AOP实现原理

Spring AOP 是构建在动态代理基础上
在这里插入图片描述
这俩的区别:
1.JDK 动态代理基于接口,要求目标对象实现接口;CGLIB 动态代理基于类,可以代理没有实现接口的目标对象。
2.JDK 动态代理使用 java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler 来生成代理对象;CGLIB 动态代理使用 CGLIB 库来生成代理对象。(出身不同,JDK 动态代理来源java,CGLIB来源于第三方)
3.JDK 动态代理生成的代理对象是目标对象的接口实现;CGLIB 动态代理生成的代理对象是目标对象的子类。
4.JDK 动态代理性能相对较高,生成代理对象速度较快;CGLIB 动态代理性能相对较低,生成代理对象速度较慢。JDK7之前CGLIB 性能高于JDK 动态代理,7之后就反过来了
5.CGLIB 动态代理无法代理 final 类和 final 方法;JDK 动态代理可以代理任意类。

5.AOP实战

5.1用户登录权限校验

5.1拦截器

之前的AOP的步骤太繁琐了,并且获取不到session对象,为此Spring提供了拦截器HandlerInterceptor,拦截器的实现分为以下两个步骤
1.自定义拦截器
在这里插入图片描述
UserInterceptor代码

//自定义拦截器
//HandlerInterceptor可以看成用来管理所有拦截器的管理器
//同时用注解将UserInterceptor注入到ioc容器中
@Component
public class UserInterceptor implements HandlerInterceptor
{
    //返回true,表明拦截器验证成功,继续执行后面的方法
    //返回false,表明拦截器验证失败,不会执行后面的方法
    //preHandle可以看成是执行目标方法之前的方法,因为拦截器就是在执行目标方法之前判断是否登录的
    //以下方法在idea自动生成步骤:右键->generate->override method
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        //先获取session对象,没获取到就不创建新的session对象了
        HttpSession session= request.getSession(false);
        if(session!=null&&session.getAttribute(AppVar.SESSION_KEY)!=null)
            return true;
        return false;
    }
}

AppVar代码

//这个方法是定义session的值的
public class AppVar
{
    //Session的值
    public static final String SESSION_KEY="SESSION_KEY";
}

2.将自定义拦截器配置到系统设置中,并自定义规则

在这里插入图片描述

//这个类继承了WebMvcConfigurer之后就是一个系统的配置文件
//且必须加上@Configuration注解
@Configuration
public class AppConfig implements WebMvcConfigurer
{
    @Autowired
    private UserInterceptor userInterceptor;
    //addInterceptors是添加多个拦截器,在一个项目中,拦截器可以有多个
    @Override
    public void addInterceptors(InterceptorRegistry registry)
    {
        //获取到拦截器
        registry.addInterceptor(userInterceptor)
                //设置拦截规则
                .addPathPatterns("/**")//拦截所有请求
                .excludePathPatterns("/user/reg")//不拦截(放行)注册请求
                .excludePathPatterns("/user/login");//不拦截(放行)登录请求
                //如果根据需求还可以放行更多的,就还继续.excludePathPatterns就行了
        ;
    }
}

拦截器已经写好了,下面来一个测试类来测试我们自己写的拦截器
在这里插入图片描述

@RestController
@RequestMapping("/user")
public class UserController
{
    //这个方法没说放行,所以访问的时候会被拦截的
    @RequestMapping("/getuser")
    public String getUser()
    {
        System.out.println("执行了getUser方法");
        return "user";
    }
    //这个没被拦截,可以执行
    @RequestMapping("/reg")
    public String reg()
    {
        System.out.println("执行了reg方法");
        return "reg";
    }
    //这个没被拦截,可以执行
    @RequestMapping("/login")
    public String login()
    {
        System.out.println("执行了login方法");
        return "login";
    }
}

在这里插入图片描述
在这里插入图片描述
怎么排除所有的静态资源,就是比如图片,就不拦截,但是图片就有好几十种格式,难道我们要一个个excludePathPatterns吗?当然不是
我们可以在static下加一个image的目录,所有的图片放这个image里,然后再excludePathPatterns
在这里插入图片描述
在这里插入图片描述
拦截器实现流程
在这里插入图片描述

5.2统一异常处理

比如后端给前端返回的响应出错了,前端的页面很懵逼,如下图
在这里插入图片描述
前端这时候不知道是哪里出现了问题
这时候就要给异常做一个统一处理了,如果是空指针异常,就返回一种让前端看的懂的方式,如果是越界异常,就返回另一种方式
这时候就要统一异常处理了
统⼀异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件
下面代码是简单的处理了空指针异常
在这里插入图片描述
ExceptionAdvice代码

@RestControllerAdvice
public class ExceptionAdvice
{
    @ExceptionHandler(NullPointerException.class)
    public Result doNullPointerException(NullPointerException e)
    {
        Result result=new Result();
        result.setCode(-1);
        result.setMeg("空指针异常"+e.getMessage());
        result.setData(null);
        return result;
    }
}

Result代码

@Data
public class Result
{
    //无论什么时候,后端都返回给前端以下三个信息
    private int code;//状态码
    private String meg;//状态码的描述信息
    private Object data;//返回数据
}

前端返回页面
在这里插入图片描述
但是上述只是空指针异常,而异常有很多,难道要我们一个一个写吗?
当然不是,所有异常的父类都是Expection
于是我们可以这么写

    @ExceptionHandler(NullPointerException.class)
    public Result doException(Exception e)
    {
        Result result=new Result();
        result.setCode(-1);
        result.setMeg("异常"+e.getMessage());
        result.setData(null);
        return result;
    }

5.3统一数据返回格式

@Data
public class Result
{
    //无论什么时候,后端都返回给前端以下三个信息
    private int code;//状态码
    private String meg;//状态码的描述信息
    private Object data;//返回数据

    //返回成功对象
    public static Result success(Object data)
    {
        Result result=new Result();
        result.setCode(200);
        result.setMeg("");
        result.setData(data);
        return  result;
    }
    public static Result fail(int code,String message)
    {
        Result result=new Result();
        result.setCode(code);
        result.setMeg(message);
        result.setData(null);
        return  result;
    }
}
package com.example.demo.config;

import com.example.demo.common.Result;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * 统一返回值的保底实现类
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * true -> 才会调用 beforeBodyWrite 方法,
     * 反之则永远不会调用 beforeBodyWrite 方法
     *
     * @param returnType
     * @param converterType
     * @return
     */
    @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 Result) {
            return body;
        }
        // 对字符串进行判断和处理
        if (body instanceof String) {
            Result resultAjax = Result.success(body);
            try {
                return objectMapper.writeValueAsString(resultAjax);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return Result.success(body);
    }
}
@RestControllerAdvice
public class ExceptionAdvice
{
    @ExceptionHandler(NullPointerException.class)
    public Result doNullPointerException(NullPointerException e)
    {
        Result result=new Result();
        result.setCode(-1);
        result.setMeg("空指针异常"+e.getMessage());
        result.setData(null);
        return result;
    }
    @ExceptionHandler(NullPointerException.class)
    public Result doException(Exception e)
    {
        Result result=new Result();
        result.setCode(-1);
        result.setMeg("异常"+e.getMessage());
        result.setData(null);
        return result;
    }
}

在这里插入图片描述

6. @ControllerAdvice 源码分析(了解)

通过对 @ControllerAdvice 源码的分析我们可以知道上⾯统⼀异常和统⼀数据返回的执行流程,我们先从 @ControllerAdvice 的源码看起,点击 @ControllerAdvice 实现源码如下
在这里插入图片描述
从上述源码可以看出 @ControllerAdvice 派生于 @Component 组件,而所有组件初始化都会调用InitializingBean 接口。所以接下来我们来看 InitializingBean 有哪些实现类?在查询的过程中我们发现了,其中 Spring MVC中的实现子类是RequestMappingHandlerAdapter,它里面有⼀个方法 afterPropertiesSet() 方法,表示所有的参数设置完成之后执行的方法,如下图所示:
在这里插入图片描述
而这个方法中有⼀个 initControllerAdviceCache 方法,查询此方法的源码如下
在这里插入图片描述
我们发现这个方法在执行是会查找使用所有的@ControllerAdvice 类,这些类会被容器中,但发生某个事件时,调用相应的 Advice 方法,比如返回数据前调⽤统⼀数据封装,比如发生异常是调用异常的Advice 方法实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1063320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于C语言的一些尘封记忆的唤醒

文章目录 size_t类型stddef.hstdint.h math.h如何生成静态链接库优化单片机的执行效率 这两天要搞一个动态背光的项目,涉及到单片机。十几年没有接触了。 size_t类型 size_t是C语言中用于表示大小和索引的无符号整数类型。它是一种与平台相关的类型,在不…

计算机专业毕业设计项目推荐10-饮食搭配平台(Go+微信小程序+Mysql)

饮食搭配平台(Go微信小程序Mysql) **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程…

好用的 WAF 工具(SafeLine)

好用的 WAF 工具(SafeLine) SafeLine安装访问 Web应用防火墙(WAF)是一种工作在应用层的防火墙,主要对Web请求/响应进行防护 WAF可以帮助保护Web应用程序免受各种常见攻击,比如SQL注入,跨站脚本漏…

leetcode - 双周赛114

一&#xff0c;2869.收集元素的最小操作次数 // 解法&#xff1a;哈希表 从右往左遍历 class Solution {public int minOperations(List<Integer> nums, int k) {Set<Integer> set new HashSet<>();for(int i1; i<k; i){set.add(i);}for(int inums.size…

记录本地部署Stable-diffusion所依赖的repositories和一些插件

今天按照其他文章的步骤拉取好了https://github.com/AUTOMATIC1111/stable-diffusion-webui后&#xff0c;点击webui-user.bat后发现&#xff0c;repositories和models还得慢慢拉取&#xff0c;好吧&#xff0c;GitHub Desktop&#xff0c;启动&#xff01; BLIP: https://git…

axios的get请求时数组参数没有下标

开发新项目过程中 发现get请求时 数组参数没有下标 这样肯定是不行的 后端接口需要数组[0]: 7 数组[1]:4这样的数据 原因是因为在请求拦截器没有处理需要的参数 解决方法 在请求拦截器 处理一下参数 import axios, { AxiosError, AxiosInstance, AxiosRequestHeaders } fro…

汽车驾驶 - 四梁六柱是什么

汽车的四梁六柱指的是车辆的两个前纵梁&#xff0c;两个后纵梁和ABC柱。虽然不像车辆上的发动机变速箱这些部件出镜率那么高&#xff0c;但这几个部位的重要作用可一点都不含糊。一辆车在碰撞时能够受力起到保护左右的就是四梁六柱&#xff0c;对我们汽车的安全性起到至关重要的…

封装unordered_map和unordered_set

先前用红黑树封装出了map和set&#xff0c;现在就要用哈希来封装unordered_map和unordered_set&#xff08;为了简化名称&#xff0c;后面称u_map和u_set&#xff09;&#xff0c;u_map和u_set在学习map时曾了解过&#xff0c;只知道是无序&#xff0c;我还在想&#xff0c;不能…

架构师选择题--数据库技术

架构师选择题--数据库技术 三级模式-两级映像数据库设计函数依赖公理系统范式 数据库在选择题考查3到5分&#xff08;不超纲&#xff09; 案例分析每年会考察一道题目 三级模式-两级映像 逻辑独立性 物理独立性 数据库设计 了解每个阶段的产出 逻辑结构设计&#xff1a;将E-R图…

使用Scipy优化梯度下降问题

目 录 问题重述 附加问题 步骤实施 1.查看Scipy官网SciPy&#xff0c;找到优化有关的模块&#xff08;Optimize&#xff09; 2.研究多种优化策略&#xff0c;选择最符合代码的方案进行优化 3.minimize函数参数及其返回值 4.代码展示 5.结果展示 6.进一步优化 6.1对…

数字孪生、AR和VR如何改进数据中心设计

数据中心基础设施管理(DCIM)已存在多年&#xff0c;它在许多数据中心被广泛使用&#xff0c;但还没有普遍使用&#xff0c;由于两个因素&#xff0c;这种情况正在改变&#xff1a;数字化的概念正在普及&#xff0c;IT与运营技术(OT)系统(如建筑管理系统(BMS)和电源管理工具)的集…

Leetcode 151. 反转字符串中的单词 JS版两种方法(内置API,双指针)有详细讲解 小白放心食用

&#x1f3b6;Leetcode 151. 反转字符串中的单词 难度&#xff1a;中等 ✨题目描述&#xff1a; 给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 …

使用4090显卡部署 Qwen-14B-Chat-Int4

使用4090显卡部署 Qwen-14B-Chat-Int4 1. Qwen-Agent 概述2. Github 地址3. 创建虚拟环境4. 安装依赖项5. 快速使用6. 启动 web 演示7. 访问 Qwen 1. Qwen-Agent 概述 通义千问-14B&#xff08;Qwen-14B&#xff09; 是阿里云研发的通义千问大模型系列的140亿参数规模的模型。…

cartographer(1)-运行

1.下载数据集 #1.下载数据集&#xff1a; mkdir /home/tang/bagfiles#2.开始二维建图 cd /home/tang/carto_ws/cartographer_detailed_comments_ws/install_isolated/source install_isolated/setup.bash rospack profile #新装的包索引地址存在ros的环境里 roslaunch ca…

【Python】读取显示pgm图像文件

文章目录 零. 前言一. pgm基本概念二. pgm基本信息读取三. pgm图像渲染四. 代码优化 零. 前言 这学期要学多媒体信息隐藏对抗&#xff0c;发现其中的图像数据集文件都是pgm文件形式的。虽然是图像文件&#xff0c;但是却不能直接通过图像查看器来打开&#xff0c;上网一搜&…

【有限域除法】二元多项式除法电路原理及C语言实现

二元多项式除法电路原理 例: g ( x ) = x 4 + x 2 + x + 1 g(x)=x^4 + x^2+x+1

C语言-字符串与输入输出

一、字符串 1、字符串简介 在 C 语言中&#xff0c;字符串实际上是使用空字符 \0 结尾的一维字符数组。因此&#xff0c;\0 是用于标记字符串的结束。 空字符&#xff08;Null character&#xff09;又称结束符&#xff0c;缩写 NUL&#xff0c;是一个数值为 0 的控制字符&…

深入了解“注意力”和“变形金刚” -第1部分

一、说明 这是一篇很长的文章&#xff0c;几乎讨论了人们需要了解的有关注意力机制的所有信息&#xff0c;包括自我注意、查询、键、值、多头注意力、屏蔽多头注意力和转换器&#xff0c;包括有关 BERT 和 GPT 的一些细节。因此&#xff0c;我将本文分为两部分。在本文中&#…

【JavaEE】JUC(Java.util.concurrent)常见类

文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题&#xff0c;java.util.concurrent 是我们多线程编程的一个常用包&#xff0c;那么今天我将为大家分…

消息驱动 —— SpringCloud Stream

Stream 简介 Spring Cloud Stream 是用于构建消息驱动的微服务应用程序的框架&#xff0c;提供了多种中间件的合理配置 Spring Cloud Stream 包含以下核心概念&#xff1a; Destination Binders&#xff1a;目标绑定器&#xff0c;目标指的是 Kafka 或者 RabbitMQ&#xff0…