Sping AOP动态代理过程

Spring AOP(面向切面编程)利用动态代理机制为应用提供横切关注点的模块化。它主要使用两种类型的代理:JDK动态代理和CGLIB代理。下面是Spring AOP动态代理过程的概述:

1. 启动AOP配置

首先,你需要在Spring配置中启用AOP支持。这通常通过@EnableAspectJAutoProxy注解或者XML配置中的元素完成。

2. 定义切面(Aspects)

切面是包含通知(Advice)和切入点(Pointcut)定义的类。通知是你要应用到连接点(Join Point)的行为,如方法执行前后。切入点则是你指定的通知应该应用的位置。

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("Method is about to be called");
    }
}

3. 配置切面

切面需要被Spring容器管理,可以通过组件扫描或XML配置来注册你的切面类。

4. 创建代理对象

当Spring容器创建一个Bean时,如果该Bean符合AOP代理的要求(例如匹配了某个切入点),则Spring会为其创建代理对象。

  • 对于实现了接口的类,Spring AOP默认使用JDK提供的java.lang.reflect.Proxy来创建代理对象。
    • 代理对象的创建:通过Proxy.newProxyInstance()方法创建代理实例。该方法需要三个参数:目标类使用的类加载器、目标类实现的所有接口以及一个InvocationHandler实例。

    • 调用拦截:每次调用代理对象上的方法时,都会经过InvocationHandlerinvoke()方法。在这个方法中,可以执行前置逻辑、调用实际的目标方法(通过反射)、以及后置逻辑。

  • 对于没有实现任何接口的类,Spring使用CGLIB来生成目标类的一个子类作为代理,并重写其中的方法以提供代理功能
    • 代理对象的创建:CGLIB通过继承目标类的方式来创建代理对象,即为代理对象生成一个目标类的子类,并且覆盖目标类中的方法。因此,代理对象实际上是一个目标类的子类实例。

    • 方法拦截:在CGLIB中,通过重写MethodInterceptor接口的intercept()方法来拦截方法调用。这个方法接收四个参数:代理对象、被调用的方法对象、方法参数以及方法代理对象。

如果想要强制使用CGLIB代理,即使目标对象实现了接口,也可以通过配置proxy-target-class="true"来实现。相反地,若想明确指定使用JDK动态代理,则应保证目标对象实现了必要的业务接口。

5. 应用通知

一旦代理对象被创建,Spring会在适当的时间将通知应用到连接点上。比如,如果你定义了一个前置通知(@Before),那么在目标方法执行之前,Spring会自动调用这个通知的方法。

6. 调用目标方法

最终,当你通过代理对象调用目标方法时,根据配置的通知类型(前置、后置、环绕等),相应的通知逻辑会被触发,然后才会执行目标方法本身。

注意事项

  • 如果一个类被标记为final,Spring无法使用CGLIB为其实现代理,因为Java不允许继承final类。
  • Spring AOP基于代理的特性意味着自调用(即在同一个类中方法之间的调用)不会触发通知,除非使用特殊的技巧如AspectJ编织。

通过这种方式,Spring AOP允许开发者以声明式的方式分离业务逻辑与系统服务(如日志记录、事务管理等),从而提高代码的清晰度和可维护性。

补充 (AOP拦截器类型)

1. @Before

@Before注解用于定义前置通知,它在目标方法执行之前运行。如果前置通知中抛出了异常,目标方法将不会被执行。

@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " is about to be called");
}

2. @After

@After注解用于定义后置通知,无论目标方法是否正常返回或者抛出异常,它都会在目标方法之后执行。

@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " has been executed");
}

3. @AfterReturning

@AfterReturning用于定义返回后通知,只有当目标方法成功返回时才会执行。

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " returned with: " + result);
}

4. @AfterThrowing

@AfterThrowing用于定义异常后通知,仅在目标方法抛出异常时执行。

@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
    System.out.println("Method " + joinPoint.getSignature().getName() + " threw exception: " + ex.getMessage());
}

5. @Around

@Around是最强大的通知类型,可以完全控制目标方法的执行流程,包括决定何时以及是否执行目标方法。

@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("Method " + joinPoint.getSignature().getName() + " is about to be called");
    try {
        Object result = joinPoint.proceed();
        System.out.println("Method " + joinPoint.getSignature().getName() + " successfully returned");
        return result;
    } catch (IllegalArgumentException e) {
        System.out.println("Illegal argument for method " + joinPoint.getSignature().getName());
        throw e;
    }
}

通过上述例子,我们可以看到不同类型的AOP通知如何影响方法的执行流程,并根据需要进行日志记录、异常处理等操作。这使得代码更加清晰且易于维护,同时减少了重复代码的数量。

你可能感兴趣的:(Spring,java,开发语言,spring)