Rubin's Blog

  • 首页
  • 关于作者
  • 隐私政策
享受恬静与美好~~~
分享生活的点点滴滴~~~
  1. 首页
  2. Spring
  3. 正文

Spring AOP 应用

2021年 5月 28日 768点热度 0人点赞 0条评论

AOP相关术语

说起“面向切面编程”,这已经不是一个新鲜的词儿了。它的本质就是在不影响原有业务逻辑的情况下做一些逻辑增强。比方说请求接口前后的日志打印、权限校验、登录验证等等场景就是很经典的AOP的使用场景。

下表阐述了AOP中一些术语的概念:

名词解释
Joinpoint(连接点)指得是可以加入增强逻辑的点。也就是说在方法执行的开始、返回之前、返回之后和抛出异常时这几个点。连接点是一些候选点。
Pointcut(切入点)通俗意义上来讲,就是定义横切逻辑会影响哪些方法和横切方法的标识。
Advice(通知/增强)指的是切面类中用于逻辑增强的方法。其分类有前置通知、后置通知、异常通知、最终通知和环绕通知几类。
Target(目标对象)即被代理的对象,或者被增强的对象。
Proxy(代理)指的是目标类被AOP织入增强后产生的代理对象。
Weaving(织入)指的是把增强应用到目标对象来创建一个新的代理对象的过程。Spring采用动态代理的方式来织入,而AspectJ采用编译期织入和类装载期织入。
Aspect(切面)指的是增强代码所关注的方面。通俗来讲就是如果一个类中有增强逻辑,那么这个类就是一个切面类。

其实概念还是很绕的,但是目的就是为了所定要在那个地方插入什么增强逻辑。所以总结来说就是:

Aspect切面 = 切入点 + 增强 = 切入点(锁定方法)+ 方位点(锁定方法中的特殊时机)+ 横切逻辑。

Spring中的AOP的代理选择

Spring实现AOP用的是动态代理技术。默认情况下,Spring会根据被代理的对象是否实现了接口来选择动态代理技术。如果实现了接口,就采用JDK动态代理技术。没有的话,使用CGLIB动态代理技术。不过,我们可以通过配置来让Spring强制使用CGLIB动态代理。

Spring中AOP的配置方式

Spring中支持三种AOP的配置方式,如下:

  • 使用xml配置。
  • 使用注解结合xml的方式配置。
  • 使用纯注解方式配置。

Spring中AOP的使用

xml配置模式

Spring是模块化开发的框架,使用AOP就引入相关的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.1.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

AOP的配置示例如下:

<!--
Spring基于xml的AOP配置前期准备:
在spring的配置文件中加入aop的约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
Spring基于xml的AOP配置步骤:
    第⼀步:把通知Bean交给Spring管理
    第⼆步:使用aop:config开始aop的配置
    第三步:使用aop:aspect配置切面
    第四步:使用对应的标签配置通知的类型
    案例采用前置通知,标签为aop:before
-->
<!--把通知bean交给spring来管理-->
<bean id="logUtil" class="com.rubin.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
    <!--配置切面-->
    <aop:aspect id="logAdvice" ref="logUtil">
        <!--配置前置通知-->
        <aop:before method="printLog" pointcut="execution(public *
 com.rubin.service.impl.TransferServiceImpl.updateAccountByCardNo(com.rubin.pojo.Account))">        
        </aop:before>
        <!--配置方法正常执行,返回后通知-->
        <aop:after-returning method="afterReturningPrintLog" pointcut="execution(public *
 com.rubin.service.impl.TransferServiceImpl.updateAccountByCardNo(com.rubin.pojo.Account))">        
        </aop:after-returning>
        <!--配置异常时通知-->
        <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(public *
 com.rubin.service.impl.TransferServiceImpl.updateAccountByCardNo(com.rubin.pojo.Account))">        
        </aop:after-throwing>
        <!--配置最终通知-->
        <aop:after method="afterPrintLog" pointcut="execution(public *
 com.rubin.service.impl.TransferServiceImpl.updateAccountByCardNo(com.rubin.pojo.Account))">        
        </aop:after>
        <!--配置环绕通知-->
        <aop:around method="aroundPrintLog" pointcut="execution(public *
 com.rubin.service.impl.TransferServiceImpl.updateAccountByCardNo(com.rubin.pojo.Account))">        
        </aop:around>
    </aop:aspect>
</aop:config>

对于示例配置,这里做一点补充。我们的一个aop:aspect里面可以定义多个切入点(通过aop:pointcut定义),也可以定义多个通知(示例中只有一个前置通知)。我们每个通知都可以引用上面定义的切入点(通过pointcut-ref)或者直接定义pointcut的表达式。

另外一个补充点是配置文件约束。配置文件约束说白了就是加入Spring对应模块的约束文件的引用地址,加入之后,我们在配置文件中就可以添加对应的配置,也会出现相应的提示。比如我们加入了aop的约束,我们在配制aop的时候,就会出现对应的标签提示。我们加入了springmvc的约束,相应的该配置文件就可以使用springmvc相对应的配置标签。约束的格式都一样,就是把后缀换成Spring对应的模块即可。

我们也可以手动切换代理方式,即手动指定Spring的AOP代理方式固定为CGLIB,配置方式如下:

<aop:config proxy-target-class="true">

或

<!--此标签是基于XML和注解组合配置AOP时的必备标签,表示Spring开启注解配置AOP
的支持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-
autoproxy>

切入点表达式

切入点表达式,也称之为AspectJ切入点表达式。指的是遵循特定语法结构的字符串,其作用是用于符合其特定语法格式的连接点方法进行增强。它是AspectJ表达式的一部分。

AspectJ是⼀个基于Java语⾔的AOP框架,Spring框架从2.0版本之后集成了AspectJ框架中切入点表达式的部分,开始⽀持AspectJ切入点表达式。

表达式的格式为:访问修饰符(可省略) 返回值 包名.类名.方法名(参数列表)。全匹配方式示例:public void com.rubin.demo.TestDemo.hello(java.lang.String)。其中访问修饰符可以省略,即也可以配制为:void com.rubin.demo.TestDemo.hello(java.lang.String)。返回值可以为*,表示所有类型的返回值,即配置为:* com.rubin.demo.TestDemo.hello(java.lang.String)。包名也可以使用*,标识任意包名,但是有几个包要写几个*,即:* *.*.*.TestDemo.hello(java.lang.String)。也可以使用*..来表示当前包以及所有子包,即:* ..TestDemo.hello(java.lang.String)。也可以使用*来表示任意类名和方法名,即:* *..*.*(java.lang.String)。参数列表,对象类型要使用全限定类名,基本类型例如int就直接写int。参数列表也可以使用*来表示一个任意参数类型,但是必须要有参数(多个参数可以使用(*,*,*)或者(*,int,boolean)等格式定义个数和类型),而使用..标识任意个数任意参数类型,也就是说可以没有参数。所以通配的配置为:* *..*.*(..)。

关于详细的切入点表达式介绍可以参考官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-introduction-defn。

xml+注解模式

此种方式是比较常用的一种使用方式。一般来讲,我们是使用xml配置文件来开启AOP注解扫描。使用AOP相关注解来定义我们的切面类。

xml中开启AOP注解扫描:

<!--开启spring对注解aop的⽀持-->
<aop:aspectj-autoproxy/>

定义切面类,示例如下:

@Component
@Aspect
public class LogUtil {
    /**
      * 我们在xml中已经使用了通用切入点表达式,供多个切面使用,那么在注解中如何使用呢?
      * 第⼀步:编写⼀个方法
      * 第⼆步:在方法使用@Pointcut注解
      * 第三步:给注解的value属性提供切入点表达式
      * 细节:
      *   1.在引用切入点表达式时,必须是方法名+(),例如"pointcut()"。
      *   2.在当前切面中使用,可以直接写方法名。在其他切面中使用必须是全限定方法名。
      */
    @Pointcut("execution(* com.rubin.service.impl.*.*(..))")
    public void pointcut(){}

    @Before("pointcut()")
    public void beforePrintLog(JoinPoint jp){
         Object[] args = jp.getArgs();
         System.out.println("前置通知:beforePrintLog,参数是:"+
 Arrays.toString(args));
    }

    @AfterReturning(value = "pointcut()", returning = "rtValue")
    public void afterReturningPrintLog(Object rtValue){
        System.out.println("后置通知:afterReturningPrintLog,返回值
是:" + rtValue);
    }

    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowingPrintLog(Throwable e){
        System.out.println("异常通知:afterThrowingPrintLog,异常是:" + e);
    }

    @After("pointcut()")
    public void afterPrintLog(){
        System.out.println("最终通知:afterPrintLog");
    }

    /**
      * 环绕通知
      * @param pjp
      * @return
      */
    @Around("pointcut()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        // 定义返回值
        Object rtValue = null;
        try{
  
            // 前置通知
            System.out.println("前置通知");
            // 1.获取参数
            Object[] args = pjp.getArgs();
            // 2.执行切入点方法
            rtValue = pjp.proceed(args);
            // 后置通知
            System.out.println("后置通知");
        }catch (Throwable t){
            //异常通知
            System.out.println("异常通知");
            t.printStackTrace();
        }finally {
            //最终通知
            System.out.println("最终通知");
        }
        return rtValue;
    }
}

注解模式

注解模式其实就是我们使用注解来替换掉上一小节中xml配置的AOP注解扫描配置。配置类如下:

@Configuration
@ComponentScan("com.rubin")
// 开启spring对注解AOP的支持
@EnableAspectJAutoProxy 
public class SpringConfiguration {
}
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: Spring
最后更新:2022年 6月 9日

RubinChu

一个快乐的小逗比~~~

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复
文章目录
  • AOP相关术语
  • Spring中的AOP的代理选择
  • Spring中AOP的配置方式
  • Spring中AOP的使用
    • xml配置模式
      • 切入点表达式
    • xml+注解模式
    • 注解模式
最新 热点 随机
最新 热点 随机
问题记录之Chrome设置屏蔽Https禁止调用Http行为 问题记录之Mac设置软链接 问题记录之JDK8连接MySQL数据库失败 面试系列之自我介绍 面试总结 算法思维
Kafka之源码环境搭建 java并发编程之Atomic类 MySQL之Sharding-JDBC编排治理剖析 JVM之内存管理 java面试系列之引用 RocketMQ之架构与实战

COPYRIGHT © 2021 rubinchu.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

京ICP备19039146号-1