spring aop切面编程 您所在的位置:网站首页 java编程是做什么的 spring aop切面编程

spring aop切面编程

2023-06-14 04:35| 来源: 网络整理| 查看: 265

AOP 切面编程-学习笔记 AOP切面编程是什么

我们用Spring中对AOP的介绍来讲解什么是切面编程, AOP 侧重于横切关注点,横切关注点横切多个对象,主要用于日志记录、安全性、性能监控和事务管理

“Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. Whereas OOP focuses on objects and their interactions, AOP focuses on crosscutting concerns which cut across multiple objects, such as logging, security, performance monitoring, and transaction management.” 我在知乎上见到一个很好的讲切面编程的例子,用洗澡来做例子,男人洗澡,需要脱衣服-> 唱歌->洗头->洗身体, 女人洗澡,需要 脱衣服-> 洗头-> 洗身体-> 洗脸-> 护肤-> 穿衣服。 其中脱衣服,穿衣服是洗澡所用到的用到的流程,可以将其独立出来 在这里插入图片描述

为什么需要用到切面编程

AOP的设计是为了将业务逻辑与其他的功能(日志,事务,安全管理,性能优化)进行分离。将其他功能统一进行分类进行统一管理,这样提高了代码的重用性,可维护性。 当你实现一个功能,如果考虑到该功能对于系统来说,是一个横向逻辑,可以考虑用切面编程。

怎么用切面编程

在Spring AOP中,提供如下注解。 在这里插入图片描述 正常执行顺序: @Around前半部分代码 -> Before -> PointCut指向的函数-> @AfterReturning-> @After -> @Around后半部分代码 异常执行的顺序: @Around前半部分代码 -> Before -> PointCut指向的函数->发生异常-> @Afterthowing-> @After -> @Around后半部分代码

接下来我们的具体的例子中讲解如何使用AOP

正常的流程 添加依赖 org.springframework.boot spring-boot-starter-aop 2.3.7.RELEASE 创建interface Person public interface Person { public void wash(); } 创建class Man package com.shirley.service; import org.springframework.stereotype.Component; @Component public class Man implements Person { @Override public void wash() { sing(); washHead(); // throw new NullPointerException("测试"); washBody(); } private void sing(){System.out.println("男人开始唱歌");} private void washHead(){System.out.println("男人开始洗头");} private void washBody(){System.out.println("男人开始洗身体");} } 创建class Woman package com.shirley.service; import org.springframework.stereotype.Component; @Component public class Woman implements Person{ @Override public void wash() { washHead(); washBody(); doFacial(); } private void washHead(){System.out.println("女人开始洗头");} private void washBody(){System.out.println("女人开始洗身体");} private void doFacial(){System.out.println("女人开始护肤");} } 创建Aspect 在@pointCut注解中,做了一个mapping, 当执行app.aop.wash.Person 方法中wash()函数前,会执行切片的其他函数(比如:注解@Around(“washPointCut()”)定义的函数, 注解@Before(“washPointCut()”)定义的函数) 在这里插入图片描述 在下面的例子中,因为我们希望在man 和woman洗澡前,都使用切面,于是我们将man 和woman 抽象到Person的接口中,在@pointCut做的mapping中,将washPointCut()于Person.wash()做一个对应,这样washPointCut()不仅与Man.wash()对应,也与Woman.wash()对应 package com.shirley.aop.wash; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * 定义衣服切面 */ @Aspect @Component public class ClothAspect { /** * 将下面相同的切点表达式抽取到统一的方法,使用的地方直接引用即可 */ @Pointcut("execution(* com.shirley.service.Person.wash(..))") public void washPointCut(){} /** * 前置通知 */ @Before("washPointCut()") public void takeoffCloth() { System.out.println(" --- @Before ---准备洗澡,脱衣服....."); } /** * 后置通知 */ @After("washPointCut()") public void wearDress() { System.out.println("--- @After ---洗澡完成!穿好衣服...."); } /** * 环绕通知 * @param point */ @Around("washPointCut()") public void aroundAdvice(ProceedingJoinPoint point) { try { System.out.println("--- @Around before ---这里是环绕通知-前"); point.proceed(); System.out.println("--- @Around - after ---这里是环绕通知-后"); } catch (Throwable throwable) { System.out.println("--- @Around--catch exception ---这里是catch到异常"); throwable.printStackTrace(); } } @AfterReturning("washPointCut()") public void returnAdvice(){ System.out.println("--- @AfterReturning ---这是方法正常执行完后会执行的通知"); } @AfterThrowing("washPointCut()") public void exceptionAdvice(){ System.out.println("--- @afterThrowing ---方法执行抛出异常时会执行的异常通知"); } } Junit Test case package com.shirley; import com.shirley.service.Man; import com.shirley.service.Woman; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest() class AspectTest { @Test public void testAspect() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.shirley"); Man man = context.getBean(Man.class); Woman woman = context.getBean(Woman.class); System.out.println("-----------------------男生开始洗澡---------------------------"); man.wash(); System.out.println("-----------------------女生开始洗澡---------------------------"); woman.wash(); } } 执行结果如下 -----------------------男生开始洗澡--------------------------- 这里是环绕通知-前 准备洗澡,脱衣服… 男人开始唱歌 男人开始洗头 男人开始洗身体 这是方法正常执行完后会执行的通知 洗澡完成!穿好衣服… 这里是环绕通知-后 -----------------------女生开始洗澡--------------------------- 这里是环绕通知-前 准备洗澡,脱衣服… 女人开始洗头 女人开始洗身体 女人开始护肤 这是方法正常执行完后会执行的通知 洗澡完成!穿好衣服… 这里是环绕通知-后 异常的流程

我在Man.wash()中抛出了一个异常

@Override public void wash() { sing(); washHead(); // throw new NullPointerException("测试"); washBody(); }

输出如下

-----------------------男生开始洗澡--------------------------- --- @Around before ---这里是环绕通知-前 --- @Before ---准备洗澡,脱衣服..... 男人开始唱歌 男人开始洗头 --- @afterThrowing ---方法执行抛出异常时会执行的异常通知 --- @After ---洗澡完成!穿好衣服.... --- @Around--catch exception ---这里是catch到异常 java.lang.NullPointerException: 测试 -----------------------女生开始洗澡--------------------------- --- @Around before ---这里是环绕通知-前 --- @Before ---准备洗澡,脱衣服..... 女人开始洗头 女人开始洗身体 女人开始护肤 --- @AfterReturning ---这是方法正常执行完后会执行的通知 --- @After ---洗澡完成!穿好衣服.... --- @Around - after ---这里是环绕通知-后 可以设置切面的范围

在上面的例子中,我们知道可以通过@pointCut设置切面的函数。 在上面的例子中,是针对单个函数进行了AOP.

1|execution(public * *(..)) //任意public方法 2|execution(* wash*(..)) //任意名称以wash开头的方法 3|execution(* com.xyz.service.AccountService.*(..)) //com.xyz.service.AccountService接口中定义的任意方法 4|execution(* com.xyz.service.*.*(..)) //com.xyz.service包中定义的任意方法 5|execution(* com.xyz.service..*.*(..)) //com.xyz.service包中及其子包中定义的任意方法 Spring AOP 切面变成的拓展例子 在每个方法调用时,打印一个日志。该日志包含: 函数传入是参数中的一个ID(每个参数中包含的ID不一样,于是增加了难度,在这里用接口来解决)+annotation中的描述

正常的业务逻辑

package com.shirley.service; import org.springframework.stereotype.Service; @Service public class OrderService { @RecordOperate(desc = " save order", convert = SaveOrderConvert.class) public boolean saveOrder(SaveOrder saveOrder) { System.out.println("save order " + saveOrder.getID()); return true; } @RecordOperate(desc = " update order", convert = UpdateOrderConvert.class) public boolean updateOrder(UpdateOrder updateOrder){ System.out.println("update order " + updateOrder.getOrderId()); return true; } }

SendOrder.java

package com.shirley.service; import lombok.Data; @Data public class SaveOrder { private long ID; }

UpdateOrder.java

package com.shirley.service; import lombok.Data; @Data public class UpdateOrder { private long orderId; }

通过annotation 触发AOP, 可以重点关注每一步的注释

package com.shirley.aop; import com.shirley.service.Convert; import com.shirley.service.OperateLogDO; import com.shirley.service.RecordOperate; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @Aspect @Component public class OperateAspect { @Pointcut("@annotation(com.shirley.service.RecordOperate)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result = proceedingJoinPoint.proceed(); // 返回的object 是该函数的返回值。 这里返回是boolean-true //调用saveOrder时,触发的AOP,这里拿到的methodSignature= 该method,也就是: saveOrder(SaveOrder) MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); Method method = methodSignature.getMethod(); //拿到注解 RecordOperate recordOperate = method.getAnnotation(RecordOperate.class); //最后解决拿不到注解的方法,是在注解上增加@Retation(value=Runtime) Class


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有