首页 > 其他分享 >Spring Boot---(25)SpringBoot使用AOP

Spring Boot---(25)SpringBoot使用AOP

时间:2023-01-10 11:35:22浏览次数:51  
标签:25 8080 SpringBoot Spring 切入点 java4all org import com


摘要:本文示例,是在一个简单的SpringBoot项目中,通过AOP技术,来实现对接口访问时的信息统计,和接口耗时统计。

AOP是Spring提供的两个核心功能之一:IOC(控制反转),AOP(Aspect Oriented Programming 面向切面编程);IOC有助于应用对象之间的解耦,AOP可以实现横切关注点和它所影响的对象之间的解耦;

AOP,它通过对既有的程序定义一个横向切入点,然后在其前后切入不同的执行内容,来拓展应用程序的功能,常见的用法如:打开事务和关闭事物,记录日志,统计接口时间等。AOP不会破坏原有的程序逻辑,拓展出的功能和原有程序是完全解耦的,因此,它可以很好的对业务逻辑的各个部分进行隔离,从而使业务逻辑的各个部分之间的耦合度大大降低,提高了部分程序的复用性和灵活性。

本文目录:
1.创建一个SpringBoot的web项目
2.引入依赖
3.UserController
3.UserController
4.几个注解
5.实现切面
6.启动项目,验证
7.是aop来记录每个接口的调用时间
8.启动项目,验证
9.AOP切面的优先级

1.创建一个SpringBoot的web项目

如何创建一个SpringBoot的web项目,可参考:(1)SpringBoot项目的创建。

本示例的目录结构如下如图:

2.引入依赖

在pom.xml中引入aop的依赖

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

引入此依赖后,我们不需要做其他特殊的配置,SpringBoot中的aop默认配置是默认开启的,我们引入即可使用。

3.UserController

我们在此类中,写两个简单的接口,便于测试

package com.java4all.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
* Author: yunqing
* Date: 2018/7/10
* Description:登陆
*/
@RestController
@RequestMapping("user")
public class UserController {

@RequestMapping(value = "login",method = RequestMethod.GET)
public String login(String userName){
return "登陆成功!";
}

@RequestMapping(value = "register",method = RequestMethod.GET)
public String register(){
try {
Thread.sleep(3000);
}catch (InterruptedException ex){
ex.printStackTrace();
}
return "注册成功!";
}
}

4.几个注解

实现aop切面,主要有以下几个关键点需要了解:

@Aspect,此注解将一个类定义为一个切面类;

@Pointcut,此注解可以定义一个切入点,可以是规则表达式,也可以是某个package下的所有函数,也可以是一个注解等,其实就是执行条件,满足此条件的就切入;

然后可以定义切入位置,我们可以选择在切入点的不同位置进行切入:

@Before在切入点开始处切入内容;

@After在切入点结尾处切入内容;

@AfterReturning在切入点return内容之后切入内容(可以用来对返回值做一些处理);

@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容;

@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑;

5.实现切面

我们定义一个切面,然后定义一个切入点,切入点这个方法,不需要方法体,返回值是void,在切入点后定义一下切入条件,当满足条件时,就会在切入点的指定位置织入我们自定义的内容。

package com.java4all.myAop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.jboss.logging.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
* Author: yunqing
* Date: 2018/8/29
* Description:切面类
*/

//定义一个切面类
@Aspect
@Component
public class WebLogAspect {

private Logger logger = Logger.getLogger(getClass());

/**
* 定义一个切入点,可以是规则表达式,也可以是某个package下的所有函数,也可以是一个注解,其实就是执行条件,满足此条件的就切入
* 这里是:com.java4all.controller包以及子包下的所有类的所有方法,返回类型任意,方法参数任意
*/
@Pointcut("execution(* com.java4all.controller..*.*(..))")
public void webLog(){}

/**
* 在切入点之前执行
* @param joinPoint 连接点,实在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,
* 切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint){

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();

//记录请求日志
logger.info("======>url;"+request.getRequestURL().toString());
logger.info("======>httpMethod:"+request.getMethod());
logger.info("======>ip:"+ request.getRemoteAddr());
logger.info("======>classMethod : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("======>args:"+ Arrays.toString(joinPoint.getArgs()));

}


/**
* 在切入点之后执行
* @param response
*/
@AfterReturning(returning = "response",pointcut = "webLog()")
public void doAfterReturn(Object response){
//记录返回值
logger.info("======>response:"+response);
}


}

在上面的切入点定义中,我们看一下含义:

@Pointcut("execution(* com.java4all.controller..*.*(..))")

从前到后顺序解释:

execution:在满足后面的条件的方法执行时触发;

第一个*:表示返回值任意;

com.java4all.controller….:表示此路径下的任意类的任意方法

(…):表示方法参数任意;

6.启动项目,验证

我们启动项目,然后访问接口:​​http://localhost:8080/user/login?userName=java4all​​

查看日志如下:

2018-08-29 19:18:38.918  INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect          : ======>url;http://localhost:8080/user/login
2018-08-29 19:18:38.918 INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>httpMethod:GET
2018-08-29 19:18:38.918 INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>ip:0:0:0:0:0:0:0:1
2018-08-29 19:18:38.918 INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>classMethod : com.java4all.controller.UserController.login
2018-08-29 19:18:38.918 INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>args:[java4all]
2018-08-29 19:18:38.918 INFO 22472 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>response:登陆成功!

7.是aop来记录每个接口的调用时间

这个也非常简单,我们只需要在切入点前记录方法调用前的时间,再在切入点后使用当前时间减去调用开始的时间即可,我们这里引入ThreadLocal类,来记录方法开始调用的时间。具体代码实现如下:

package com.java4all.myAop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.jboss.logging.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
* Author: yunqing
* Date: 2018/8/29
* Description:切面类
*/

//定义一个切面类
@Aspect
@Component
public class WebLogAspect {

private Logger logger = Logger.getLogger(getClass());

/**记录方法调用开始时间*/
ThreadLocal<Long> startTime = new ThreadLocal<>();

/**
* 定义一个切入点,可以是规则表达式,也可以是某个package下的所有函数,也可以是一个注解,其实就是执行条件,满足此条件的就切入
* 这里是:com.java4all.controller包以及子包下的所有类的所有方法,返回类型任意,方法参数任意
*/
@Pointcut("execution(* com.java4all.controller..*.*(..))")
public void webLog(){}

/**
* 在切入点之前执行
* @param joinPoint 连接点,实在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,
* 切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint){
//记录调用开始时间
startTime.set(System.currentTimeMillis());

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();

//记录请求日志
logger.info("======>url;"+request.getRequestURL().toString());
logger.info("======>httpMethod:"+request.getMethod());
logger.info("======>ip:"+ request.getRemoteAddr());
logger.info("======>classMethod : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("======>args:"+ Arrays.toString(joinPoint.getArgs()));

}


/**
* 在切入点之后执行
* @param response
*/
@AfterReturning(returning = "response",pointcut = "webLog()")
public void doAfterReturn(JoinPoint joinPoint,Object response){

//获取调用事件
logger.info("======>"+joinPoint.getSignature().getDeclaringTypeName()+"."
+joinPoint.getSignature().getName()+"方法耗时:"
+(System.currentTimeMillis()-startTime.get())/1000+"s");

//记录返回值
logger.info("======>response:"+response);
}


}

8.访问地址:​​http://localhost:8080/user/register​​

可见日志如下:

2018-08-29 19:27:10.050  INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect          : ======>url;http://localhost:8080/user/register
2018-08-29 19:27:10.050 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>httpMethod:GET
2018-08-29 19:27:10.050 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>ip:0:0:0:0:0:0:0:1
2018-08-29 19:27:10.051 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>classMethod : com.java4all.controller.UserController.register
2018-08-29 19:27:10.051 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>args:[]
2018-08-29 19:27:13.052 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>com.java4all.controller.UserController.register方法耗时:3s
2018-08-29 19:27:13.053 INFO 1148 --- [nio-8080-exec-4] com.java4all.myAop.WebLogAspect : ======>response:注册成功!

9.AOP切面的优先级

当我们对web层做多个切面时,会有一个问题:究竟先去切入谁?这里引入一个优先级的问题。处理方法非常简单,我们定义切面时,使用一个注解@Order(i),这个i决定着优先级的高低。

比如,现在定义了两个切面,一个@Order(3),一个@Order(8),那么执行时:

在切入点前的操作,按order的值由小到大执行,即:先@Order(3),后@Order(8);

在切入点后的操作,按order的值由大到小执行,即:先@Order(8),后@Order(3);



标签:25,8080,SpringBoot,Spring,切入点,java4all,org,import,com
From: https://blog.51cto.com/u_15936016/5999926

相关文章

  • 225. 多重集组合数(挑战程序设计竞赛)
    地址https://www.papamelon.com/problem/225有n种物品,第i种物品有a_i个。不同种类的物品可以互相区分但相同种类的无法区分。从这些物品中取出m个物品的话,有多少......
  • @ComponentScan详解&@SpringBootApplication的scanBasePackages属性
    一、@ComponentScan源码@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Repeatable(ComponentScans.class)public@interfaceComponen......
  • 【转】Vue+springboot集成PageOffice实现在线编辑Word、excel文档
    说明:PageOffice是一款在线的office编辑软件,帮助Web应用系统或Web网站实现用户在线编辑Word、Excel、PowerPoint文档。可以完美实现在线公文流转,领导批阅,盖章。可以给文件......
  • SpringBoot整合ueditor编辑器
    1.到ueditor编辑器官网下载jsp版(目前官网地址改为了GitHub)​​https://ueditor.baidu.com/website/download.html​​2.下载解压后复制到当前项目3.导入maven依赖<dependenc......
  • Springboot集成Disruptor做内部消息队列
    一、基本介绍Disruptor的github主页:https://github.com/LMAX-Exchange/disruptor1,什么是Disruptor? (1)Disruptor是英国外汇交易公司LMAX开发的一个高性能的并发框架......
  • Spring-注解
    目录前言1.声明bean的注解2.注入bean的注解3.java配置类相关注解前言参考:https://www.cnblogs.com/chen991126/p/14110299.html1.声明bean的注解@Component组件......
  • MySQL25 - 三范式
    数据库三范式数据库中表的设计依据第一范式要求任何一张表必须有主键,每一个字段原子性不可再分必须有唯一非空列作为主键比如邮箱和手机号不能存储在一列中,应该分为......
  • 关于SpringBoot多数据源的实现与增加不同拦截器的实现
    1.SpringBoot多数据源的实现在参考了各个文档之后有jpa形式配置文件多为.yml快速搭建springbootmaven项目多数据源https://blog.csdn.net/pete1024/article/details/10......
  • SpringBoot笔记--自动配置(高级内容)(中集)
    @Enable*注解使用该注解,需要导入相应的依赖坐标,其中的groupId标签里面写入Bean的Java文件所在的包的路径下面spring-enable-other还需要在SpringBoot的执行文件那里加......
  • Spring IOC官方文档学习笔记(八)之容器扩展点
    1.通过BeanPostProcessor来自定义bean(1)BeanPostProcessor用于在容器完成了对bean的实例化,配置及初始化后来实现一些自定义逻辑,它是用于操纵由容器创建的每个bean实例的......