SpringAOP超详细图文解释(深入学习笔记可用于面试)

1.SpringAop(Aspect Oriented Programming)是什么?

我们听说过很多次关于springAop的问题,那么什么是AOP呢?怎样结合官方文档来学习这个呢?那么我们将从他的概念,底层原理进行深入分析,做到应对面试和深入理解的层级

1.1springaop的概念

  • AOP意思为面向切面编程和AOP比较像的一个词是我们的OOP,oop是面向对象编程,AOP是建立在OOP编程上的一种设计思想,而SpringAOP是实现AOP思想的主流框架.
  • AOP目的:对业务逻辑的各个部分进行隔离,在不改动代码的前提下进行功能的增强,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,通过是提高了开发效率;
  • 应用场景:各个模块的横向切入点,比如日志、权限控制等具体如下:
    1.在调用service具体一些业务方法的时候,想在前面打一些日志。
    2.通过前后两次取时间戳来减一下,来统计所有业务方法执行的时间。
    3.在调用某一类业务方法时,判断用户有没有权限。
    4.在一系列业务方法前后加上事务的控制。比如startTransaction、commitTransaction(模拟事务控制)。

1.2 SpringAOP的底层原理

  • 实际上AOP底层是通过Spring提供的动态代理技术实现的,在运行期间Spring通过动态代理技术动态的生成代理对象代理对象方法执行时进行增强功能的介入,在调用目标对象的方法,从而完成业务功能的增强

1.3SpringAop实现的技术内容

  • Spring框架监控切入点(Pointcut)方法的执行,一旦监控到切入点方法被运行,使用代理机制(proxy),动态创建目标对象的代理对象,根据通知类别(advice),在代理对象的对应位置将通知对应的功能织入完成完整代码逻辑运行

AOP代理模式

  • 代理有两个,分别是动态代理和静态代理,SpringAOP是动态代理,而AspectJ是静态代理实现的AOP

2.1 AOP动态代理技术

  • 上述提到我们的AOP是基于我们动态代理实现的,接下来我们看看AOP中的两种代理技术
  • JDK动态代理(基于接口实现) cglib动态代理技术(非接口,基于父类)

在这里插入图片描述
在Spring里可以把一个类型注册成Spring里的一个Bean,这时候Spring就会帮我们把这个Bean初始化,变成一个可用的对象。加入我们需要在上面做一些增强,就是我们所谓的AOP。这时候我们就需要在中间加一层代理类或者增强类。

2.1.1 Jdk动态代理的实现原理

jdk动态代理,基于Java的反射机制实现,必须有接口才能使用该方法生成代理对象。JDK代理主要涉及到了两个类;Java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy。这两个类的主要方法如下

  • 大致流程是实现InvactionHandle接口创建方法调用器,通过Proxy类指定classLoader对象和一组interface创建动态代理,通过反射获取动态代理类的构造函数,参数类型就是调用器接口类型,通过构造函数创建动态代理类实例,构造时调用处理器接口类型。
  • JdkProxy假如说这个对象所在的类上面有接口(基于接口来做的),Spring会默认使用JdkProxy(JDK的动态代理),来生成一个代理,在代理里进一步的把所有对这个类做的增强操作,放到代理执行的代码里面。然后先做了增强的操作,再去调用原本的类的他的方法。

2.1.2 GGLib动态代理的原理

  • CGLIB 代理的工作原理:利用ASM开源包,将对象类的class文件加载进来,通过修改字节码生成的子类来进行处理;在这个子类里,当我们调用原先这个类的某个方法时,先做增强操作,再去调原本类的方法,最后再把结果返回回来
jdk动态代理的代码实现

在这里插入图片描述

  • 基于反射
    在这里插入图片描述

在这里插入图片描述

  • proxyTargetClass:如果要代理的类有接口但想强制不用默认JDK的动态代理,也是用字节码增强的技术,就可以开启proxyTargetClass选项。同CGlib。

  • CGlib:假如说要增强或代理的这个类没有接口,只有一个类的定义,Spring会默认使用CGlib,对他做字节码增强。相当于硬生生的给他生成一个子类。在这个子类里,当我们调用原先这个类的某个方法时,先做增强操作,再去调原本类的方法,最后再把结果返回回来。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总之,Spring AOP面向切面的增强功能,都是作用在方法上的!

3.AOP的相关概念

在正式讲解AOP的操作之前,我们需要理解AOP的相关术语,常用的如下

  • Target(目标对象): 代理的目标对象
  • JoinPoint(连接点):所谓连接点是指那些要被拦截到的点,在spring中这些点指的方法,因为spring只支持方法类型的连接点;典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point
  • Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行操作,是Joinpoint的集合体可以理解成
  • Advice(通知、增强):所谓通知是拦截到joinpoint之后要做的事情就是通知
  • Aspect(切面): 是指pointCut与Advice的结合体
  • Weaving(织入) :是指把增强(advice)应用到目标对象(target)来创建新的代理对象的过程;spring采用动态代理织入;但AspectJ采用的是编译期织入和类加载时期织入(weaving)
  • Proxy(代理): 一个类AOP织入(weaving)增强后,就产生一个结果代理类
    在这里插入图片描述

3.1连接点的小知识补充

AOP中的Joinpoint可以有多种类型:**构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。**也就是说在AOP的概念中我们可以在上面的这些Joinpoint上织入我们自定义的Advice,但是在Spring中却没有实现上面所有的joinpoint,确切的说,Spring只支持方法执行类型的Joinpoint。
在这里插入图片描述

AOP在开发中的使用

4.1XML配置AOP的详解

  • 关于pointcut的example:其中(.)代表任意,流程是:返回类型.哪个包下的.哪个类.哪个方法.哪个参数`
<aop:pointCut id = "sakura" expression = "execution com.itheima.cast.*.*(..)"></aop:pointcut>

在这里插入图片描述

  • 通知单配置语法
<aop:通知类型 method = "切面类中方法名" pointcut = "切点表达式" ></aop:通知类型>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2重点讲解我们用的注解AOP

在这里插入图片描述

  • 1.创建目标接口和目标类:(基于JDK接口代理)
    在这里插入图片描述
  • 2.创建切面类
    在这里插入图片描述
  • 3.将目标类和切面类交由spring进行管理(@component是将前面xml的注进来)
    在这里插入图片描述
  • 4.在切面类使用注解@Aspect
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4.2.1AOP详解注解配置

在这里插入图片描述

这里我建议要搭配xml进行理解
在这里插入图片描述

  • 比如,这里我们定义的一个MyAspect类,里面有切点(pointcut)方法,用到的是@PointCut(…)
  • 其次,我们的**@Before也就是我们的通知类(Advice)也就是增强方法,里面是包含我们的pointcut(包含我们要对那些类进行增强)的**
  • 在往外看,这整个就是一个Aspect切面
    在这里插入图片描述
  • IDEA中的注解开发
    在这里插入图片描述

5.需求的实现:需要验证有权限的用户才能调用该接口代码实现

场景:我们B站用户根据等级不同,进行的权限不同,比如LV6可以进行购买大会员激活码,或者彩色弹幕等等…那么我们就可以采用AOP简化开发

-1.创建接口类的注解

@Component
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ApiLimitedRole {

    String[]limitedRoleCodeList()default{};
}
@Component
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface DataLimitedRole {


}
  • 2.创建切面类Aspect
@Aspect
@Component
public class ApiLimitedRoleAspect {
    @Autowired
    private UserSupport userSupport;

    @Autowired
    private UserRoleService userRoleService;

    @Pointcut("@annotation(com.imooc.bilibili.domain.annotation.ApiLimitedRole)")
    public void check(){
    }

    @Before("check() && @annotation(apiLimitedRole)")
    public void doBefore(JoinPoint joinPoint, ApiLimitedRole apiLimitedRole){
        Long userId = userSupport.getCurrentUserId();
        //获取用户权限集合
        List<UserRole> userRoleList = userRoleService.getUserRoleByUserId(userId);
        //获取到权限限制集合
        String[] limitedRoleCodeList = apiLimitedRole.limitedRoleCodeList();
        Set<String> limitedRoleCodeSet = Arrays.stream(limitedRoleCodeList).collect(Collectors.toSet());
        Set<String> roleCodeSet = userRoleList.stream().map(UserRole::getRoleCode).collect(Collectors.toSet());
        roleCodeSet.retainAll(limitedRoleCodeSet);
        if(roleCodeSet.size()>0){
            throw new ConditionalException("权限不足!");
        }
    }
}

@Aspect
@Component
public class DataLimitedRoleAspect {
    @Autowired
    private UserSupport userSupport;

    @Autowired
    private UserRoleService userRoleService;

    @Pointcut("@annotation(com.imooc.bilibili.domain.annotation.DataLimitedRole)")
    public void check(){
    }

    @Before("check()")
    public void doBefore(JoinPoint joinPoint){
        Long userId = userSupport.getCurrentUserId();
        //获取用户权限集合
        List<UserRole> userRoleList = userRoleService.getUserRoleByUserId(userId);
        //获取到权限限制集合
        Set<String> roleCodeSet = userRoleList.stream().map(UserRole::getRoleCode).collect(Collectors.toSet());
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if(arg instanceof UserMoment){
            UserMoment userMoment = (UserMoment)arg;
            String type = userMoment.getType();
            if(roleCodeSet.contains(AuthRoleConstant.ROLE_LV1 )&& !"0".equals(type)){
                throw new ConditionalException("参数异常");
               }
            }
        }
    }
}
  • 3.对需要实现AOP加载的方法上加上对应注解
    /**
     * 新增用户动态请求
     * @param userMoment
     * @return
     * @throws Exception
     */
    @ApiLimitedRole(limitedRoleCodeList = {AuthRoleConstant.ROLE_LV0})
    @DataLimitedRole
    @PostMapping("/user-moments")
    public JsonResponse<String> addUserMoments(@RequestBody UserMoment userMoment) throws Exception{
        Long userId = userSupport.getCurrentUserId();
        userMoment.setUserId(userId);
        userMomentsService.addUserMoments(userMoment);
        return JsonResponse.success();
    }
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李知恩真爱粉

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

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

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

打赏作者

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

抵扣说明:

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

余额充值