Spring Aop 详解(下)

简介: 面向切面编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式。OOP中模块化的关键单元是类,而在AOP中,模块化单元是切面。切面支持跨多个类型和对象的切点(如事务管理)的模块化。 Spring AOP 是 Spring 框架的关键组件之一。Spring IOC 容器不依赖于AOP组件,如果不要我们项目中不需要 AOP 功能那么就可以不加载这个模块。AOP 补充了 Spring IOC,以提供一个非常强大的中间件解决方案。

JdkDynamicAopProxy


下面是生成代理对象的代码:


// 获取代理对象
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // 查找所有需要实现的接口
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 创建代理对象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}


我们再来看看当我们调用目标对象的方法时候,就会来调用 JdkDynamicAopProxy#invoke 方法来实现代理对象。其实核心的原理就是生成了一个代理类,然后去查找关联的 MethodInterceptor r然后在代理方法中去把这些串起来实现代理。下面我们来看看 inovke 的具体实现:


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;
    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            // 目标没有实现自己的 equals 方法
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            // 目标没有实现自己的 hashCode 方法
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            // 根据代理对象的配置调用服务,如果是Advised接口的实现类,则直接调用
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        Object retVal;
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        // 有可能为null.尽可能减少拥有目标对象的时间,在这种情况下对象来自于对象池
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // Get the interception chain for this method.
        // 获得这个方法的拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        // 如果没有拦截器链,则直接调用目标对象
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            // 构造一个方法调用
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // 调用连接点的拦截器链
            retVal = invocation.proceed();
        }
        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            // 必须来自TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            // 保存旧的代理对象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}


总结一下: 这个过程其实可以分为三步:


  1. 获取原始对象和原始对象的类型,原始对象再 targetSource 中获取到 target, 然后再通过 target 获取 targetClass


  1. 通过当前对象的 ProxyFactory 所添加的并匹配的 Advisor 封装成 MethodInterceptor 的拦截器链 chain


  1. 如果chain为空,则直接执行target对应的当前方法


  1. 如果chain不为空,则会依次执行chain中的MethodInterceptor。 执行的顺序,会按照通知的设定执行,如果存在相同类型的通知,会按照排序进行执行。


ObjenesisCglibAopProxy


gclib 的代理过程和 jdk 代理很多类似目前我就不再这里详细描述,下面是 gclib 代理对象创建的过程:


  1. 创建Enhancer


  1. 设置Enhancer的superClass为通过ProxyFactory.setTarget()所设置的对象的类


  1. 设置Enhancer的interfaces为通过ProxyFactory.addInterface()所添加的接口,以及SpringProxy、Advised接口


  1. 设置Enhancer的Callbacks为DynamicAdvisedInterceptor


  1. 最后通过Enhancer创建一个代理对象


查看生成的代理类代码


//该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
//该设置用于输出jdk动态代理产生的类 默认在com/sun/proxy 目录下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");


Spring Aop 使用方式


通过实现PointcutAdvisor接口


  1. BeanNameAutoProxyCreator

@Bean
public BeanNameAutoProxyCreator creator(){
    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
    beanNameAutoProxyCreator.setBeanNames("userService");
    beanNameAutoProxyCreator.setInterceptorNames("myAdvisor");
    return beanNameAutoProxyCreator;
}

定义的这个bean,相当于一个“自动代理”器,有了这个Bean之后,可以自动的对setBeanNames中所对应的bean进行代理,代理逻辑为所设置的interceptorNames


  1. DefaultAdvisorAutoProxyCreator


// 定义了 Advisor 过后  DefaultAdvisorAutoProxyCreator
// 回自动去查找 BeanPostProcessor
@Bean
public DefaultAdvisorAutoProxyCreator creator1(){
    DefaultAdvisorAutoProxyCreator creator
            = new DefaultAdvisorAutoProxyCreator();
    return creator;
}


定义 Advisor


@Component
public class MyAdvisor implements PointcutAdvisor {
  @Override
  public Pointcut getPointcut() {
    NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();
    methodPointcut.setMappedName("test");
    return methodPointcut;
  }
  @Override
  public Advice getAdvice() {
    return new MethodBeforeAdvice() {
      @Override
      public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法执行之前");
      }
    };
  }
  @Override
  public boolean isPerInstance() {
    return false;
  }
}


AbstractAutoProxyCreator#postProcessAfterInitialization 后置处理器方法执行的时候。


public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
// wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    //当前这个 Bean 不用被代理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    //1.先判断当前的 bean 是不是要进行AOP,比如当前的Bean的类型是 Pointcut, Advice, Advisor 等那些就不需要 AOP
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    // Create proxy if we have advice.
    //2.如果匹配到 Advisors 不为 null, 那么进行代理并且返回代理对象
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) { //需要动态代理
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //3. 基于 bean 对象和 advice 创建代理对象
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        //存一个代理对象的类型
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}


后面会调到 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 然后通过 findEligibleAdvisors 方法查找到所有的 Advisor。 后面再对 Bean 和 Advisor 进行绑定。


通过@Aspect、@Pointcut、@Before等注解


注解方式实现,再前面概念描述的时候已经提到本处不在详细描述。


Spring 源码解析









相关文章
|
3天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
28 8
|
2月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
81 5
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
81 8
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
51 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
51 4
|
3月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
58 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
48 1
|
4月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP