深度分析SpringAOP,一文带你彻底搞懂SpringAOP底层原理!

·  阅读 799

SpringAOP

我们为什么要使用AOP(面向切面编程)?当我们在现实中完成实际的项目时,我们总是需要在一个“动作”进行前,进行中,或进行后进行一些操作,比如当我们在运行程序时,我们想要进行日志保存,或者在每一个方法调用后输出一句话,这就表示我们每一次进行一个“动作”都需要进行同样的操作,这就导致程序员会进行大量的、无用的重复性动作,面对这种情况,AOP应运而生。

AOP概述

AOP,即Aspect Oriented Rrogramming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能统一维护的一种技术。AOP可以对业务漏极的各个部分进行隔离,从而使得业务逻辑之间得耦合性降低,提高程序得可重用性,同时提高了开发得效率。

AOP和OOP是两种不同的设计思想。OOP(面向对象编程)针对业务处理过程得实体及其属性和行为进行抽象封装,获得清晰高效得逻辑单元划分。AOP则是针对业务处理过程中得切面进行提取,是面对业务处理过程中的某个步骤或阶段,获得逻辑过程中各部分之间低耦合性得隔离效果。

面向切面编程的好处就是:减少重复,专注业务。它是面向对象编程的一种补充。 个人整理了一些资料,有需要的朋友可以直接点击领取。

Java基础知识大全

22本Java架构师核心书籍

从0到1Java学习路线和资料

1000+道2021年最新面试题

核心原理及使用案例

原理:使用动态代理的方式在执行方法前后或出现异常时加入相关的逻辑。

使用:

事务处理:开启事务,关闭事务,出现异常回滚事务.....
权限判断:执行方法前,判断是否具有权限;
日志处理;
......
事务处理:开启事务,关闭事务,出现异常回滚事务.....
权限判断:执行方法前,判断是否具有权限;
日志处理;
......
复制代码

AOP的基本概念(Spring的专业术语)

0.增强:向各个程序内部注入一些逻辑代码从而增强原有程序的功能。

1.连接点(JoinPoint):类中可以被增强的方法,这个方法就就被称为连接点,切记连接点并不是一定会被增强。

2.切入点(Pointcut):类中实际被增强的方法。

3.通知(Advice):指一个切面在特定的连接点要做的事情,简单来说就是“增强”。可以分为方法执行前通知,方法执行后通知,环绕通知等等。

4.切面(Aspect):把通知添加到切入点的过程就叫切面。

5.目标(Target):代理的目标对象,即要增强的方法所在的类。

6.代理(Proxy):向目标对象应用通知之后创建的代理对象。

SpringAOP实现

很多的框架都对AOP这种编程思想进行了实现。Spring只是其中的一种,可以完成面向切面编程。AspectJ也是一个面向切面的框架,并且实现方式更为简捷,更为方便,并且支持注解式开发。所以,Spring又将AspectJ对于AOP的实现引入到自己的框架之中。

Spring中使用AOP开发时,通常使用AspectJ的实现方式。其中常用的通知有五种类型:

  • 前置通知:方法执行前执行;
  • 后置通知:方法执行后执行;
  • 环绕通知:前后都执行;
  • 异常通知:出异常时通知;
  • 最终通知:如return后执行。

SpringAOP的使用

导入实现AOP的AspectJ的jar

<!--Spring实现AOP是依靠Aspects框架实现-->
<!--Aspects相关jar-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

复制代码

基于AspectJ的xml配置实现

所有的配置都在spring.xml文件中进行。

1.创建一个增强功能的类。

import org.aspectj.lang.ProceedingJoinPoint;
//通知(Advice):在连接点要做的事情
public class Aop {

    public void doLog() {
        System.out.println("=====保存日志=====");
    }

    public void commit() {
        System.out.println("=====提交事务=====");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("======方法前通知======");
        try {
            proceedingJoinPoint.proceed();//调用自己的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("======方法后通知======");
    }

    public void throwable(Throwable throwable) {
        System.out.println("======出异常了======");
        System.out.println(throwable.getMessage());
    }
}

复制代码

2.将装有增强功能的类交给交由spring管理

<?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:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--把装有通知(Advice)的类交给Spring管理-->
    <bean id="aop" class="com.cwd.spring4pro.demo1.aop.Aop"></bean>
    
    <!--在这里进行织入,即切面(Aspect):将通知添加到切入点-->

</beans>

复制代码

3.配置切面(Aspect)

先准备一个被增强的类,即目标(Target)

import org.springframework.stereotype.Component;

//目标(Target):代理的目标对象,即要增强的类
@Component(value = "target")
public class Target {
    /*
    连接点(Joinpoint),可以被增强的方法
    切入点(pointcut),实际被增强的方法,被增强了
    */
    public void pointCut() {
        System.out.println("这是一个保存的操作!!!");
        return;
    }
}

复制代码

将通知添加到切入点。

<!--织入-->
    <aop:config>
        <!--
        配置切入点 
        execution表达式 前*表示返回值 saveUser(..)表示要增强的方法 ..表示参数
        -->
        
        <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>

        <!--配置通知 ref中引用的是通知类的id-->
        <aop:aspect ref="aop">
        
           <!--前置通知-->
           <aop:before method="doLog" pointcut-ref="pointCut"/>

        </aop:aspect>
    </aop:config>

复制代码

五种通知类型配置

1.前置通知

<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--前置通知-->
        <aop:before method="doLog" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

复制代码

2.后置通知

<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--后置通知-->
        <aop:after method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

复制代码

3.环绕通知

<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--环绕通知-->
        <aop:around method="around" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

复制代码

4.异常通知

public void pointCut() {
    System.out.println("这是一个保存的操作!!!");
    int a = 10 / 0;
    return;
}

复制代码
<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--异常通知-->
        <aop:after-throwing method="throwable" pointcut-ref="pointCut" throwing="throwable"/>
    </aop:aspect>
</aop:config>

复制代码

5.最终通知

<!--织入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--最终通知-->
        <aop:after-returning method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

复制代码

最终通知一般在return之后执行。

注解实现

开启aop注解扫描

<!--开启注解标签-->
<aop:aspectj-autoproxy/>

复制代码

在通知类中进行配置,如下所示:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component//将这个类交给Spring管理
@Aspect//标注这个类时装有通知的类
public class Aop {

    @Before("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public void doLog() {
        System.out.println("=====保存日志=====");
    }

    @After("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public void commit() {
        System.out.println("=====提交事务=====");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("======方法前通知======");
        try {
            proceedingJoinPoint.proceed();//调用自己的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("======方法后通知======");
    }


    @AfterThrowing(value = "execution(* com.cwd.spring4pro.demo.Target.pointCut(..))",throwing = "throwable")
    public void throwable(Throwable throwable) {
        System.out.println("======出异常了======");
        System.out.println(throwable.getMessage());
    }

    @AfterReturning("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public  void returnAfter() {
        System.out.println("======return后=====");
    }
}

复制代码

最后

都看到这里了,觉得文章对你有帮助的话记得点个赞,感谢支持!

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改