Spring AOP 技术详解

学完AOP技术,真的不得不佩服发明AOP技术的人,真是个天才!!!

想要了解AOP,首先要了解什么叫代理。

1.代理

我们先来说说什么叫做代理:

        假如我要买辆奥迪A7,我不会直接去长春一汽工厂去买,为啥?路途远,而且,如果以这样方式购买,一手交钱,一手交车,事先没人给我介绍车辆配置情况,发动机型号等等,买完之后的后续维修等等都是没有人给我介绍的。当然,这些都是基于假设情况,现实肯定不能这样。那我应该去哪买奥迪A7呢?4S店。车是长春一汽生产的,卖是4S店卖的,这就是代理。4S店会买前给我介绍车辆的详细情况,卖完给我提供售后保障。如果说卖车是目标方法,那么这就叫目标方法的增强。

1.1静态代理

        静态代理,麻烦之处在于,我要创建多个代理类,就好比,我卖奥迪就开一个奥迪4S店,卖奔驰就开个奔驰4S店,那岂不是全国各地都开满了4S店?假设所有4S店服务也都是买车前介绍车辆详情,买后都是提供维修保障。它们的服务是一样的,这样就出现了代码冗余的情况。

1.2动态代理

        动态代理,好处就在于我只需要创建一个代理类(切面类),也就是我只开一家4S店,我可以销售各种车辆,我卖奥迪,就买前介绍奥迪车辆详情,买后提供奥迪售后保障。我卖奔驰,就买前介绍奔驰车辆详情,买后提供奔驰售后保障。同样能起到方法增强的作用,但是大大减少了代码的冗余。

2.AOP简介

        AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

作用:在不修改源码的基础上,对已有方法(目标方法)进行增强。

实现原理:动态代理技术。

优势:减少重复代码、提高开发效率、维护方便应用场景:事务处理、日志管理、权限控制、异常处理等方面。

3.AOP框架

        AOP是一种思想,是一种面向切面编程的思想,AOP的主要作用就是,告诉我们可以通过动态代理的方式,织入一段代码到已经写到另一段代码中去。

SpringAop 和 AspectJ 是两个框架,它主要就是通过AOP的思想,来实现面向切面编程。

因为动态代理的方式,不止一种,所以也出现了两个框架,其实实现的作用都是一样的。

但还是有区别:

SpringAOP是动态织入、AspectJ是静态织入 (静态织入的意思就是:运行AspectJ的代码,将需要被面向切面编程的代码生成出来,变成class文件。)

他们的核心就是: 动态代理。

Spring整合AspectJ很麻烦,有很多配置,那是因为AspectJ先出来,后来spring想,让你来和我的spring整合多麻烦呀,我自己写一个AOP的实现,所以出现了SpringAop,最开始Spring其实也实现了aop,不过实现的不好,后来看AspectJ这个代码写的不错,然后就借用了AspectJ的语法,又实现了一遍,所以springaop其实是依赖AspectJ的,只不过依赖之后,配置简化了。

4.AOP术语

cea3a199a5604b7593ccc002b15be98e.png

ccefca1e88ae472ba57c84002aca9318.png

fc73d520e7fd4655b068a456f7e086e8.png

以上这些术语中,我们要首先理解,什么叫目标对象,这个就好比一汽工厂,目标类里面能被拦截的方法都叫连接点,什么是切入点呢?就是要被增强的方法。说到拦截,就要说到切点表达式了

切点表达式(重点)

切点表达式负责拦截连接点,这个什么意思呢?程序开启,方法一个个执行,我想在某个或者某些方法上进行增强,首先我得先拦截到这些方法(也就是找到符合条件的方法),然后进行织入。

9760927fcc57454e8cbbb461f1ca814b.png

5.通知类型

9f444ccec43f423d932cd1dbfd04e6a8.png

通知执行顺序

27e68548fe454203857381d2410621df.png

目标类

package com.icss.zrgj;

import org.springframework.stereotype.Repository;

@Repository("carFactory")
public class CarFactory {
//   这是目标方法Target

    //    目标方法中的方法被称为连接点(jionpoint)
    public void sellCar() {
        System.out.println("长春一汽正在售卖一汽奥迪A7");
    }

    public int xinsuai() {
       int a=1000;
       int b=2536;
       int c=a+b;
        System.out.println("我返回一个C");
        return c;
    }

    //    以下也是一个连接点
    public void zhaoping() {
        System.out.println("长春一汽正在招聘销售人员");
        System.out.println(1/0);
    }

}

切面类

package com.icss.zrgj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//这是一个切面类
public class MyAspect {
/*在切面类中可定义多个方法*/

//    这个JoinPoint jionpoint 是目标方法的对象,可以用来获取目标对象的信息
    /*例如:目标方法所属于的类
    * 目标方法的名字
    * 目标方法的参数列表
    * 目标方法访问修饰符等
    * 目标方法的返回结果(不通用)
    * 目标方法的异常信息(不通用)
    * */
    public void before(JoinPoint jionpoint){
//         目标方法还未执行,后两个不适用
        System.out.println("这是前置通知");
    }
//     这两个参数分别是          ↓目标方法对象       ↓目标方法返回值
//          ↓关于切面类里面的方法返回值,我们只知道返回了,但是返回哪了不知道
    public int afterreturn(JoinPoint jionpoint,int h){
        System.out.println(h);
        System.out.println("这是后置返回通知");
        return  555;
    }
    public void after(){
        System.out.println("这是最终通知");
    }
    public void exception(JoinPoint jionpoint,Exception e){
        System.out.println(e);
    }
    /*
    * 环绕通知比较特殊它的参数类型叫ProceedingJoinPoint其实感觉意义一样*/
    public void arount(ProceedingJoinPoint jionpoint) throws Throwable {
        System.out.println("环绕前");
        jionpoint.proceed();
        System.out.println("环绕后");
    }
}

配置文件

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--    扫描此包下的注解-->
    <context:component-scan base-package="com.icss.zrgj"/>
    <!--    扫描配置文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="myaspect" class="com.icss.zrgj.MyAspect"/>
    <aop:config>
        <aop:pointcut id="is" expression="execution(public * com.icss.zrgj.CarFactory.* (..))"/>
        <aop:aspect ref="myaspect">
            <aop:before method="before" pointcut-ref="is"/>
            <aop:after method="after" pointcut-ref="is"/>
<!--            这个returning是绑定方法afterreturn的参数名字(它俩名字要一样),是目标方法的返回值(afterreturn方法的第二个参数是目标方法的返回值类型)-->
            <aop:after-returning method="afterreturn" returning="h" pointcut-ref="is"/>
            <aop:after-throwing method="exception" throwing="e" pointcut-ref="is"/>
            <aop:around method="arount" pointcut-ref="is"/>
        </aop:aspect>
    </aop:config>

</beans>

6.AOP代码执行过程

首先要有目标类,这个目标类可以有接口,也可以没有接口。如果有接口,底层就用JDK动态代理,如果没有接口,就用CGLIB动态代理。

接着我们要创建切面类(代理类)。

在配置文件中,首先我们要创建切面类对象,接着配置切入点和切面类。前者告诉spring,我拦截到那些方法作为切入点,后者告诉spring哪个类是切面类和哪些方法是切入点,我要织入切面类里的是哪个方法。

代理类中的方法如果有返回值的话,返回哪里了,这个不知道

在测试类中,再次执行目标方法,这个方法已经不是单纯的原来的方法了,这是增强后的目标方法,如果方法有返回值,返回目标方法的调用处。但是代理类中的通知(增强)方法,可以通过参数获取这个返回值。

注意:  目标方法的返回值  和  代理类中方法的参数的关系(通过代理类中方法的参数,可以获得目标方法的返回值)

  • 35
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐晓率

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值