简简单单了解一下Spring AOP

Spring AOP正题

AOP是什么

AOP(Aspect Oriented Programming)意为面向切面编程,通过预编译和运行动态代理实现程序功能的统一维护的一种技术。(动态代理见我上一篇博客),以下称AOP为"横切"。
“横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect”,即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。使用"横切"技术,AOР把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

总结来AOP的核心思想就是一句话:

不通过修改源代码的方式增加新功能。

AOP应用场景

那AOP在实际开发中AOP有哪些应用场景呢?

1、Authentication权限

2、Caching 缓存

3、Context passing内容传递

4、Error handling 错误处理

5、Lazy loading懒加载

6、Debugging调试

7、logging, tracing, profiling and monitoring记录跟踪优化校准

8、Persistence持久化

8、Performance optimization性能优化

10、Resource pooling资源池

11、Synchronization同步

12、Transactions事务

AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

AOP核心的概念

  • 横切关注点:跨越应用程序多个模块的方法或功能。即便是与我们业务逻辑无关的,但是我们也需要关注的部分,就是横切关注点。如:日志,安全,缓存,事务等等… …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

可以简单的认为,使用@Aspect注解的类就是切面。

  • 通知(Advice):所谓通知只得就是拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
通知类型 连接点 实现接口
前置通知(before advice) 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知() 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知() 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知() 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知() 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor
  • 目标(Target):被通知的对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知执行的“地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

彻底理解aspect, join point, point cut, advice

看完上面这些理论,相信大家还是很迷惑,我也是,这里放上简单的例子来加深AOP 中 aspect, jointpoint, pointcut 与 advice 的理解

假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系.
首先我们知道, 在 Spring AOP 中 join point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 join point, 通过 point cut, 我们就可以确定哪些 join point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, join point 就相当于 爪哇的小县城里的百姓, point cut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问.
为什么可以这样类比呢?

  • join point --> 爪哇的小县城里的百姓: 因为根据定义, join point 是所有可能被织入 advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 join point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.
  • point cut --> 男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 advice, 但是我们并不希望在所有方法上都织入 advice, 而 pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问.
  • advice --> 抓过来审问, advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 join point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸爪哇的小县城里的百姓.
  • aspect: aspect 是 point cut 与 advice 的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 aspect.

参考

https://segmentfault.com/a/1190000007469968

使用Spring实现AOP

使用AOP织入,要导入一个aspect依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

excution表达式:

​ 例如定义切入点表达式 execution (* com.sample.service.impl…*. *(…)) <------所有类的所有方法

  1. execution(): 表达式主体。
  2. 第一个号:表示返回类型, 号表示所有的类型。
  3. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,
    com.sample.service.impl包、子孙包下所有类的方法。
  4. 第二个号:表示类名,号表示所有的类。
  5. (…):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数

方式一:使用Spring的API接口实现

<!--    方式一:使用原生SpringAPI接口-->
<!--    配置AOP:需要导入aop 的约束-->
    <aop:config>
        <!--切入点:expression:表达式,execution(要执行的位置!* * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.feng.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

方式二:自定义类来实现AOP

【主要是切面定义】

<!--方式二:自定义类-->
    <bean id="diy" class="com.feng.diy.DiyPointCut"/>
        <aop:config>
            <aop:aspect ref="diy">
                <!--切入点-->
                <aop:pointcut id="point" expression="execution(* com.feng.service.UserServiceImpl.*(..))"/>
                <!--通知-->
                <aop:before method="before" pointcut-ref="point"/>
                <aop:after method="after" pointcut-ref="point"/>
            </aop:aspect>
        </aop:config>

方式三:通过注解实现

<!--    方式三-->
    <bean id="annotationPointCut" class="com.feng.diy.AnnotationPointCut"/>
<!--    开启注解支持!-->
    <aop:aspectj-autoproxy/>

AnnotationPointCut.java

package com.feng.diy;
//方式三:使用注解方式实现AOP
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
//标注这个类是一个切面
public class AnnotationPointCut {
    
    

    @Before("execution(* com.feng.service.UserServiceImpl.*(..))")
    public void before(){
    
    
        System.out.println("=====方法执行前=====");
    }

    @After("execution(* com.feng.service.UserServiceImpl.*(..))")
    public void after(){
    
    
        System.out.println("=====方法执行后=====");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.feng.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
    
    
        System.out.println("环绕前");
        Object proceed = jp.proceed();
        //执行方法
        System.out.println("环绕后");

//        Signature signature = jp.getSignature();//获得签名
//        System.out.println("signature:"+signature);
//        System.out.println(proceed);
    }
}

在这里插入图片描述

具体实现

https://blog.csdn.net/java_cxrs/article/details/108372459

常见Question

描述一下Spring AOP

Spring AOP(Aspect Oriented Programming,面向切面编程)是OOPs(面向对象编程)的补充,它也提供了模块化。在面向对象编程中,关键的单元是对象,AOP的关键单元是切面,或者说关注点(可以简单地理解为你程序中的独立模块)。一些切面可能有集中的代码,但是有些可能被分散或者混杂在一起,例如日志或者事务。这些分散的切面被称为横切关注点。一个横切关注点是一个可以影响到整个应用的关注点,而且应该被尽量地集中到代码的一个地方,例如事务管理、权限、日志、安全等。
AOP让你可以使用简单可插拔的配置,在实际逻辑执行之前、之后或周围动态添加横切关注点。这让代码在当下和将来都变得易于维护。如果你是使用XML来使用切面的话,要添加或删除关注点,你不用重新编译完整的源代码,而仅仅需要修改配置文件就可以了。
Spring AOP通过以下两种方式来使用。但是最广泛使用的方式是Spring AspectJ 注解风格(Spring AspectJ Annotation Style)

  • 使用AspectJ 注解风格
  • 使用Spring XML 配置风格

在Spring中AOP的关注点和横切关注点有什么不同

关注点 我们想在应用的模块汇总实现的行为。关注点可以被定义为:我们想以解决特定业务问题的方法。比如,在电子商务应用中,不同的关注点(或者模块)可能是库存管理,航运管理,用户管理等。

横切关注点是贯穿整个应用程序的关注点。像日志、安全和数据转换,它们在应用的每一个模块都是必须的,所以他们是一种横切关注点。

AOP有哪些可用的实现?

基于Java的主要AOP实现有

1、AspectJ

2、SpringAOP

3、JBossAOP

Spring中有哪些不同的通知类型

通知(Advice)是你在你的程序中想要应用在其他模块中的横切关注点的实现。Advie主要有以下5中类型:

1、前置通知(Before Advice):在连接点之前执行的Advice,不过除非它抛出异常,否则没有能力中断执行流。使用**@Before**注解来使用这个Advice。

2、返回之后通知(After Retuning Advice):在连接点正常之后执行的Advice。例如,一个方法没有抛出异常正常返回。通过**@AfterReturning**关注使用它。

3、抛出(异常)后执行通知(After Throwing Advice):如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。通用**@AfterThrowing**注解来使用。

4、后置通知(After Advice):无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行在结束后执行这些Advice。通过**@After**注解使用。

5、围绕通知(Around Advice):围绕连接点执行的Advice,就你一个方法调用。这是最强大的Advice。通过**@Around**注解使用。

SpringAOP代理是什么?

代理是使用非常广泛的设计模式。简单来说,代理的作用就是通过代理,控制对对象的访问。SpringAOP是基于代理实现的。AOP代理是一个由AOP框架创建的用于在运行时实现切面协议的对象。

SpringAOP默认为AOP代理使用标准的JDK动态代理。这使得任何接口(或者接口的集合)可以被代理。SpringAOP也可以使用CGLib代理。这对代理类而不是接口是必须的。

如果业务对象没有实现任何接口那么默认使用CGLib。

引介(Introduction)是什么?

引介让一个切面可以声明被通知的对象实现了任何他们没有真正实现的额外接口,而且为这些对象提供接口的实现。使用**@DeclareParaents**注解来生成一个引介。

连接点(Joint Point)和切入点(Point cut)是什么?

连接点是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在SpringAOP中,一个连接点总是代表一个方法执行。就是说,所有我们自己定义的接口中的方法都可以被认为是一个连接点,如果你在这些方法使用横切关注点的话。

切入点(切入点)是一个匹配连接点的断言或者表达式。Advice 与切入点表达式相关联,并在切入点匹配的任何连接点处运行(比如,表达式execution(*EmployeeManager.getEmployeeById(...)) 可以匹配 EmployeeManager 接口的 getEmployeeById() )。由切入点表达式匹配的连接点的概念是 AOP 的核心。Spring 默认使用 AspectJ 切入点表达式语言。

什么是织入(weaving)?

这些方法使用横切关注点的话。

切入点(切入点)是一个匹配连接点的断言或者表达式。Advice 与切入点表达式相关联,并在切入点匹配的任何连接点处运行(比如,表达式execution(*EmployeeManager.getEmployeeById(...)) 可以匹配 EmployeeManager 接口的 getEmployeeById() )。由切入点表达式匹配的连接点的概念是 AOP 的核心。Spring 默认使用 AspectJ 切入点表达式语言。

什么是织入(weaving)?

Spring AOP 框架仅支持有限的几个 AspectJ 切入点的类型,它允许将切面运用到在 IoC 容器中声明的 bean 上。如果你想使用额外的切入点类型或者将切面应用到在 Spring IoC 容器外部创建的类,那么你必须在你的 Spring 程序中使用 AspectJ 框架,并且使用它的织入特性。

猜你喜欢

转载自blog.csdn.net/weixin_43876186/article/details/108629877