Spring AOP 应用

Spring AOP 应用

AOP本质:在不改变原有业务代码逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。

AOP 相关术语

业务主线

在讲解AOP术语之前,我们先来看一下下面这两张图
在这里插入图片描述

上图描述的就是未采用AOP思想设计的程序,当我们红色框中圈定的方法时,会带来大量的重复劳动,程序中充斥着大量的重复代码,使我们的程序独立性很差。
而下图中是采用了AOP思想设计的程序,它把红框部分的代码抽取出来的同时,运用动态代理技术,在运行期对需要使用的业务逻辑方法进行增强。

在这里插入图片描述

AOP 术语

名称解释
Joinpoint(连接点)它指的是那些可以用于把增强代码加入到业务主线中的点,那么由上图我们可以看出,这些点指的就是方法。在方法执行的前后通过动态代理技术加入增强的代码。在Spring框架AOP思想的技术实现中,也只支持方法类型的连接点。
Pointcut(切入点)它指的是那些已经把增强代码加入到业务主线进来之后的连接点。由上图中,我们看出表现层transfer 方法就只是连接点,因为判断访问权限的功能并没有对其增强。
Advice(通知/增强)它指的是切面类中用于提供增强功能的方法,并且不同的方法增强的时机是不一样的。比如,开启事务肯定要在业务方法执行之前执行;提交事务要在业务方法正常执行之后执行;而回滚事务要在要在业务方法执行产生异常后执行等等。那么这就是通知的类型,其分类有:前置通知、后置通知。异常通知、最终通知、环绕通知
Target(目标对象)它指的是代理的目标对象,即被代理对象。
Proxy(代理)它指的是一个类被AOP织入增强后产生的代理类,即代理对象。
Weaving(织入)它指的是把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Aspect(切面)它指的是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是个切面类。例如事务切面,它里面定义的方法就是和事务相关的,像开启事务、提交事务、回滚事务等,不会定义其他与事务无关的方法。

重点:

  • 连接点:方法开始时、结束时、正常运行完毕时、方法异常时等这些特殊的时机点,我们称之为连接点,项目中每个方法都有连接点,连接点是一种候选点

  • 切入点:指定AOP思想想要影响的具体方法是哪些,描述那些需要增强的方法

  • Advice 增强:

    • 第一个层次:指的是横切逻辑
    • 第二个层次:方位点(在某一些连接点上加入横切逻辑,那么这些连接点就叫方位点,描述的是具体的特殊时机)
  • Aspect切面:

    • 切面概念是对上述概念的一个综合

    • Aspect切面 = 切入点 + 增强

      ​ = 切入点(锁定方法)+ 方位点(锁定方法中的特殊时机)+ 横切逻辑

      众多的概念,目的就是为了锁定要在哪个地方插入什么横切逻辑代码

Spring中AOP的代理选择

Spring 实现AOP思想使用的是动态代理技术

默认情况下,Spring会根据被代理对象是否实现接口来选择JDK还是CGLIB。当代理对象没有实现任何接口时,Spring会选择CGLIB。当代理对象实现了接口,Spring会选择JDK动态代理。不过我们也可以通过配置的方式,让Spring强制使用CGLIB

xml方式:

<aop:aspectj-autoproxy proxy-target-class="true"/>

注解方式:

@EnableAspectJAutoProxy(proxyTargetClass = true)

一般情况下,使用默认让spring自动选择即可。

Spring中AOP的配置方式

在Spring的AOP配置中,也和IoC配置一样,支持三种配置方式

  • 使用XMl配置
  • 使用XML+注解配置
  • 使用纯注解配置

Spring中AOP的实现

打印日志需求:把打印日志的代码逻辑织入service层中

XML模式

  • 引入AOP依赖

    <!-- 引入AOP -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    
  • 切面类LogUtils:

    package com.test.utils;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class LogUtil {
    
        public void beforeLog() {
            System.out.println("前置通知...");
        }
    
        public void afterReturningLog(){
            System.out.println("正常执行通知...");
        }
    
        public void afterLog() {
            System.out.println("后置通知...");
        }
    
        public void afterThrowingLog() {
            System.out.println("抛出异常通知...");
        }
    
        public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕通知1...");
            // 获取参数
            Object[] args = pjp.getArgs();
    
            System.out.println("环绕通知2...");
    
            // 调用方法
            Object result = pjp.proceed(args);
    
            System.out.println("环绕通知3...");
            return result;
    
    
        }
    
    }
    
  • applicationContext.xml配置

    在xml配置文件约束加上aop的约束

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        ......
    
        <!--把通知bean交给spring来管理-->
        <bean id="logUtil" class="com.test.utils.LogUtil"></bean>
        <!-- 开始AOP配置 -->
        <aop:config>
            <!-- 配置切面 -->
            <aop:aspect id="logAdvice" ref="logUtil">
                <!-- 切入点配置 -->
                <aop:pointcut id="pt1" expression="execution(public void com.test.service.impl.TransferServiceImpl.transfer(java.lang.String, String, int))"/>
                <!-- 配置前置通知 -->
                <aop:before method="beforeLog" pointcut-ref="pt1"/>
                <!-- 正常执⾏时通知 -->
                <aop:after-returning method="afterReturningLog" pointcut-ref="pt1"/>
                <!-- 配置异常通知 也可以直接指定切入点 -->
                <aop:after-throwing method="afterThrowingLog" pointcut="execution(public void com.test.service.impl.TransferServiceImpl.transfer(java.lang.String, String, int))"/>
                <!-- 配置最终通知,无论是否异常,都会触发 -->
                <aop:after method="afterLog" pointcut-ref="pt1"/>
                <!-- 环绕通知 -->
                <aop:around method="aroundLog" pointcut-ref="pt1"/>
            </aop:aspect>
        </aop:config>
    
        <aop:aspectj-autoproxy/>
    </beans>
    

    执行输出:

    前置通知...
    环绕通知1...
    环绕通知2...
    业务核心代码
    环绕通知3...
    最终通知...
    正常执行通知...
    

    上述配置实现了对 TransferServiceImpl#transfer 方法进行增强。

    • 关于切入点表达式:

      • 概念及作用

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

      • 关于AspectJ

        AspectJ是一个机遇Java语言的AOP框架,Spring框架从2.0版本之后就集成了AspectJ框架中切入点表达式的部分,开始支持AspectJ的切入点表达式。

      • 表达式使用示例

        public void com.test.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, int)
        访问修饰符 返回值 全限定方法名(参数类型列表)
        
        • 访问修饰符可以省略
        • 返回值可以用 * ,表示任意返回值 `*
        • 包名可以使用 . ,但是有几级包,必须写几个 void ...impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, int)
        • 包名可以使用 .. 表示当前包及其子包 * ..TransferServiceImpl.transfer(java.lang.String, java.lang.String, int)
        • 参数:基本类型直接写类型名称,引用类型需要写全限定类名
        • * 可以表示参数列表,但至少要有一个参数;.. 可以表示任意参数个数
    • 五种通知类型:

      • before前置通知

        • 执行时机是在切入点方法(业务核心方法)执行之前执行
        • 前置通知可以获取切入点方法的参数,并对其进行增强
      • after-returning正常执行通知

        • 执行时机是在切入点方法(业务核心方法)正常执行后执行
      • after-throwing异常通知

        • 执行时机是在切入点方法(业务核心方法)产生异常后执行,如果没有异常不会执行
      • after最终通知

        • 执行时机是在切入点方法(业务核心方法)执行完成之后,返回之前执行,无论是否产生异常,都会执行。
      • around环绕通知

        环绕通知有别于以上四种通知类型。前面四种通知它们都是指定何时增强的通知类型;而环绕通知,它是Spring框架为我们提供一种可以通过编码的方式,控制增强代码何时执行的通知类型。

        它里面借助的ProceedingJoinPoint接口及其实现类,实现手动自定义除服切入点方法的调用。

        • Object result = pjp.proceed(args);
          

        需要调用该方法,原代码才会执行,并且需要返回返回值

XML+注解模式

  • 在xml文件中配置

    <!-- 开启spring对注解aop的支持 -->
    <aop:aspectj-autoproxy/>
    
  • 修改切面类LogUtils

    @Component
    @Aspect
    public class LogUtil {
    
        /**
         * 提供切入点表达式
         */
        @Pointcut("execution(public void com.test.service.impl.TransferServiceImpl.transfer(java.lang.String, java.lang.String, int))")
        public void pointcut(){}
    
        @Before("pointcut()")
        public void beforeLog() {
            System.out.println("前置通知...");
        }
    
        @AfterReturning("pointcut()")
        public void afterReturningLog(){
            System.out.println("正常执行通知...");
        }
    
        @AfterThrowing("pointcut()")
        public void afterThrowingLog() {
            System.out.println("抛出异常通知...");
        }
    
        @After("pointcut()")
        public void afterLog() {
            System.out.println("最终通知...");
        }
    
        @Around("pointcut()")
        public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("环绕通知1...");
            // 获取参数
            Object[] args = pjp.getArgs();
    
            System.out.println("环绕通知2...");
    
            // 调用方法
            Object result = pjp.proceed(args);
    
            System.out.println("环绕通知3...");
            return result;
    
        }
    
    }
    

    首先要添加一个方法,使用**@Pointcut** 注解给后续的通知方法提供切入点表达式,然后只需把对应方法上添加对应的注解即可

纯注解模式

在上面xml+注解的基础上,只需用注解替换掉配置文件中的这行配置即可:

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

只需在配置类用注解替换即可

@Configuration
@ComponentScan("com.test")
@EnableAspectJAutoProxy //开启spring对注解AOP的⽀持
public class SpringConfiguration {
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中应用AOP(面向切面编程)思想的一种方式。它使用动态代理技术来实现。默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK动态代理还是CGLIB动态代理。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术。但是我们也可以通过配置的方式,让Spring强制使用CGLIB动态代理。 Spring AOP的配置方式有多种,包括XML模式、XML注解模式和纯注解模式。通过这些配置方式,我们可以将AOP的切面逻辑织入到我们的业务代码中,实现各种功能,比如事务管理等。 在Spring中,事务管理是AOP的一种常见应用Spring提供了多种事务管理器的实现,例如DataSourceTransactionManager和HibernateTransactionManager等。这些事务管理器实现了Spring的事务管理器核心接口,负责提供具体的事务实现策略。我们可以根据需要选择合适的事务管理器来管理数据库操作或其他事务。同时,Spring也提供了基于注解的声明式事务配置,通过在代码中使用注解来实现事务管理。 总之,Spring AOP可以应用在各种场景中,包括但不限于事务管理。通过AOP的切面编程思想,我们可以将一些横切逻辑代码(比如事务管理、日志记录等)与业务代码解耦,提高代码的可维护性和可扩展性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Spring AOP 应用](https://blog.csdn.net/weixin_44152160/article/details/125360176)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值