Spring AOP

简介: Spring AOP

一. 什么事AOP

AOP(Aspect Oriented Programming):面向切面编程,它是一种思想,它是对某一类事务的集中处理。比如用户登录权限的校验,再没学AOP之前,我们所有需要判断用户登录的界面(中的方法),都要各自实现或调用用户验证的方法,然而有了AOP之后,我们只需要再某一处配置一下,所有需要判断用户登录页面就都可以实现用户登录验证了,不需要每个方法都写相同的用户登录验证了。


而AOP 是一种思想,而Spring AOP 是一个框架,提供了一种对AOP 思想的实现,它们的关系和IoC 与 DI 类似

二. AOP 的组成

2.1 切面(Aspect)

切面由切点和通知组成,它既包含了横切逻辑的定义,也包含了连接点的定义

切面是包含了:通知,切点和切面的类,相当于 AOP 实现了某个功能的集合(定义一个事件:用户登录校验)

2.2 连接点(Join Point)

应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

连接点相当于需要被增强的某个 AOP 功能的所有方法(有可能触发切点的所有点:所有接口)

2.3 切点(Pointcut)

Pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述)来匹配 Join Point , 给满足规则的 Join Point 添加 Advice.


切点相当于保存了众多连接点的一个集合(定义用户登录拦截规则,哪些接口需要判断用户登录,哪些不判断)

2.4 通知(Advice)

AOP 执行的具体方法。定义了切面是什么,何时使用,其描述了切面要完成的工作。

Spring 切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:

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

三. Spring AOP 实现

Spring AOP 的实现步骤如下:

3.1 添加Spring AOP 框架支持

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 定义切面和切点

//表明此类为一个切面
@Aspect
@Component
public class UserAspect {
    //定义一个切点,这里使用AspectJ 表达式语法
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void poingcut(){}
}

其中pointcut 为空方法,它不需要有方法体,此方法名就是起到一个“标识”的作用,标识下面的通知方法具体指的是哪个切点(因为切点可能会有很多个)

切点表达式支持三种通配符:

  • *:匹配任意字符,只匹配一个元素(包类,或方法,方法参数)
  • ..:匹配任意字符,可以匹配多个元素,在表示类时,必须和 * 联合使用
  • +:表示按照类型匹配指定类的所有类,必须跟在类名后面,如 com.cad.Car+,表示继承该类的所有子类包括本身

切点表达式由切点函数组成,其中 execution() 是最常用的切点函数,用来匹配方法,语法为:

execution(<修饰符><返回类型><包.类.方法(参数)><异常>)

修饰符和异常通常可以省略,具体含义如下:

修饰符,一般省略

public 公共方法
* 任意

返回值,不能省略

void 没有返回值
String 返回值字符串
* 任意

com.gyf.crm 固定包
com.gyf.crm.*.service crm包下面子包任意(ex:com.guf.crm.staff.service)
com.gyf.crm.. crm包下面任意子包(包括自己)
com.gyf.crm.*.service.. crm包下任意子包,固定目录service,service目录任意包

UerService 指定类
*lmpl 以 lmpl 结尾
User* 以 User 开头
* 任意

方法名,不能省略

addUser 固定方法
add* 以 add 开头
*Do 以 Do 结尾
* 任意


参数

() 无参
(int) 一个整型
(int,int) 两个整型
(..) 任意参数


表达式示例

execution(* com.cad.demo.User.*(..)) 匹配User 类里的所有方法
execution(* com.cad.demo.User+.*(..)) 匹配该类的子类包括该类的所有方法
execution(* com.cad.*.*(..)) 匹配 com.cad 包下所有类的所有方法
execution(* com.cad..*.*(..)) 匹配 com.cad 包下、子孙包下所有类的所有方法
execution(* addUser(String,int)) 匹配 addUser 方法,且第一个参数类型是 String,第二个参数类型是 int

3.4 定义通知

    //前置通知
    @Before("pointcut()")
    public void doBefore(){
        System.out.println("执行了 doBefore 方法");
    }
    //后置通知
    @After("pointcut()")
    public void doAfter(){
        System.out.println("执行了 doAfter 方法");
    }
    //返回之后通知
    @AfterReturning("pointcut()")
    public void doAfterReturning(){
        System.out.println("执行了 doAfterReturning 方法");
    }
    //抛出异常之后通知
    @AfterThrowing("pointcut()")
    public void doAfterThrowing(){
        System.out.println("执行了 doAfterThrowing 方法");
    }
    //环绕通知(传入的参数类型 ProceedingJoinPoint 是固定的)
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        Object result=null;
        System.out.println("doAround 方法开始执行");
        try {
            result=joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("doAround 方法结束执行");
        return result;
    }
    @RequestMapping("/login.html")
    public String login(HttpServletRequest request){
        HttpSession session=request.getSession();
        session.setAttribute("username","张三");
        System.out.println("登录成功");
        return "login";
    }

访问后的打印结果:

doAround 方法开始执行

执行了 doBefore 方法

登录成功

执行了 doAfterReturning 方法

执行了 doAfter 方法

doAround 方法结束执行

四. Spring AOP 实现原理

Spring AOP 是构建在动态代理基础上,因此 Spring 对 SOP 的支持局限于方法级别的拦截

Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理.默认情况下,实现了接口的类,使用AOP 会基于JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类.二者都是基于反射实现的。

JDK 动态代理实现:

import org.example.demo.service.AliPayService;
        import org.example.demo.service.PayService;
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
//动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被代理类必须实现接⼝
public class PayServiceJDKInvocationHandler implements InvocationHandler {
    //⽬标对象即就是被代理对象
    private Object target;
    public PayServiceJDKInvocationHandler( Object target) {
        this.target = target;
    }
    //proxy代理对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.安全检查
        System.out.println("安全检查");
//2.记录⽇志
        System.out.println("记录⽇志");
//3.时间统计开始
        System.out.println("记录开始时间");
//通过反射调⽤被代理类的⽅法
        Object retVal = method.invoke(target, args);
//4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }
    public static void main(String[] args) {
        PayService target= new AliPayService();
//⽅法调⽤处理器
        InvocationHandler handler =
                new PayServiceJDKInvocationHandler(target);
//创建⼀个代理类:通过被代理类、被代理类实现的接⼝、⽅法调⽤处理器来创建
        PayService proxy = (PayService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{PayService.class},
                handler
        );
        proxy.pay();
    }
}

CGLIB 动态代理实现:

import org.springframework.cglib.proxy.Enhancer;
        import org.springframework.cglib.proxy.MethodInterceptor;
        import org.springframework.cglib.proxy.MethodProxy;
        import org.example.demo.service.AliPayService;
        import org.example.demo.service.PayService;
        import java.lang.reflect.Method;
public class PayServiceCGLIBInterceptor implements MethodInterceptor {
    //被代理对象
    private Object target;
    public PayServiceCGLIBInterceptor(Object target){
        this.target = target;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, Method
            Proxy methodProxy) throws Throwable {
//1.安全检查
        System.out.println("安全检查");
//2.记录⽇志
        System.out.println("记录⽇志");
//3.时间统计开始
        System.out.println("记录开始时间");
//通过cglib的代理⽅法调⽤
        Object retVal = methodProxy.invoke(target, args);
//4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }
    public static void main(String[] args) {
        PayService target= new AliPayService();
        PayService proxy= (PayService) Enhancer.create(target.getClass(),
                new PayServiceCGLIBInterceptor(target));
        proxy.pay();
    }
}

区别:


  • 出身不同,JDK Proxy 是来自于原生的jdk,而 CGLIB 是第三方库的
  • JDK Proxy 要求被代理类必须实现接口,CGLIB 可以不实现接口,可以通过实现代理类的子类来实现动态代理
  • CGLIB 动态代理无法代理 final 类和 final 方法;JDK 动态代理可以代理任意类
  • JDK 动态代理性能相对较高,生成代理对象速度较快;CGLIB 动态代理性能相对较低,生成代理对象速度较慢。
相关文章
|
2月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
6天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
3月前
|
Java Spring
在Spring Boot中使用AOP实现日志切面
在Spring Boot中使用AOP实现日志切面
|
20天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
安全 Java 开发者
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
31 1
|
2月前
|
Java Spring
Spring的AOP组件详解
该文章主要介绍了Spring AOP(面向切面编程)组件的实现原理,包括Spring AOP的基础概念、动态代理模式、AOP组件的实现以及Spring选择JDK动态代理或CGLIB动态代理的依据。
Spring的AOP组件详解
|
2月前
|
Java API Spring
Spring Boot 中的 AOP 处理
对 Spring Boot 中的切面 AOP 做了详细的讲解,主要介绍了 Spring Boot 中 AOP 的引入,常用注解的使用,参数的使用,以及常用 api 的介绍。AOP 在实际项目中很有用,对切面方法执行前后都可以根据具体的业务,做相应的预处理或者增强处理,同时也可以用作异常捕获处理,可以根据具体业务场景,合理去使用 AOP。
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
下一篇
无影云桌面