Spring Aop以及SpringBoot统一功能的处理

news2024/11/15 7:27:30

一.SpringAop

 1.SpringAop是一种思想,指的是对使用比较多的功能进行统一处理,比如我们在写博客系统项目,当我们在登录博客列表页和博客详情页以及博客编辑页的时候的时候,都需要写代码进行登录验证,这时候代码就比较繁琐,即使我们将验证是否登录的方法实现好了,那么也需要调用,这时候我们使用AOP的思想,单独用一个地方来进行用户登录验证,每次跳转到博客列表页或者博客详情页或者博客编辑页的时候先去判断是否登录,如果没有登录直接返回,这时候就不需要在当前代码中单独再写方法来判断用户是否登陆了。

2.除了统一的AOP判断之外,AOP还可以实现

(1)统一的日志思想

 (2)统一方法执行时间统计

(3)统一的返回格式设置

(4)统一的异常处理

(5)事务的开启和提交

3.AOP的组成

(1)切面:指的是要处理的某一集中功能的整个过程,比如用户登录验证的这个过程

(2)切点:定义了方法和拦截规则,规定了哪些方法需要被拦截

(3)连接点:每一个能触发切点的方法,比如在博客详情页、博客编辑页、博客列表页,都需要判断用户登录状态,这时候就会触发用户登录验证这个切点,博客详情页、编辑页、列表页的登录状态就是连接点

(4)通知:拦截方法的具体要实现的业务

          (1)前置通知在目标方法执行之前执行的通知

                 (2) 后置通知:在目标方法执行之后执行的通知

               (3 ) 返回通知:在目标方法返回之后执行的通知

               (4)异常通知:在目标方法抛出异常之后通知

                 (5)环绕通知:在目标方法执行之前和执行之后进行通知

4.SpringAop的实现:

(1)添加SpringAOP的框架支持

(2)定义切面的切点

(3)定义通知

(1)添加AOP框架支持     注意这里是spring-boot的AOP不是spring的AOP

     <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

(2)定义切面和切点以及相关通知

@Aspect  // 切面
@Component // 不能省略
public class UserAop {
    // 切点(配置拦截规则)
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
    }

    // 前置通知
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行了前置通知:" + LocalDateTime.now());
    }

    // 后置通知
    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行了后置通知:" + LocalDateTime.now());
    }

    // 环绕通知
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开始执行环绕通知了");
        Object obj = joinPoint.proceed();
        System.out.println("结束环绕通知了");
        return obj;
    }
}

这里要注意@Aspect这个注解表明这个类是切面类,而@Component这个注解也不能少,只有这样我们AOP框架才会随着我们的spring项目启动而启动,如果不加这个注解,aop没法使用

切点表达式由切点函数组成,其中execution()是最常用的切点函数:execution(<修饰符><返回类型><包.类.方法(参数)<异常> 

其中修饰符可以省略,异常可以省略,其他的则不能省略

 (3)在UserController底下建立一个类,类里面包含两个方法

@RestController
public class Usercontroller {
    @RequestMapping("/user/sayhi")
    public String sayHi() {
        System.out.println("执行了 sayHi 方法");
        return "hi,spring boot aop.";
    }

    @RequestMapping("/user/login")
    public String login() {
        System.out.println("执行了 login 方法");
        return "do user login";
    }
}

当我们起去访问login方法时,login方法会被拦截

当我们下面我们来运行一下看一下运行结果

我们不难发现开始执行环绕通知和前置通知都是在目标方法 执行之前通知,而后置通知和结束环绕通知都是在目标方法执行之后通知,开始执行环绕通知在前置通知前面执行,结束环绕通知在执行后置通知之后执行。

5.SpringAOP的实现原理:

(1)JDK 动态代理:通过反射实现动态代理,其特点速度快

(2)CGLIB:通过字节码增强技术,生成代理类的子类实现动态代理,它不能代理被final修饰类

6.SpringAOP进行用户统一登录验证

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;
@Aspect

@Component

public class UserAspect {
 // 定义切点⽅法 controller 包下、⼦孙包下所有类的所有⽅法

 @Pointcut("execution(* com.example.demo.controller..*.*(..))")
 public void pointcut(){ }
 // 前置⽅法

 @Before("pointcut()")
 public void doBefore(){
 
 }
 
 // 环绕⽅法

 @Around("pointcut()")
 public Object doAround(ProceedingJoinPoint joinPoint){
 Object obj = null;
 System.out.println("Around ⽅法开始执⾏");
 try {
 // 执⾏拦截⽅法

 obj = joinPoint.proceed();
 } catch (Throwable throwable) {
 throwable.printStackTrace();
 }
 System.out.println("Around ⽅法结束执⾏");
 return obj;
 }
}

用这种方法实现存在两个问题:(1)httpseesion对象我们获取不到(2)拦截规则表达式不好定义, 有的方法需要拦截,但是像登录方法和注册方法则不需要拦截

那这样如何解决呢?

二.统一功能及处理

1.spring拦截器

我们使用spring为我们提供的拦截器,可以方便快捷的实现拦截功能

(1)首先自定义一个拦截器,实现HandlerInterceptor接口,重写preHandle方法

public class LoginIntercpter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession httpSession=request.getSession(false);
      //对所有拦截的方法进行验证,如果验证通过则返回继续执行目标方法
//  如果验证失效,则不执行目标方法
        if(httpSession!=null&&httpSession.getAttribute("session_userifo")!=null)
        {
            return true;
        }
        response.setStatus(401);
        return false;
    }
}

(2)将自定义拦截器加入到系统配置,自定义一个类,然后继承WebMvcConfigurer这个接口使用addInterceptor()方法将拦截器加入到系统配置,然后定义拦截规则

Controller
public class Myconfig implements WebMvcConfigurer {
    @Autowired
    public LoginIntercpter loginIntercpter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginIntercpter)
                .addPathPatterns("/**")  //拦截所有的url
                .excludePathPatterns("/login")//将登录页排除
                .excludePathPatterns("/reg")//将注册页排除
                .excludePathPatterns("/image/**")//将image底下所有文件排除
                ;
    }
}

addPathPatterns:表示要拦截的URL,"**"表示拦截任一方法

excludePathPatterns:表示需要排除的URL

以上拦截规则可以拦截项目中使用的URL、包括静态文件(图片文件、JS、CSS等文件) 

我们定义一些方法来进行验证

@RestController
public class Usercontroller {
    @RequestMapping("/login")
    public String login()
    {
       return"执行了login方法";
    }
    @RequestMapping("/reg")
    public String reg()
    {
        return "执行了reg方法";
    }
    @RequestMapping("/bloglist")
    public String bloglist()
    {
        return "执行了bloglist方法";
    }
}

当我们访问login方法和reg方法时:

但是当我访问需要被拦截的方法bloglist的时候,因为没有进行登录,没有通过登录验证所以bloglist方法不会被执行,被返回401

但是这里还是存在问题,大家想一下,如果被拦截器 拦截的方法没有经过验证,拦截器返回一个状态码401,这对于前端不友好,因为前端不清楚到底是目标方法出错了,还是没有通过拦截器的验证。因此这里我们最好返回一个json格式的字符串

 

拦截器原理:

拦截器实现原理源码分析:

每个请求都会经过一个调度器 DispatcherServlet ,由它来决定和分配怎样去执行如何去执行,所有⽅法都会执⾏ DispatcherServlet 中的 doDispatch 调度⽅法, doDispatch 调度⽅法源码如下:

所有统一访问前缀添加:所有请求地址加上api前缀,这种做法主要是为了中小型公司为了节约成本,中小型公司一般一个服务器上可能会部署多个程序,如果两个程序名相同,即使它们的端口名相同,但是也不好区分,而在地址前加上api前缀则解决了这一问题

 

2.统一异常处理:

当我们去访问一个具体的方法的时候,如果我们的代码抛出异常这时候会直接返回一个状态码500,

 

这就不够具体,我们应该要和前端约定一个数据格式,当后端出现异常的时候,把这个固定的数据格式返回给前端,那如何来解决这一问题呢?

(1)先来定义一个处理空指针异常的类

@ControllerAdvice
@ResponseBody
public class Myexception {
    @ExceptionHandler(NullPointerException.class)
    public HashMap<String, Object> donullpoint(NullPointerException e) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("code", -300);
        map.put("msg:", "空指针异常" + e.getMessage());
        map.put("data", "");
        return map;

    }

}

(2)再写一个会出现空指针异常的方法

 @RequestMapping("/user1")
    public String douser()
    {
        Object object=null;
        System.out.println(object.hashCode());
        return "hello user";

    }

 

 

我们来看一下运行结果:

 

 

我们知道当前我们所写的类是指针空指针异常处理的 类,那如果出现的异常不是空指针的异常呢?比如下面出现的算术异常,

 

 那这样的话处理空指针异常的方法就不起作用了,这时我们定义一个能处理所有异常的方法

 

3.统一数据返回格式

为什么要返回统一数据格式:
(1) 降低了程序员之间的沟通成本,所有的接口按照固定的格式进行返回

(2)有利于项目统一数据的维护和修改

(3)方便前端程序员更好地接收和解析后端数据接口返回的数据

(4)有利于后端技术部门统一规范的标准制定,使返回内容格式统一

我们约定返回数据格式为有code(状态码),msg(信息),data(数据)Map结构

但我们在一个方法里返回1的时候,因为其不符合我们约定的固定的返回数据的格式

@RestController
public class Myfordata {
    @RequestMapping("/data1")
    public Integer dodata1()
    {

        return 1;
    }
}

我们建一个统一处理格式的类使其符合约定的返回数据格式

注意这个类不需要加ResponseBody注解,因为我们这里只是把不符合格式的数据进行处理,最终返回给前端还是要通过被访问的方法进行返回的

当我们去访问dodate1这个方法时 

 

 

我们可以看到数据被按照约定的格式返回了

当我们去返回一个类型为boolean类型的时候,因为我们建立了一个统一返回数据格式的类,它也会被按照 规定格式返回。

但是这里有一个特殊的的例子:就是我们返回的数据为String类型的时候

 @RequestMapping("/data2")
    public String data2()
    {

        return "helloword2";
    }

我们来访问一下: 

 

发现不符合预期,这又是什么原因呢?

 (1)方法返回的是String

    (2)  统一数据返回之前  将String转换成HashMap的格式

  (3)最后将HashMap转换成application/json格式的字符串给前端

在第三步的时候会判断Body的类型,如果是String类型,会交给StringHttpMessageConverter进行类型转换,如果使用StringHttpMessageConverter是不能把hashmap转换成json格式的字符串的,如果不是String类型,会交给HttpMessageConverter进行类型转换
应该如何解决呢?主要有两种解决方案

1.在代码里添加逻辑,如果是String类型,那么我们通过objectMappe这个对象的writeValueAsStrin

g方法把它转换成json格式的字符串

 if (body instanceof String) {
            try {
                body= objectMapper.writeValueAsString(map);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }


      return body;

2.去掉StringHttpMessageConverter

@Configuration
public class MyConfig implements WebMvcConfigurer {
  
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof StringHttpMessageConverter);
    }
}

 

 

 

 

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

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

相关文章

linux内核篇-内存管理(虚拟内存和物理内存、进程虚拟内存布局、内存映射)

主要包括虚拟内存和物理内存、进程内存空间、用户态和内核态的内存映射。 分段机制 分段机制比较符合逻辑&#xff0c;比如可以把程序分成代码段、全局变量段、堆栈段等。 分段的虚拟地址主要包含段选择因子和段内偏移。段选择子就保存在段寄存器中&#xff0c;段选择子中有…

【资料分享】低速数字输入电路

1、方案设计&#xff1a;单通道、单向、反相器 该电路采用单通道&#xff0c;单向光耦&#xff0c;只支持漏型输入&#xff0c;电路的输入端压差满足24V DC10%(21.6V DC-26.4V DC)&#xff0c;输出端电压在0~3.3V范围摆动。 1.1关键技术规格 1.2具体原理图 1.3电路原理详解 …

数字图像处理-基础

数字图像处理-基础 文章目录 一、闲谈二、人类视觉系统三、光和电磁波谱四、图像感知与获取五、图像取样与量化5.1. 数字图像的表示5.2. 空间和灰度分辨率5.3. 图像内插5.3.1. 最近邻内插5.3.2. 双线性内插5.3.3. 双三次内插 六、像素间的关系6.1. 相邻像素6.2. 邻接性、连通性…

【架构设计】阿里开源架构Cola4.0的项目实践:订单系统

项目介绍 使用SpringBootMybaitsPlusCola&#xff08;整洁面向对象分层架构&#xff09;4.0重构订单功能 项目地址 Gitee&#xff1a;https://gitee.com/charles_ruan/smile-cola Github&#xff1a;https://github.com/charles0719/smile-cola 项目核心API 新增 POST http:…

华为OD机试真题 Java 实现【整理扑克牌】【2023Q1 100分】

一、题目描述 给定一组数字&#xff0c;表示扑克牌的牌面数字&#xff0c;忽略扑克牌的花色&#xff0c;请按如下规则对这一组扑克牌进行整理&#xff1a; 步骤1 对扑克牌进行分组&#xff0c;形成组合牌&#xff0c;规则如下&#xff1a; 当牌面数字相同张数大于等于4时&a…

【FPGA】Verilog:锁存器 Latch | RS Flip-Flop 与 D Flip-Flop 的实现

&#x1f4ad; 写在前面&#xff1a;本章将理解 RS/D 锁存器的概念&#xff0c;了解 RS/D/JK 触发器的概念&#xff0c;使用 Verilog 实现各种锁存器 (Latch) 和翻转器 (Flip-Flop)&#xff0c;并通过 FPGA 验证用 Verilog 的实现。 &#x1f4dc; 本章目录&#xff1a; Ⅰ. …

Java中synchronized的优化

本文介绍为了实现高效并发&#xff0c;虚拟机对 synchronized 做的一系列的锁优化措施 高效并发是从 JDK5 升级到 JDK6 后一项重要的改进项&#xff0c;HotSpot 虚拟机开发团队在 JDK6 这个版本上花费了大量的资源去实现各种锁优化技术&#xff0c;如适应性自旋&#xff08;Ada…

【fly-iot飞凡物联】(6):通过docker镜像使用gitbook启动ActorCloud项目文档,发现是个IOT功能非常丰富的项目,可以继续研究下去。

目录 前言1&#xff0c;关于 ActorCloud 使用手册2&#xff0c;使用docker 构建文档4&#xff0c;或者使用别人的gitbook镜像5&#xff0c;总结 前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 fly-iot飞凡物联专栏&#xff1a; https://…

含sop的配电网重构(含风光|可多时段拓展)

目录 1 主要内容 2 部分程序 3 下载链接 1 主要内容 之前分享了很多配电网重构的程序&#xff0c;每个程序针对场景限定性比较大&#xff0c;程序初学者修改起来难度较大&#xff0c;本次分享一个基础程序&#xff0c;针对含sop的配电网重构模型&#xff0c;含风电和光伏&…

skywalking安全认证问题

skywalking安全认证 一、问题二、步骤2.1 skywalking-aop配置文件修改2.2 agent配置文件修改 一、问题 在springboot项目使用java-agent接入skywalking时&#xff0c;为保证两者之间的数据安全传输&#xff0c;准备加个安全认证 参考文章&#xff1a; https://www.helloworld…

尝试探索水下目标检测,基于yolov5轻量级系列模型n/s/m开发构建海底生物检测系统

其实&#xff0c;水下目标检测相关的项目早在之前就已经做了几个了&#xff0c;但是没有系统性地对比过&#xff0c;感兴趣的话可以先看下之前的文章&#xff0c;如下&#xff1a; 《基于自建数据集【海底生物检测】使用YOLOv5-v6.1/2版本构建目标检测模型超详细教程》 《基于…

Qt编写视频监控系统73-不同视频流不同类型的判断和解析(http/m3u8/rtsp/rtmp等)

一、前言 这套视频监控系统大概从2018年起步整体框架&#xff0c;一步步积累到现在&#xff0c;中间经历了无数次的各种视频文件、视频流、视频设备的播放测试&#xff0c;比如光视频文件就有mp4/wmv/rmvb/mkv/avi等格式&#xff0c;视频设备有本地USB摄像头、桌面等&#xff…

【k8s】【ELK】【zookeeper+kafka+efak】日志环境部署

1、日志收集基本概念 k8s中pod的路径&#xff1a; containers log: /var/log/containers/*.log Pod log&#xff1a; /var/log/pods docker log: /var/lib/docker/containers/*/*.log如何收集日志 使用 EFKLogstashKafka 1、filebeat读取容器中的日志&#xff0c;然后写入K…

camunda如何发布和调用rest服务接口

一、camunda如何发布rest服务接口 Camunda BPM 平台本身提供了 REST API 接口&#xff0c;可以用于管理和操作 Camunda 平台中的各种资源和数据&#xff0c;如流程定义、流程实例、任务等。因此&#xff0c;我们可以通过编写 Camunda 应用程序的方式&#xff0c;将 Camunda RE…

Ubuntu系统远程桌面安装运行记录

Ubuntu系统远程桌面安装运行记录 分别测试了20.04和22.04两个版本 一、widows远程 参考连接https://blog.csdn.net/qq_50263172/article/details/128465149 安装步骤如下&#xff1a; sudo apt-get install xrdpsudo apt install tightvncserversudo apt-get install xubuntu-…

CSS基础-超详解

目录 什么是CSS? CSS的引入方式 内部样式 外部样式 内联样式 CSS选择器 CSS标签选择器 类选择器 id选择器 通配符选择器 CSS属性设置 字体 文本 什么是CSS? CSS也被叫做层叠样式表, 英文全称为: Cascading Style Sheets, 是一种用来表现HTML(标准通用标记语言的一个应用)…

【云原生】k8s管理工具--Kubectl(二)

k8s管理工具--Kubectl&#xff08;二&#xff09; 一、项目生命周期1、项目生命周期2、创建kubectl run命令3、发布kubectl expose命令&#xff08;1&#xff09; Service的作用&#xff08;2&#xff09;Service的类型&#xff08;3&#xff09;查看Pod网络状态详细信息和Serv…

django 数据迁移makemigrations和migrate的备忘录

问题描述 之前成功生成过&#xff0c;重新执行python manage.py migrate后&#xff0c;数据库没有生成对应的数据表&#xff1b;表现为 Apply all migrations: admin, auth, contenttypes, sessions Running migrations:No migrations to apply.执行python manage.oy makemig…

STM32-光敏传感器实验

光敏传感器的主要是光敏二极管&#xff0c;核心是PN结&#xff0c;利用了光电效应&#xff0c;对光强很敏感&#xff0c;有单向导电性&#xff0c;工作时需要加反向电压。光照越强&#xff0c;等效电阻越小。 实验要求通过ADC3通道6&#xff08;PF8&#xff09;采集光敏二极管…

geoserver安装与发布服务过程

geoserver是做地图领域开发必会的插件之一&#xff0c;今天我们来分享多种安装方式与发布服务过程&#xff1a; 一、安装下载 1、官网地址&#xff1a;GeoServer 进入下载页面选择下载的版本&#xff0c;我这里选择2.22.3的稳定版本。 来到&#xff1a; 多种安装包可选&#…