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知识库 -> SpringBoot2自定义注解,以AOP简单日志记录为例 -> 正文阅读

[Java知识库]SpringBoot2自定义注解,以AOP简单日志记录为例

作者:recommend-item-box type_blog clearfix

一、前言

好久不见,大家还好吗?

最近开始学习若依框架:一个基于SpringBoot的后台管理系统,作为快速开发框架,网上评价不错,有时间的同学可以看看代码学习一下。

一开始打算以若依框架中关于Controller层的Log注解作为模板进行自定义注解的介绍,因为在切面类中涉及到了一些基础框架层的封装类及方法,如果深入去讲摊子会铺的很大,就脱离了自定义注解的主题,因此本文仅仅以最简单的程序来演示一下SpringBoot自定义注解的实现。仅做演示

二、正文开始

1、项目结构:最简单的SpringBoot脚手架搭建的web项目

在这里插入图片描述
pom依赖:

	<dependencies>
        <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot 拦截器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

application.yml:

server:
  port: 12345

2、自定义注解

package com.example.demoannotation.annotation;

import java.lang.annotation.*;

/**
 * @Author: zongshaofeng
 * @Description: 自定义的操作日志注解
 * @Date:Create:in 2021/10/22 8:25
 * @Modified By:
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
	/**
	 * 模块名称
	 */
	String title() default "";
	
	/**
	 * 为了简单,省略后续内容
	 */
}

3.自定义注解切面类

package com.example.demoannotation.aspectj;

import com.example.demoannotation.annotation.OperationLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: zongshaofeng
 * @Description: 操作日志记录的切面类
 * @Date:Create:in 2021/10/22 8:30
 * @Modified By:
 */
@Aspect
@Component
public class OperationLogAspect {
	
	/**
	 * @Author: zongshaofeng
	 * @Description: 返回增强,目标方法正常执行完毕时执行
	 * @Date: 2021/10/22 20:15 
	 * @param: joinPoint
	 * @param: operationLog
	 * @param: returnObj
	 * @return: void
	 * @Version: 1.0
	 */
	@AfterReturning(pointcut = "@annotation(operationLog)",returning = "returnObj")
	public void doAfterReturning(JoinPoint joinPoint, OperationLog operationLog, Object returnObj) {
		System.out.println("===============AfterReturning切入点开始执行......===============");
		Map<String,Object> map=new HashMap<>();
		map.put("title",operationLog.title());
		map.put("returnObj",returnObj);
		map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName());
		System.out.println(map);
		System.out.println("===============AfterReturning切入点执行完成。===============");
	}
	
	/**
	 * @Author: zongshaofeng
	 * @Description: 异常抛出增强,目标方法发生异常的时候执行
	 * @Date: 2021/10/22 20:16 
	 * @param: joinPoint
	 * @param: operationLog
	 * @param: e
	 * @return: void
	 * @Version: 1.0
	 */
	@AfterThrowing(pointcut = "@annotation(operationLog)",throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception e){
		System.out.println("===============AfterThrowing切入点开始执行......===============");
		Map<String,Object> map=new HashMap<>();
		map.put("title",operationLog.title());
		map.put("exceptionMsg",e.getMessage());
		map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName());
		System.out.println(map);
		System.out.println("===============AfterThrowing切入点执行完成。===============");
	}
	
	/**
	 * @Author: zongshaofeng
	 * @Description: 前置增强:在目标方法执行之前
	 * @Date: 2021/10/22 20:16
	 * @param: joinPoint
	 * @param: operationLog
	 * @return: void
	 * @Version: 1.0
	 */
	@Before(value = "@annotation(operationLog)")
	public void doBefore(JoinPoint joinPoint, OperationLog operationLog){
		System.out.println("===============Before切入点开始执行......===============");
		Map<String,Object> map=new HashMap<>();
		map.put("title",operationLog.title());
		map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName());
		System.out.println(map);
		System.out.println("===============Before切入点执行完成。===============");
	}
	
	/**
	 * @Author: zongshaofeng
	 * @Description: 后置增强,不管是抛出异常或者正常退出都会执行
	 * @Date: 2021/10/22 20:17 
	 * @param: joinPoint
	 * @param: operationLog
	 * @return: void
	 * @Version: 1.0
	 */
	@After(value = "@annotation(operationLog)")
	public void doAfter(JoinPoint joinPoint, OperationLog operationLog){
		System.out.println("===============After切入点开始执行......===============");
		Map<String,Object> map=new HashMap<>();
		map.put("title",operationLog.title());
		map.put("method",joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName());
		System.out.println(map);
		System.out.println("===============After切入点执行完成。===============");
	}
	
	/**
	 * @Author: zongshaofeng
	 * @Description:环绕通知:目标方法执行前后进行一些操作
	 * @Date: 2021/10/22 20:17
	 * @param: proceedingJoinPoint
	 * @param: operationLog
	 * @return: java.lang.Object
	 * @Version: 1.0
	 */
	@Around(value = "@annotation(operationLog)")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint, OperationLog operationLog) throws Throwable {
		System.out.println("===============Around切入点开始执行......===============");
		Map<String,Object> map=new HashMap<>();
		map.put("title",operationLog.title());
		map.put("method",proceedingJoinPoint.getTarget().getClass().getName()+"."+proceedingJoinPoint.getSignature().getName());
		System.out.println(map);
		System.out.println("---------Around中前置通知执行完成-------");
		Object proceed = proceedingJoinPoint.proceed();
		System.out.println("---------Around中目标方法返回结果:"+proceed);
		System.out.println("---------Around中目标方法执行完成-------");
		System.out.println("这是后置通知。。。");
		System.out.println("---------Around中后置通知执行完成-------");
		System.out.println("===============Around切入点执行完成。===============");
		return proceed;
	}
}

4、Controller层

package com.example.demoannotation.controller;

import com.example.demoannotation.annotation.OperationLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: zongshaofeng
 * @Description:
 * @Date:Create:in 2021/10/22 8:16
 * @Modified By:
 */
@RestController
@RequestMapping("/demo")
public class SayHelloController {
	
	
	@GetMapping("/sayHello")
	@OperationLog(title = "自定义日志注解测试-说你好模块")
	public String sayHello(String name) {
		return "Hello " + name;
	}
}

5、基本概念简述

AOP(Aspect Oriented Programming,面向切面编程)是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

在Spring AOP中业务逻辑仅仅只关注业务本身,将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,从而在改变这些行为的时候不影响业务逻辑的代码。

AOP 涉及的相关注解主要有以下几个:

注解作用
@Aspect定义切面类
@PointcutPointcut是织入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around环绕通知:目标方法执行前后进行一些操作
@Before前置增强:在目标方法执行之前
@AfterReturning返回增强,目标方法正常执行完毕时执行
@AfterThrowing异常抛出增强,目标方法发生异常的时候执行
@After后置增强,不管是抛出异常或者正常退出都会执行

三、看运行结果,说结论

文字看起来记不住,让我们运行一下,看看这几个增强通知的执行顺序是怎样的,这样在今后的实际开发中才能够得心应手,运用自如。

  • 首先启动项目,我们在Controller中写了一个sayHello方法,并且增加了我们自定义的@OperationLog注解进行了通知增强。
  • 打开浏览器访问说你好(http://localhost:12345/demo/sayHello?name=小宗)
  • 页面返回:
    在这里插入图片描述

1、正常执行

让我们一起来看一下控制台打印:
在这里插入图片描述
所以说,整个AOP的增强通知的执行顺序是这样的:
@Around环绕通知中目标方法调用之前的代码—>@Before前置通知中代码—>@Around中执行调用目标方法—>@Around目标方法调用之后的代码—>@After后置通知—>@AfterReturning

2、产生异常时

让我们再来人为制造点麻烦吧:加点byZero异常
在这里插入图片描述
再来看看执行结果:
在这里插入图片描述
我不是要强调@AfterThrowing的必然执行,而是要指出经过我们的验证:@After增强通知,无论是正常执行还是出现异常,它都会被执行。

好了,收工。

其实讲的非常简单,其中有很多东西并没有被提及,笔者还是想着能够起到一个抛砖引玉的作用吧。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-23 12:20:41  更:2021-10-23 12:22:34 
 
开发: 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年10日历 -2024/10/26 16:27:03-

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