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

文章目录

  • 1.SpringAop(Aspect Oriented Programming)是什么?
      • 1.1springaop的概念
      • 1.2 SpringAOP的底层原理
      • 1.3SpringAop实现的技术内容
  • AOP代理模式
    • 2.1 AOP动态代理技术
      • 2.1.1 Jdk动态代理的实现原理
      • 2.1.2 GGLib动态代理的原理
        • jdk动态代理的代码实现
    • 3.AOP的相关概念
      • 3.1连接点的小知识补充
    • AOP在开发中的使用
      • 4.1XML配置AOP的详解
    • 4.2重点讲解我们用的注解AOP
      • 4.2.1AOP详解注解配置
    • 5.需求的实现:需要验证有权限的用户才能调用该接口代码实现

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动态代理技术(非接口,基于父类)

SpringAOP超详细图文解释(深入学习笔记可用于面试)_第1张图片
在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动态代理的代码实现

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

  • 基于反射
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第3张图片

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

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

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

SpringAOP超详细图文解释(深入学习笔记可用于面试)_第5张图片
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第6张图片
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第7张图片

总之,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)增强后,就产生一个结果代理类
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第8张图片

3.1连接点的小知识补充

AOP中的Joinpoint可以有多种类型:**构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。**也就是说在AOP的概念中我们可以在上面的这些Joinpoint上织入我们自定义的Advice,但是在Spring中却没有实现上面所有的joinpoint,确切的说,Spring只支持方法执行类型的Joinpoint。
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第9张图片

AOP在开发中的使用

4.1XML配置AOP的详解

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

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

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

SpringAOP超详细图文解释(深入学习笔记可用于面试)_第11张图片
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第12张图片
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第13张图片

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

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

  • 1.创建目标接口和目标类:(基于JDK接口代理)
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第15张图片
  • 2.创建切面类
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第16张图片
  • 3.将目标类和切面类交由spring进行管理(@component是将前面xml的注进来)
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第17张图片
  • 4.在切面类使用注解@Aspect
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第18张图片
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第19张图片
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第20张图片

4.2.1AOP详解注解配置

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

这里我建议要搭配xml进行理解
SpringAOP超详细图文解释(深入学习笔记可用于面试)_第22张图片

  • 比如,这里我们定义的一个MyAspect类,里面有切点(pointcut)方法,用到的是@PointCut(…)
  • 其次,我们的**@Before也就是我们的通知类(Advice)也就是增强方法,里面是包含我们的pointcut(包含我们要对那些类进行增强)的**
  • 在往外看,这整个就是一个Aspect切面
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第23张图片
  • IDEA中的注解开发
    SpringAOP超详细图文解释(深入学习笔记可用于面试)_第24张图片

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();
    }

你可能感兴趣的:(SpringBoot项目,java,spring,boot,spring,代理模式)