IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpingBoot整合AOP教程详解 -> 正文阅读

[Java知识库]SpingBoot整合AOP教程详解

说起spring,我们知道其最核心的两个功能就是AOP(面向切面)和IOC(控制反转),这边文章来总结一下SpringBoot如何整合使用AOP。

前言

AOP术语解析

连接点

简单来说,就是允许你使用通知、增强的地方。就比如在方法前后打印日志一样,我们可以在一段代码的前后做操作,可以在一段代码前做操作,可以在一段代码后做操作,可以在一段代码抛异常之后做操作。所以,在这里这些可以操作的一行行代码(方法等等)都是一个个的连接点。

切入点

把一个个方法等代码看作连接点,那我们从哪个位置打印日志(增强操作)呢,而我们挑选出需要打印日志的位置(也就是连接点的周围),就被称为切入点。

增强、通知

关于增强,在上面我已经说到过了,通过在切入点做的操作叫做增强,比如我们要打印日志的话,日志就是一个增强功能操作。

目标对象

目标对象,简单来说是要被增强的对象。

引介

允许我们向现有的类添加新方法属性。这不就是把切面(也就是增强定义的新方法属性)用到目标对象中

织入

把增强应用到具体的目标对象中,进而创建新的代理类的过程

代理

代理就像我们买房子的中介一样,也就是被AOP织入后产生的代理对象(中介对象),通过代理对象可以实现对我们的目标对象增强

切面

切面是通知(增强)和切入点的结合。通知说明了干什么和什么时候干,而切入点说明了在哪干,这就是一个完整的切面定义。

一、示例应用场景:对方法添加打印通知

1.1 pom文件引入相关依赖


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

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>2.3</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.3.9.RELEASE</version>
		</dependency>


其中:
cglib包是用来动态代理用的,基于类的代理;
aspectjrt和aspectjweaver是与aspectj相关的包,用来支持切面编程的;
aspectjrt包是aspectj的runtime包;
aspectjweaver是aspectj的织入包;

1.2 编写Controller类并编写需要被增强的方法

@RestController
@RequestMapping("/hello")
public class HelloController {
    @GetMapping
    public String sayHello() {
        return "Hello,Spring AOP";
    }
}

注意:在完成了引入AOP依赖包后,一般来说并不需要去做其他配置。使用过Spring注解配置方式的人会问是否需要在程序主类中增加@EnableAspectJAutoProxy来启用,实际并不需要。

因为在AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy。

1.3 定义切面类,实现web层的日志切面

要想把一个类编程切面类,需要两步:

  • 在类上使用@Component 注解,把切面类加入到IOC容器中
  • 在类上使用@Aspect注解 使之成为切面类
package stu01.com.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MethodAcpect {
    //配置连接点
    @Pointcut("execution(public * stu01.com.controller..*.*(..))")
    //此方法名与下面注解中的参数一一对应
    public void methodAcpect(){

    }

    //实现要对某个方法增强的功能
    @Before("methodAcpect()")
    public  void methodAcpectBefore(JoinPoint joint){
        //获取代理执行的方法名
        String name = joint.getSignature().getName();
        System.out.println("name:  "+name+" 方法执行前.....");
    }

    @After("methodAcpect()")
    public  void methodAcpectAfter(JoinPoint joint){
        String name = joint.getSignature().getName();
        System.out.println("name:  "+name+" 方法执行后.....");
    }
}

1.4 访问URL请求,观察结果

在这里插入图片描述
在这里插入图片描述

二、AOP支持的通知

2.1 前置通知@Before

前置通知@Before:在某连接点之前执行的通知,除非抛出一个异常,否则这个通知不能阻止连接点之前的执行流程。

/** 
 * 前置通知,方法调用前被调用 
 * @param joinPoint/null
 */  
@Before(value = POINT_CUT)
public void before(JoinPoint joinPoint){
    logger.info("前置通知");
    //获取目标方法的参数信息  
    Object[] obj = joinPoint.getArgs();  
    //AOP代理类的信息  
    joinPoint.getThis();  
    //代理的目标对象  
    joinPoint.getTarget();  
    //用的最多 通知的签名  
    Signature signature = joinPoint.getSignature();  
    //代理的是哪一个方法  
    logger.info("代理的是哪一个方法"+signature.getName());  
    //AOP代理类的名字  
    logger.info("AOP代理类的名字"+signature.getDeclaringTypeName());  
    //AOP代理类的类(class)信息  
    signature.getDeclaringType();  
    //获取RequestAttributes  
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();  
    //从获取RequestAttributes中获取HttpServletRequest的信息  
    HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);  
    //如果要获取Session信息的话,可以这样写:  
    //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);  
    //获取请求参数
    Enumeration<String> enumeration = request.getParameterNames();  
    Map<String,String> parameterMap = Maps.newHashMap();  
    while (enumeration.hasMoreElements()){  
        String parameter = enumeration.nextElement();  
        parameterMap.put(parameter,request.getParameter(parameter));  
    }  
    String str = JSON.toJSONString(parameterMap);  
    if(obj.length > 0) {  
        logger.info("请求的参数信息为:"+str);
    }  
}

注意:
这里用到了JoinPoint和 RequestContextHolder。

  • 通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等
  • 通过RequestContextHolder来获取请求信息,Session信息;

2.2 后置通知@AfterReturning

在某连接点之后执行的通知,通常在一个匹配的方法返回的时候执行(可以在后置通知中绑定返回值)。

/** 
 * 后置返回通知 
 * 这里需要注意的是: 
 *      如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 
 *      如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 
 *       returning:限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,
 *       对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 
 * @param joinPoint 
 * @param keys 
 */  
@AfterReturning(value = POINT_CUT,returning = "keys")  
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){  
    logger.info("第一个后置返回通知的返回值:"+keys);  
}  

@AfterReturning(value = POINT_CUT,returning = "keys",argNames = "keys")  
public void doAfterReturningAdvice2(String keys){  
    logger.info("第二个后置返回通知的返回值:"+keys);  
}

2.3 后置异常通知@AfterThrowing

在方法抛出异常退出时执行的通知。

/** 
 * 后置异常通知 
 *  定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; 
 *  throwing:限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, 
 *           对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 
 * @param joinPoint 
 * @param exception 
 */  
@AfterThrowing(value = POINT_CUT,throwing = "exception")  
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){  
    //目标方法名:  
    logger.info(joinPoint.getSignature().getName());  
    if(exception instanceof NullPointerException){  
        logger.info("发生了空指针异常!!!!!");  
    }  
}  

2.4 后置最终通知@After:

当某连接点退出时执行的通知(不论是正常返回还是异常退出)。

/** 
 * 后置最终通知(目标方法只要执行完了就会执行后置通知方法) 
 * @param joinPoint 
 */  
@After(value = POINT_CUT)  
public void doAfterAdvice(JoinPoint joinPoint){ 
    logger.info("后置最终通知执行了!!!!");  
}  

2.5 环绕通知@Around

包围一个连接点的通知,如方法调用等。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或者直接返回它自己的返回值或抛出异常来结束执行。

环绕通知最强大,也最麻烦,是一个对方法的环绕,具体方法会通过代理传递到切面中去,切面中可选择执行方法与否,执行几次方法等。环绕通知使用一个代理ProceedingJoinPoint类型的对象来管理目标对象,所以此通知的第一个参数必须是ProceedingJoinPoint类型。在通知体内调用ProceedingJoinPoint的proceed()方法会导致后台的连接点方法执行。proceed()方法也可能会被调用并且传入一个Object[]对象,该数组中的值将被作为方法执行时的入参。

/** 
 * 环绕通知: 
 *   环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 
 *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 
 */  
@Around(value = POINT_CUT)  
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){  
    logger.info("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName());  
    try {  
        Object obj = proceedingJoinPoint.proceed();  
        return obj;  
    } catch (Throwable throwable) {  
        throwable.printStackTrace();  
    }  
    return null;  
}  
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:16:56  更:2022-02-26 11:19:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:30:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码