【Spring注解驱动开发】你敢信?面试官竟然让我现场搭建一个AOP测试环境!
写在前面
今天是9月1号,金九银十的跳槽黄金期已拉开序幕,相信很多小伙伴也在摩拳擦掌,想换一个新的工作环境。然而,由于今年疫情的影响,很多企业对于招聘的要求是越来越严格。之前,很多不被问及的知识点,最近面试时都会被问到了。这不,有些面试官竟然让面试者现场搭建一个AOP测试环境。那怎么办呢?那就给他搭建一个呗!
关注 冰河技术 微信公众号,后台回复 “Spring注解” 领取源码。
什么是AOP?
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
比如,在《Spring实战(第4版)》中有如下一张图描述了AOP的大体模型。

从这张图中,我们可以看出:所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
总之一句话:AOP是指在程序的运行期间动态的将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。
搭建环境
1.导入AOP依赖
要想搭建AOP环境,首先,我们就需要在项目的pom.xml文件中引入AOP的依赖,如下所示。
<properties>
<spring.version>5.2.6.RELEASE</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
2.定义目标类
在io.mykit.spring.plugins.register.aop包下创建一个MathHandler类,用于处理数学计算上的一些逻辑。比如,我们在MathHandler类中定义了一个加法操作,返回两个整数类型值的和,如下所示。
package io.mykit.spring.plugins.register.aop;
/**
* @author binghe
* @version 1.0.0
* @description 定义一个数据处理器类,用于测试AOP
*/
public class MathHandler {
public int add(int i, int j){
System.out.println("目标方法执行");
return i + j;
}
}
3.定义切面类
在io.mykit.spring.plugins.register.aspect包下创建一个LogAspect切面类,在LogAspect类中定义了几个打印日志的方法,以这些方法来感知MathHandler类中的add()方法的运行情况。如果需要切面类来感知目标类方法的运行情况,则需要使用Spring AOP中的通知方法。
AOP中的通知方法及其注解与含义如下:
- 前置通知(@Before):在目标方法运行之前运行。
- 后置通知(@After):在目标方法运行结束之后运行,不管是正常结束还是异常结束都会执行。
- 返回通知(@AfterReturning):在目标方法正常返回之后运行。
- 异常通知(@AfterThrowing):在目标方法抛出异常后运行。
- 环绕通知(@Around):动态代理,手动推进目标方法运行。
综上,LogAspect类中的具体方法定义如下所示。
package io.mykit.spring.plugins.register.aspect;
import org.aspectj.lang.annotation.*;
/**
* @author binghe
* @version 1.0.0
* @description 打印日志的切面类
*/
@Aspect
public class LogAspect {
@Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))")
public void pointCut(){
}
@Before("pointCut()")
public void logStart(){
System.out.println("加法运行开始,参数列表是:{}");
}
@After("pointCut()")
public void logEnd(){
System.out.println("加法运行结束");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("加法正常返回,运行结果:{}");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("加法异常,异常信息:{}");
}
}
- 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容器中,如下所示。
package io.mykit.spring.plugins.register.config;
import io.mykit.spring.plugins.register.aop.MathHandler;
import io.mykit.spring.plugins.register.aspect.LogAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @author binghe
* @version 1.0.0
* @description 测试AOP
*/
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public MathHandler mathHandler(){
return new MathHandler();
}
@Bean
public LogAspect logAspect(){
return new LogAspect();
}
}
5.创建测试类
在 io.mykit.spring.test包中创建AopTest测试类,并在AopTest类中创建testAop01()方法,如下所示。
package io.mykit.spring.test;
import io.mykit.spring.plugins.register.aop.MathHandler;
import io.mykit.spring.plugins.register.config.AopConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author binghe
* @version 1.0.0
* @description 测试切面
*/
public class AopTest {
@Test
public void testAop01(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
MathHandler mathHandler = context.getBean(MathHandler.class);
mathHandler.add(1, 2);
context.close();
}
}
运行AopTest类中的testAop01()方法,输出的结果信息如下所示。
加法运行开始,参数列表是:{}
目标方法执行
加法运行结束
加法正常返回,运行结果:{}
可以看到,执行了切面类中的方法,并打印出了相关信息。但是没有打印参数列表和运行结果。
6.在切面类中打印参数列表和返回结果
那如果需要打印出参数列表和运行结果,该怎么办呢?别急,我们继续往下看。
要想打印出参数列表和运行结果,就需要对LogAspect类中的方法进行优化,优化后的结果如下所示。
package io.mykit.spring.plugins.register.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 打印日志的切面类
*/
@Aspect
public class LogAspect {
@Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))")
public void pointCut(){
}
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName() + " 运行开始,参数列表是:{"+ Arrays.asList(joinPoint.getArgs()) +"}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName() + " 运行结束");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result){
System.out.println(joinPoint.getSignature().getName() + " 正常返回,运行结果:{"+result+"}");
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception){
System.out.println(joinPoint.getSignature().getName() + " 异常,异常信息:{"+exception+"}");
}
}
这里,需要注意的是:JoinPoint参数一定要放在参数的第一位。
此时,我们再次运行AopTest类中的testAop01()方法,输出的结果信息如下所示。
add 运行开始,参数列表是:{[1, 2]}
目标方法执行
add 运行结束
add 正常返回,运行结果:{3}
7.目标方法抛出异常
我们在MathHandler类的add()方法中抛出一个异常,来测试下异常情况,如下所示。
package io.mykit.spring.plugins.register.aop;
/**
* @author binghe
* @version 1.0.0
* @description 定义一个数据处理器类,用于测试AOP
*/
public class MathHandler {
public int add(int i, int j){
System.out.println("目标方法执行");
throw new RuntimeException();
//return i + j;
}
}
此时,我们再次运行AopTest类中的testAop01()方法,输出的结果信息如下所示。
add 运行开始,参数列表是:{[1, 2]}
目标方法执行
add 运行结束
add 异常,异常信息:{java.lang.RuntimeException}
可以看到,正确的输出了切面中打印的信息。
至此,我们的AOP测试环境就搭建成功了。
重磅福利
关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!
好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!!
写在最后
如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!

【Spring注解驱动开发】你敢信?面试官竟然让我现场搭建一个AOP测试环境!的更多相关文章
- 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!
写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...
- 【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!
在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件.之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法.构造器位置的自动装配吗?今天我们就一起来探讨下如 ...
- 【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!
写在前面 在之前的文章中,我们探讨了如何向Spring的IOC容器中注册bean组件,讲解了有关bean组件的生命周期的知识.今天,我们就来一起聊聊@Value注解的用法. 项目工程源码已经提交到Gi ...
- 【Spring注解驱动开发】聊聊Spring注解驱动开发那些事儿!
写在前面 今天,面了一个工作5年的小伙伴,面试结果不理想啊!也不是我说,工作5年了,问多线程的知识:就只知道继承Thread类和实现Runnable接口!问Java集合,竟然说HashMap是线程安全 ...
- 【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!
写在前面 在<[String注解驱动开发]面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!>一文中,我们详细的介绍了BeanPostProcessor的执行流 ...
- 【Spring注解驱动开发】你还不会使用@Resource和@Inject注解?那你就out了!!
写在前面 我在 冰河技术 微信公众号中发表的<[Spring注解驱动开发]使用@Autowired@Qualifier@Primary三大注解自动装配组件,你会了吗?>一文中,介绍了如何使 ...
- 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解
写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...
- 1、课程简介-Spring 注解驱动开发
1.课程简介-Spring 注解驱动开发
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...
随机推荐
- NC19989 [HAOI2012]容易题(EASY)
题目链接 题目 题目描述 为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下: 有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能 ...
- NC14545 经商
题目链接 题目 题目描述 小d是一个搞房地产的土豪.每个人经商都有每个人经商的手段,当然人际关系是需要放在首位的. 小d每一个月都需要列出来一个人际关系表,表示他们搞房地产的人的一个人际关系网,但是他 ...
- NC16655 [NOIP2005]过河
题目链接 题目 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可 ...
- ARM 中常用的汇编指令解释汇总
前言 嵌入式项目中经常涉及到需要通过分析编译后的汇编文件,来确定异常代码,对一些常用的指令进行了汇总. 一.处理器内部数据传输指令 在ARM架构中,包括Cortex-A7处理器内部,有一些专门用于数据 ...
- UTF-8 的理解
举个简单的例子: Unicode 只是一个业界标准,具体一个字符占多少字节,取决于编码方式,包括 UTF-8 UTF-16 GB2312 等 "汉" 在 UTF-8 中占到 3 个 ...
- win32- 使用WM_NCPAINT在非客户区域绘制边框
#pragma comment(lib, "UxTheme") #include <windows.h> #include <uxtheme.h> LRES ...
- go-ini解析ini文件
文档 https://github.com/go-ini/ini https://ini.unknwon.io/docs/intro/getting_started go get -u gopkg.i ...
- 【LeetCode二叉树#14】验证二叉搜索树(巩固迭代中序遍历#1)
验证二叉搜索树 力扣题目链接(opens new window) 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子 ...
- 浅谈errgroup的使用以及源码分析
本文讲解的是golang.org/x/sync这个包中的errgroup 1.errgroup 的基础介绍 学习过 Go 的朋友都知道 Go 实现并发编程是比较容易的事情,只需要使用go关键字就可以开 ...
- Bind DNS Server的基础配置
1.访问https://192.168.3.254:10000 由于SSL证书是不安全的,我用的Firefox浏览器会阻止打开网页: 看到上述界面,先选择"高级", 然后再选择&q ...