Spring AOP与代理类的执行顺序是什么

发布时间:2023-03-27 15:14:16 作者:iii
来源:亿速云 阅读:139

Spring AOP与代理类的执行顺序是什么

1. 引言

Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它允许开发者通过切面(Aspect)来模块化横切关注点(Cross-cutting Concerns),如日志记录、事务管理、安全性等。AOP的核心思想是将这些横切关注点从业务逻辑中分离出来,从而提高代码的可维护性和可重用性。

在Spring AOP中,代理类(Proxy)是实现AOP的关键。Spring通过动态代理技术(如JDK动态代理或CGLIB代理)来创建代理类,从而在目标方法执行前后插入切面逻辑。理解Spring AOP与代理类的执行顺序对于正确使用AOP至关重要。

本文将深入探讨Spring AOP与代理类的执行顺序,包括AOP的基本概念、代理类的创建过程、切面的执行顺序以及如何通过配置和代码来控制这些顺序。

2. Spring AOP的基本概念

2.1 切面(Aspect)

切面是AOP中的一个核心概念,它是对横切关注点的模块化。切面通常包含一组通知(Advice)和切点(Pointcut)。通知定义了在何时执行切面逻辑,而切点定义了在何处执行切面逻辑。

2.2 通知(Advice)

通知是切面中的具体逻辑,它定义了在目标方法执行的不同阶段执行的代码。Spring AOP支持以下几种通知类型:

2.3 切点(Pointcut)

切点定义了在哪些方法上应用通知。切点通常使用表达式来匹配目标方法,如方法名、参数类型、返回类型等。

2.4 连接点(Join Point)

连接点是程序执行过程中的一个点,如方法调用、方法执行、异常抛出等。切点匹配的连接点就是通知执行的地方。

2.5 目标对象(Target Object)

目标对象是被代理的对象,即包含业务逻辑的对象。AOP通过代理类在目标对象的方法执行前后插入切面逻辑。

2.6 代理类(Proxy)

代理类是Spring AOP实现AOP的关键。Spring通过动态代理技术创建代理类,代理类在目标方法执行前后插入切面逻辑。Spring支持两种代理方式:JDK动态代理和CGLIB代理。

3. 代理类的创建过程

3.1 JDK动态代理

JDK动态代理是基于接口的代理方式,它要求目标对象实现一个或多个接口。JDK动态代理通过java.lang.reflect.Proxy类创建代理对象,代理对象实现了目标对象的所有接口,并在方法调用时插入切面逻辑。

3.1.1 创建过程

  1. 定义接口:目标对象必须实现一个或多个接口。
  2. 实现接口:目标对象实现接口中的方法。
  3. 创建InvocationHandler:实现java.lang.reflect.InvocationHandler接口,定义在方法调用时执行的逻辑。
  4. 创建代理对象:使用Proxy.newProxyInstance()方法创建代理对象。

3.1.2 示例代码

public interface UserService {
    void addUser(String username);
}

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
}

public class UserServiceProxy implements InvocationHandler {
    private Object target;

    public UserServiceProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置通知");
        Object result = method.invoke(target, args);
        System.out.println("后置通知");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new UserServiceProxy(target)
        );
        proxy.addUser("Alice");
    }
}

3.2 CGLIB代理

CGLIB代理是基于类的代理方式,它不要求目标对象实现接口。CGLIB通过继承目标类并重写其方法来实现代理。

3.2.1 创建过程

  1. 定义目标类:目标类不需要实现接口。
  2. 创建MethodInterceptor:实现org.springframework.cglib.proxy.MethodInterceptor接口,定义在方法调用时执行的逻辑。
  3. 创建代理对象:使用Enhancer类创建代理对象。

3.2.2 示例代码

public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
}

public class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置通知");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("后置通知");
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new UserServiceInterceptor());
        UserService proxy = (UserService) enhancer.create();
        proxy.addUser("Alice");
    }
}

4. 切面的执行顺序

在Spring AOP中,切面的执行顺序由多个因素决定,包括通知类型、切面的优先级以及切面的配置方式。理解这些因素对于控制切面的执行顺序至关重要。

4.1 通知类型的执行顺序

不同类型的通知在目标方法执行的不同阶段执行,它们的执行顺序如下:

  1. 前置通知(Before Advice):在目标方法执行之前执行。
  2. 环绕通知(Around Advice):在目标方法执行前后都执行,可以控制目标方法的执行。
  3. 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否成功执行。
  4. 返回通知(After Returning Advice):在目标方法成功执行并返回结果后执行。
  5. 异常通知(After Throwing Advice):在目标方法抛出异常后执行。

4.2 切面的优先级

当多个切面应用于同一个目标方法时,切面的执行顺序由切面的优先级决定。切面的优先级可以通过@Order注解或实现Ordered接口来指定。

4.2.1 使用@Order注解

@Order注解用于指定切面的优先级,值越小优先级越高。

@Aspect
@Order(1)
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
@Order(2)
public class SecurityAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

4.2.2 实现Ordered接口

实现Ordered接口并重写getOrder()方法也可以指定切面的优先级。

@Aspect
public class LoggingAspect implements Ordered {
    @Override
    public int getOrder() {
        return 1;
    }

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
public class SecurityAspect implements Ordered {
    @Override
    public int getOrder() {
        return 2;
    }

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

4.3 切面的配置方式

切面的配置方式也会影响切面的执行顺序。Spring AOP支持XML配置和注解配置两种方式。

4.3.1 XML配置

在XML配置中,切面的执行顺序由切面在配置文件中的顺序决定。

<aop:config>
    <aop:aspect id="loggingAspect" ref="loggingAspectBean" order="1">
        <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
    </aop:aspect>
    <aop:aspect id="securityAspect" ref="securityAspectBean" order="2">
        <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
    </aop:aspect>
</aop:config>

4.3.2 注解配置

在注解配置中,切面的执行顺序由@Order注解或Ordered接口决定。

@Aspect
@Order(1)
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
@Order(2)
public class SecurityAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

5. 代理类的执行顺序

在Spring AOP中,代理类的执行顺序由多个因素决定,包括代理类型、切面的优先级以及切面的配置方式。理解这些因素对于控制代理类的执行顺序至关重要。

5.1 代理类型的执行顺序

Spring AOP支持两种代理类型:JDK动态代理和CGLIB代理。它们的执行顺序略有不同。

5.1.1 JDK动态代理

JDK动态代理是基于接口的代理方式,它要求目标对象实现一个或多个接口。在JDK动态代理中,代理类的执行顺序如下:

  1. 前置通知(Before Advice):在目标方法执行之前执行。
  2. 环绕通知(Around Advice):在目标方法执行前后都执行,可以控制目标方法的执行。
  3. 目标方法:执行目标方法。
  4. 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否成功执行。
  5. 返回通知(After Returning Advice):在目标方法成功执行并返回结果后执行。
  6. 异常通知(After Throwing Advice):在目标方法抛出异常后执行。

5.1.2 CGLIB代理

CGLIB代理是基于类的代理方式,它不要求目标对象实现接口。在CGLIB代理中,代理类的执行顺序如下:

  1. 前置通知(Before Advice):在目标方法执行之前执行。
  2. 环绕通知(Around Advice):在目标方法执行前后都执行,可以控制目标方法的执行。
  3. 目标方法:执行目标方法。
  4. 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否成功执行。
  5. 返回通知(After Returning Advice):在目标方法成功执行并返回结果后执行。
  6. 异常通知(After Throwing Advice):在目标方法抛出异常后执行。

5.2 切面的优先级

当多个切面应用于同一个目标方法时,切面的执行顺序由切面的优先级决定。切面的优先级可以通过@Order注解或实现Ordered接口来指定。

5.2.1 使用@Order注解

@Order注解用于指定切面的优先级,值越小优先级越高。

@Aspect
@Order(1)
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
@Order(2)
public class SecurityAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

5.2.2 实现Ordered接口

实现Ordered接口并重写getOrder()方法也可以指定切面的优先级。

@Aspect
public class LoggingAspect implements Ordered {
    @Override
    public int getOrder() {
        return 1;
    }

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
public class SecurityAspect implements Ordered {
    @Override
    public int getOrder() {
        return 2;
    }

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

5.3 切面的配置方式

切面的配置方式也会影响代理类的执行顺序。Spring AOP支持XML配置和注解配置两种方式。

5.3.1 XML配置

在XML配置中,切面的执行顺序由切面在配置文件中的顺序决定。

<aop:config>
    <aop:aspect id="loggingAspect" ref="loggingAspectBean" order="1">
        <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
    </aop:aspect>
    <aop:aspect id="securityAspect" ref="securityAspectBean" order="2">
        <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
    </aop:aspect>
</aop:config>

5.3.2 注解配置

在注解配置中,切面的执行顺序由@Order注解或Ordered接口决定。

@Aspect
@Order(1)
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("LoggingAspect: 前置通知");
    }
}

@Aspect
@Order(2)
public class SecurityAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice() {
        System.out.println("SecurityAspect: 前置通知");
    }
}

6. 总结

Spring AOP通过代理类实现AOP,代理类的执行顺序由多个因素决定,包括代理类型、切面的优先级以及切面的配置方式。理解这些因素对于正确使用AOP至关重要。

在Spring AOP中,切面的执行顺序由通知类型、切面的优先级以及切面的配置方式决定。不同类型的通知在目标方法执行的不同阶段执行,切面的优先级可以通过@Order注解或实现Ordered接口来指定,切面的配置方式也会影响切面的执行顺序。

代理类的执行顺序由代理类型、切面的优先级以及切面的配置方式决定。JDK动态代理和CGLIB代理的执行顺序略有不同,切面的优先级和配置方式也会影响代理类的执行顺序。

通过正确配置切面的优先级和配置方式,开发者可以控制切面和代理类的执行顺序,从而实现更灵活和可维护的AOP编程。

推荐阅读:
  1. Spring Cloud Alibaba 使用 Feign+Sentinel 怎么完成熔断
  2. 怎么为Spring Cloud Gateway加上全局过滤器

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring aop

上一篇:Vue怎么动态扩展表头的表格及数据

下一篇:Java多线程怎么理解

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》