SpringAOP的理解

7 篇文章 0 订阅

1.概念

面向切面编程;AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等
特点:

  1. AOP利用一种称为"横切"的技术,将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。
  2. 使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。
  3. 横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

使用场景及作用:
对以上统一业务来说,常见的切面业务如:

  1. 响应统一的数据格式
  2. 统一异常处理
  3. 统一日志记录,如跟踪用户访问信息
  4. 用户统一会话管理、权限管理
  5. 方法执行时间计算
  6. 事务管理
  7. 其他
    对于使用AOP技术来说,解决的最大问题就是对横切业务的统一管理:
  8. 横切代码的高度复用,和业务代码互相独立,满足代码设计上的高内聚低耦合
  9. 系统更好的扩展性,可维护性

2.代理的了解

代理在实现的技术手段上,都是在 class 代码运行期,动态的织入字节码。
代理设计模式:分为静态代理和动态代理:关键点有两个:

  1. 被代理类:原始类不进行任何修改,但创建和使用时,不再使用原始的被代理类,而是设计一个原始类的代理类
  2. 代理类:基于被代理类,构造一个代理类,在使用时,也是使用代理类;
    Spring 框架中的AOP,主要基于两种方式:JDK 及 CGLIB 的方式。这两种方式的代理目标都是被代理类中的方法,在运行期,动态的织入字节码生成代理类。
    1.CGLIB是Java中的动态代理框架,主要作用就是根据目标类和方法,动态生成代理类。
    2.Java中的动态代理框架,几乎都是依赖字节码框架(如 ASM,Javassist 等)实现的。
    字节码框架是直接操作class字节码的框架。可以加载已有的class字节码文件信息,修改部分信息,或动态生成一class
    两种方式的区别;
  3. JDK 实现,要求被代理类必须实现接口,之后是通过 InvocationHandler 及 Proxy ,在运行时动态的在内存中生成了代理类对象,该代理对象是通过实现同样的接口实现(类似静态代理接口实现的方式),只是该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。
  4. CGLIB 实现,被代理类可以不实现接口,是通过继承被代理类,在运行时动态的生成代理类对象。
    dd

3.SpringAOP中的实现

Spring AOP由 spring-aop 、 spring-aspects 和 spring-instrument 3 个模块组成。
SpringAOP只能实现方法拦截;不能拦截属性;但对于AOP来说两者都应该实现;

特点:
1.SpringAOP构建在动态代理基础上,因此Spring对AOP的支持局限于方法拦截。
2.SpringAOP支持 JDK 和 CGLIB 方式实现动态代理。默认情况下,实现了接口的类,使用 AOP 会基于JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。

关于AOP的术语:

切面:切面(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
/n
通知-Advice:切面也是有目标的它必须完成的工作。在AOP术语中,切面的工作被称之为通知。定义了切面是什么,何时使用,其描述了切面要完成的工作,还解决何时执行这个工作的问题。Spring切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用;
/n
切点-pointcut:Pointcut是匹配 Join Point 的谓词。在 Spring 中, 所有的方法都可以认为是 Join Point,但是我们并不希望在所有的方法上都添加 Advice,而 Pointcut 的作用就是提供一组规则(使用 AspectJpointcut expression language 来描述)来匹配Join Point,给满足规则的 Join Point 添加 Advice。
&/n
连接点-joinpoint:应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
在这里插入图片描述

SpringAOP中的几种通知:

  1. 前置通知 使用@Before:通知方法会在目标方法调用之前执行。
  2. 后置通知 使用@After:通知方法会在目标方法返回或者抛出异常后调用。
  3. 返回之后通知 使用@AfterReturning:通知方法会在目标方法返回后调用。
  4. 抛异常后通知 使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
  5. 环绕通知 使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行 为。

4. 代码实现

关于切点表达式说明

@AspectJ 支持三种通配符

  • :匹配任意字符,只匹配一个元素(包,类,或方法,方法参数)
    … :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使用。
  • :表示按照类型匹配指定类的所有类,必须跟在类名后面,如 com.cad.Car+ ,表示继承该类的所有
    子类包括本身
    切点表达式由切点函数组成,其中 execution() 是最常用的切点函数,用来匹配方法,语法为:
    execution(<修饰符><返回类型><包.类.方法(参数)><异常>);修饰符和异常可以省略。
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")

这就表示拦截到ccom.example.demo.controller.UserController底下的所有方法;

实现切面和通知方法:

首先写几个示例对象用于设置切面:

package com.example.demo.controller;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public int login(String username,String password){
        System.out.println("进入了login的方法");
        return 0;
    }
    @RequestMapping("/regin")
    public int regin(String username,String password){
        System.out.println("执行了注册的方法");
        return 0;
    }
}

接下来设置切面:

package com.example.demo.pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class Userpointcut {
    //1.定义切面规则
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")//这里设置的是切面的位置,
    public void pointcut(){

    }

    //设置通知方法:
    @Before("pointcut()")
    public void doBefore(){
        System.out.println("执行了前置通知");
    }
    @After("pointcut()")
    public void doAfter(){
        System.out.println("执行了后置通知");
    }
    @AfterReturning("pointcut()")
    public void doReturning(){
        System.out.println("执行了返回之前通知的方法");
    }
    @AfterThrowing("pointcut()")
    public void doThrowing(){
        System.out.println("执行了抛出异常之前的通知方法");
    }
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        System.out.println("进入了环绕通知,方法执行之前");
        Object result = null;
        try{
            //执行被代理的方法;


            result= joinPoint.proceed();


        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("执行环绕之后");
        return result;
    }
}

测试:

在这里插入图片描述
从测试结果我们可以总结其执行的顺序:
Around ->before->切入点的方法->AfterReturning->After->Around

5.织入的了解

织入:织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:

  1. 编译期:
    切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的
  2. 类加载器:
    切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入(load-time weaving. LTW)就支持以这种方式织入切面。
  3. 运行期
    切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。SpringAOP就是以这种方式织入切面的
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值