SpringAOP的应用实例与总结

什么是AOP

AOP的全称是Aspect Orient Programming,即面向切面编程。是对OOP(Object Orient Programming)的一种补充,战门用于处理一些具有横切性质的服务。常常用于日志输出、安全控制等。

优点:
1、降低模块之间的耦合度
2、使系统容易扩展
3、更好的代码复用。

AOP使用场景:
AOP用来封装横切关注点,具体可以在下面的场景中使用:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

一:AOP的背景

面试的时候面试官让我解释一下什么是AOP,当时不懂,在路上就查了,AOP:面向切面的编程技术,困惑了,JAVA是OOP:面向对象的编程技术。那么自己就立刻查了几个为题:1、什么是面向切面的编程技术;2、为什么要面向切面的编程技术;3、与OOP是什么关系?
  
首先解释第二个问题:在我们平时的开发过程中,你肯定会遇到下面几个面:1)权限校验;2)业务的核心代码;3)记录日志。那么在@Service层采用代码累加的方法,那么结构就会如下。

@Service
public class myService{
 
@Resource
private CoreService coreService;
 
@Resource
private LogService logService;
@Resource
private PropertyService propertyService;
  
// 权限校验代码
 
//核心业务层代码
 
//记录日志的代码  <br><br>// 异常的处理<br>
}

从上面的代码结构中我们可以看出以下几个问题:

1.1、代码混乱:核心业务模块与其他非核心的代码交织在一起,大大影响了代码的模块独立性能,不利于代码的维护,而且分工不明确造成代码混乱。

1.2、冗余代码:其实权限的校验,异常的处理,日志的记录可以独立在一个模块给所有的服务公用,写在一起导致代码的分散和冗余。

因此面向切面的编程技术应运而生。

解释第一个问题:什么是面向切面的编程技术。切面与切点是几何上面的术语,用在这里可以这样理解:将核心业务代码过程比作一个柱体,其他的日志记录,权限校验等就像是横切核心业务的面,这些面需要完成一些非核心的业务。如下图:
在这里插入图片描述在这里插入图片描述从图中可以看出我们定义了多个切面,每个切面都完成各自的非核心的业务,一个切面上还可以完成多个非核心的业务。

1.3、第三个问题:与OOP是什么关系?

AOP的实现技术有多种,其中与Java无缝对接的是一种称为AspectJ的技术,Spring AOP 与AspectJ 实现原理上并不完全一致,但功能上是相似的。AOP的出现确实解决外围业务代码与核心业务代码分离的问题,但它并不会替代OOP,如果说OOP的出现是把编码问题进行模块化,那么AOP就是把涉及到众多模块的某一类问题进行统一管理(参考:关于 Spring AOP (AspectJ) 你该知晓的一切;其实我也想到了他们的关系,但是感觉没有这篇博客总结的很好)

二:AOP的核心概念

从上右图可以可以很好看到切面(Aspect)包含了切点(PointCut)、连接点(JoinPoint);额外还有通知(Advice),织入(Weaving),引入(Introduce)。

package springMVCmybatis.com.my.aop;
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.After; 
import org.aspectj.lang.annotation.AfterReturning; 
import org.aspectj.lang.annotation.AfterThrowing; 
import org.springframework.core.annotation.Order;
@Aspect
// 切面执行顺序
@Order(3)
public class MyAopTest {
      @Pointcut("execution(* springMVCmybatis..addController.addEmp(..))") 
        private void pointCutMethod() { 
        } 
       
      @Pointcut("execution(* springMVCmybatis.com.my.aop.UserServiceImp.*(..))") 
        private void testAOP() { 
        }
      /*
       *  声明前置通知 ,JoinPont是srpring提供的静态变量,
       *  通过joinPoint参数可以获得目标方法的类名,方法参数,方法名等信息,这个参数可有可无。
       */
        
        @Before("pointCutMethod() || testAOP()") 
        public void doBefore(JoinPoint joinPoint) { 
            System.out.println("@Before:开始添加--order=3"); 
        } 
       
        //声明后置通知 ,如果result的类型与proceed执行的方法返回的参数类型不匹配那么就不会执行这个方法
        @AfterReturning(pointcut = "pointCutMethod()  || testAOP()", returning = "result") 
        public void doAfterReturning(String result) { 
            System.out.println("@AfterReturning:后置通知--order=3"); 
            System.out.println("---" + result + "---"); 
        } 
       
        //声明例外通知 
        @AfterThrowing(pointcut = "pointCutMethod() || testAOP()", throwing = "e") 
        public void doAfterThrowing(Exception e) { 
            System.out.println("@AfterThrowing:例外通知--order=3"); 
            System.out.println(e.getMessage()); 
        } 
       
        //声明最终通知 
        @After("pointCutMethod() || testAOP()") 
        public void doAfter() { 
            System.out.println("@After:最终通知--order=3"); 
        } 
      /*
       * 声明环绕通知
       * 参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标函数,
       * proceed()的返回值就是环绕通知的返回值,proceedingJoinPoint是个接口,
       * implement JoinPoint,所以也可以获得目标函数的类名,方法名等参数。
       */
         
        @Around("pointCutMethod() || testAOP()") 
        public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
            System.out.println("@Around:进入方法---环绕通知--order=3"); 
            Object o = pjp.proceed(); 
            System.out.println("@Around:退出方法---环绕通知--order=3"); 
            return o; 
        }
}

结合例子我们来看看这些核心的概念:

2.1、切面(Aspect):是一个类,里面定义了通知与切点。
2.2、切点(PointCut):表达式。就是告诉程序要在执行哪些核心业务的时候,执行非核心的业务。
2.3、通知(advice):五种通知方式:
@Before:前置通知,在调用目标方法之前执行通知定义的任务
@After:后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
@After-returning:后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
@After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
@Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务。

五种通知方式的执行顺序:

正常情况下的执行顺序:<br><br>@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
============执行业务方法findUser,查找的用户是:张三=============
@Around:退出方法---环绕通知--order=3
@After:最终通知--order=3
@AfterReturning:后置通知--order=3
---张三---
<br>异常情况下的执行顺序:
 
@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
============执行业务方法addUser=============
@After:最终通知--order=3
@AfterThrowing:例外通知--order=3
null

三:切点表达式。

这个表达式有很多种,如方法签名表达式,类型签名表达式,还有其他的表达式,我只用过前面两个,其他的没用过,也不做介绍,如果这两种表达式不能解决问题的可以参考我参考的博客。
3.1:方法签名表达式

execution(<修饰符模式>?<返回类型模式><方法所在类的完全限定名称模式>(<参数模式>)<异常模式>?)
execution(modifiers-pattern? ret-type-pattern fully-qualified-class-name (param-pattern) throws-pattern?) 

其实如果单纯的给定这个表达式还是不容易记忆,下面对比方法的定义来记忆,一个java方法的全部定义方式可以表示成下面的方式:

public String springMVCmybatic.com.my.aop.UserServiceImp(String a, int b) throw Exception{
}
  • modifier-pattern?:表示方法的修饰符,可有可无;对应的就是 public
  • ret-type-pattern:表示方法的返回值;对应的就是 String
  • fully-qualified-class-name 方法所在类的完全限定名称;对应的就是 springMVCmybatic.com.my.aop.UserServiceImp
  • param-pattern:表示方法的参数;对应的就是 String a, int b
  • throws-pattern:表示方法抛出的异常,可有可无;对应的就是 throw Exception

3.2:&&,||,!

@Around("pointCutMethod() || testAOP()") 
      public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
          System.out.println("@Around:进入方法---环绕通知"); 
          Object o = pjp.proceed(); 
          System.out.println("@Around:退出方法---环绕通知"); 
          return o; 
      }

表达式之间可以采用与,或,非的方式来过滤。

四:多个切点的执行顺序
 上面的例子中,定义了order=3,重新创建一个切面,定义order=6,执行的结果是:

@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
@Around:进入方法---环绕通知--order=6
@Before:开始添加--order=6
============执行业务方法findUser,查找的用户是:张三=============
@Around:退出方法---环绕通知--order=6
@After:最终通知--order=6
@AfterReturning:后置通知--order=6
---张三---
@Around:退出方法---环绕通知--order=3
@After:最终通知--order=3
@AfterReturning:后置通知--order=3
---张三---
 
 
@Around:进入方法---环绕通知--order=3
@Before:开始添加--order=3
@Around:进入方法---环绕通知--order=6
@Before:开始添加--order=6
============执行业务方法addUser=============
@After:最终通知--order=6
@AfterThrowing:例外通知--order=6
null
@After:最终通知--order=3
@AfterThrowing:例外通知--order=3
null

从结果中可以看出order越小越先执行,执行完了之后就order越小就越后推出。总结为下面的图:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值