你敢信?面试官竟然让我现场搭建一个AOP测试环境!

开发 前端
金九银十的跳槽黄金期已拉开序幕,相信很多小伙伴也在摩拳擦掌,想换一个新的工作环境。然而,由于今年疫情的影响,很多企业对于招聘的要求是越来越严格。

[[340324]]

作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:https://github.com/sunshinelyz/mykit-delay

写在前面

金九银十的跳槽黄金期已拉开序幕,相信很多小伙伴也在摩拳擦掌,想换一个新的工作环境。然而,由于今年疫情的影响,很多企业对于招聘的要求是越来越严格。之前,很多不被问及的知识点,最近面试时都会被问到了。这不,有些面试官竟然让面试者现场搭建一个AOP测试环境。那怎么办呢?那就给他搭建一个呗!

什么是AOP?

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

比如,在《Spring实战(第4版)》中有如下一张图描述了AOP的大体模型。

从这张图中,我们可以看出:所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。

总之一句话:AOP是指在程序的运行期间动态的将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。

搭建环境

1.导入AOP依赖

要想搭建AOP环境,首先,我们就需要在项目的pom.xml文件中引入AOP的依赖,如下所示。

  1. <properties> 
  2.     <spring.version>5.2.6.RELEASE</spring.version> 
  3. </properties> 
  4. <dependency> 
  5.     <groupId>org.springframework</groupId> 
  6.     <artifactId>spring-aspects</artifactId> 
  7.     <version>${spring.version}</version> 
  8. </dependency> 

2.定义目标类

在io.mykit.spring.plugins.register.aop包下创建一个MathHandler类,用于处理数学计算上的一些逻辑。比如,我们在MathHandler类中定义了一个加法操作,返回两个整数类型值的和,如下所示。

  1. package io.mykit.spring.plugins.register.aop; 
  2. /** 
  3.  * @author binghe 
  4.  * @version 1.0.0 
  5.  * @description 定义一个数据处理器类,用于测试AOP 
  6.  */ 
  7. public class MathHandler { 
  8.  
  9.     public int add(int i, int j){ 
  10.         System.out.println("目标方法执行"); 
  11.         return i + j; 
  12.     } 

3.定义切面类

在io.mykit.spring.plugins.register.aspect包下创建一个LogAspect切面类,在LogAspect类中定义了几个打印日志的方法,以这些方法来感知MathHandler类中的add()方法的运行情况。如果需要切面类来感知目标类方法的运行情况,则需要使用Spring AOP中的通知方法。

AOP中的通知方法及其注解与含义如下:

  • 前置通知(@Before):在目标方法运行之前运行。
  • 后置通知(@After):在目标方法运行结束之后运行,不管是正常结束还是异常结束都会执行。
  • 返回通知(@AfterReturning):在目标方法正常返回之后运行。
  • 异常通知(@AfterThrowing):在目标方法抛出异常后运行。
  • 环绕通知(@Around):动态代理,手动推进目标方法运行。

综上,LogAspect类中的具体方法定义如下所示。

  1. package io.mykit.spring.plugins.register.aspect; 
  2. import org.aspectj.lang.annotation.*; 
  3. /** 
  4.  * @author binghe 
  5.  * @version 1.0.0 
  6.  * @description 打印日志的切面类 
  7.  */ 
  8. @Aspect 
  9. public class LogAspect { 
  10.  
  11.     @Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))"
  12.     public void pointCut(){ 
  13.  
  14.     } 
  15.  
  16.     @Before("pointCut()"
  17.     public void logStart(){ 
  18.         System.out.println("加法运行开始,参数列表是:{}"); 
  19.     } 
  20.  
  21.     @After("pointCut()"
  22.     public void logEnd(){ 
  23.         System.out.println("加法运行结束"); 
  24.     } 
  25.  
  26.     @AfterReturning("pointCut()"
  27.     public void logReturn(){ 
  28.         System.out.println("加法正常返回,运行结果:{}"); 
  29.     } 
  30.  
  31.     @AfterThrowing("pointCut()"
  32.     public void logException(){ 
  33.         System.out.println("加法异常,异常信息:{}"); 
  34.     } 
  • logStart()方法:MathHandler类的add()方法运行之前运行。
  • logEnd()方法:MathHandler类的add()方法运行结束之后运行。
  • logReturn()方法:MathHandler类的add()方法正常返回之后运行。
  • logException()方法:MathHandler类的add()方法抛出异常后执行。

4.将目标类和切面类加入到IOC容器

在io.mykit.spring.plugins.register.config包中,新建AopConfig类,并使用@Configuration注解标注这是一个Spring的配置类,同时使用@EnableAspectJAutoProxy注解开启基于注解的AOP模式。在AopConfig类中,使用@Bean注解将MathHandler类和LogAspect类加入到IOC容器中,如下所示。

  1. package io.mykit.spring.plugins.register.config; 
  2. import io.mykit.spring.plugins.register.aop.MathHandler; 
  3. import io.mykit.spring.plugins.register.aspect.LogAspect; 
  4. import org.springframework.context.annotation.Bean; 
  5. import org.springframework.context.annotation.Configuration; 
  6. import org.springframework.context.annotation.EnableAspectJAutoProxy; 
  7. /** 
  8.  * @author binghe 
  9.  * @version 1.0.0 
  10.  * @description 测试AOP 
  11.  */ 
  12. @Configuration 
  13. @EnableAspectJAutoProxy 
  14. public class AopConfig { 
  15.      
  16.     @Bean 
  17.     public MathHandler mathHandler(){ 
  18.         return new MathHandler(); 
  19.     } 
  20.  
  21.     @Bean 
  22.     public LogAspect logAspect(){ 
  23.         return new LogAspect(); 
  24.     } 

5.创建测试类

在 io.mykit.spring.test包中创建AopTest测试类,并在AopTest类中创建testAop01()方法,如下所示。

  1. package io.mykit.spring.test; 
  2. import io.mykit.spring.plugins.register.aop.MathHandler; 
  3. import io.mykit.spring.plugins.register.config.AopConfig; 
  4. import org.junit.Test; 
  5. import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
  6. /** 
  7.  * @author binghe 
  8.  * @version 1.0.0 
  9.  * @description 测试切面 
  10.  */ 
  11. public class AopTest { 
  12.  
  13.     @Test 
  14.     public void testAop01(){ 
  15.         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class); 
  16.         MathHandler mathHandler = context.getBean(MathHandler.class); 
  17.         mathHandler.add(1, 2); 
  18.         context.close(); 
  19.     } 

运行AopTest类中的testAop01()方法,输出的结果信息如下所示。

  1. 加法运行开始,参数列表是:{} 
  2. 目标方法执行 
  3. 加法运行结束 
  4. 加法正常返回,运行结果:{} 

可以看到,执行了切面类中的方法,并打印出了相关信息。但是没有打印参数列表和运行结果。

6.在切面类中打印参数列表和返回结果

那如果需要打印出参数列表和运行结果,该怎么办呢?别急,我们继续往下看。

要想打印出参数列表和运行结果,就需要对LogAspect类中的方法进行优化,优化后的结果如下所示。

  1. package io.mykit.spring.plugins.register.aspect; 
  2.  
  3. import org.aspectj.lang.JoinPoint; 
  4. import org.aspectj.lang.annotation.*; 
  5. import java.util.Arrays; 
  6. /** 
  7.  * @author binghe 
  8.  * @version 1.0.0 
  9.  * @description 打印日志的切面类 
  10.  */ 
  11. @Aspect 
  12. public class LogAspect { 
  13.  
  14.     @Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))"
  15.     public void pointCut(){ 
  16.  
  17.     } 
  18.  
  19.     @Before("pointCut()"
  20.     public void logStart(JoinPoint joinPoint){ 
  21.         System.out.println(joinPoint.getSignature().getName() + " 运行开始,参数列表是:{"+ Arrays.asList(joinPoint.getArgs()) +"}"); 
  22.     } 
  23.  
  24.     @After("pointCut()"
  25.     public void logEnd(JoinPoint joinPoint){ 
  26.         System.out.println(joinPoint.getSignature().getName() + " 运行结束"); 
  27.     } 
  28.  
  29.     @AfterReturning(value = "pointCut()", returning = "result"
  30.     public void logReturn(JoinPoint joinPoint, Object result){ 
  31.         System.out.println(joinPoint.getSignature().getName() + " 正常返回,运行结果:{"+result+"}"); 
  32.     } 
  33.  
  34.     @AfterThrowing(value = "pointCut()", throwing = "exception"
  35.     public void logException(JoinPoint joinPoint, Exception exception){ 
  36.         System.out.println(joinPoint.getSignature().getName() + " 异常,异常信息:{"+exception+"}"); 
  37.     } 

这里,需要注意的是:JoinPoint参数一定要放在参数的第一位。

此时,我们再次运行AopTest类中的testAop01()方法,输出的结果信息如下所示。

  1. add 运行开始,参数列表是:{[1, 2]} 
  2. 目标方法执行 
  3. add 运行结束 
  4. add 正常返回,运行结果:{3} 

7.目标方法抛出异常

我们在MathHandler类的add()方法中抛出一个异常,来测试下异常情况,如下所示。

  1. package io.mykit.spring.plugins.register.aop; 
  2.  
  3. /** 
  4.  * @author binghe 
  5.  * @version 1.0.0 
  6.  * @description 定义一个数据处理器类,用于测试AOP 
  7.  */ 
  8. public class MathHandler { 
  9.  
  10.     public int add(int i, int j){ 
  11.         System.out.println("目标方法执行"); 
  12.         throw new RuntimeException(); 
  13.        //return i + j; 
  14.     } 

此时,我们再次运行AopTest类中的testAop01()方法,输出的结果信息如下所示。

  1. add 运行开始,参数列表是:{[1, 2]} 
  2. 目标方法执行 
  3. add 运行结束 
  4. add 异常,异常信息:{java.lang.RuntimeException} 

可以看到,正确的输出了切面中打印的信息。

至此,我们的AOP测试环境就搭建成功了。

本文转载自微信公众号「冰河技术」,可以通过以下二维码关注。转载本文请联系冰河技术公众号。

 

责任编辑:武晓燕 来源: 冰河技术
相关推荐

2019-12-02 10:51:11

Redis存储系统

2017-03-16 15:27:10

面试官测试技术

2021-09-28 13:42:55

Chrome Devwebsocket网络协议

2024-09-03 09:20:45

2024-05-28 10:14:31

JavaScrip模板引擎

2015-08-13 10:29:12

面试面试官

2020-05-22 08:11:48

线程池JVM面试

2022-04-08 08:26:03

JavaHTTP请求

2023-01-15 17:57:12

缓存技术kafka磁盘

2021-01-14 05:23:32

高并发消息中间件

2022-11-04 08:47:52

底层算法数据

2021-12-02 08:19:06

MVCC面试数据库

2021-08-02 17:21:08

设计模式订阅

2019-05-10 10:50:04

Spring AOPJDK动态代理CGLIB动态代理

2024-04-03 00:00:00

Redis集群代码

2022-02-07 20:18:29

Redis缓存装饰

2022-01-10 11:04:41

单链表面试编程

2020-06-22 07:47:46

提交面试官订单

2022-08-18 20:02:04

JSLRU缓存

2023-07-31 08:26:09

点赞
收藏

51CTO技术栈公众号