spring aop

AOP是spring框架提供的非常重要的功能,让开发者可以非常方便地实现面向切面编程。

一、AOP简介

AOP(面向切面编程)可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,从而让开发者可以大幅度解耦代码,建立良好的代码结构。但当我们需要在大量的对象中引入公共行为的时候,OOP就无法方便的帮我们完成了。因为OOP描述的是一种层次关系,不是一种切面关系。例如,我们使用OOP完成了业务逻辑的开发,但是当我们需要在所有的业务逻辑中添加日志记录、安全校验等逻辑时,如果依照OOP的思想,我们需要进行大量的修改,对象变得臃肿,我们的业务逻辑里面也会掺杂很多无关业务的代码,增加了耦合度。

为了解决这个问题,面向切面编程应运而生。所谓面向切面编程,就是将我们的业务代码剖开,将上述的无关业务的逻辑进行模块整合后,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。这样,可以大大降低代码的耦合度,对源代码无侵入性,让我们更关注于业务。如今我们接触的框架、中间件,都大面积地使用了AOP编程,让我们的代码编写更加得心应手。

在介绍AOP技术实现之前,先介绍几个AOP的常用概念:

  • advice(增强):我们要横向引入的功能,如上文所说的日志打印、安全校验等
  • joinPoint(连接点):我们要将引入的功能应用在哪个地方,如在执行某个方法前/后,异常抛出前等
  • Pointcut(切点):决定哪些在哪些连接点进行增强的规则,如我要在所有方法名中含有get的方法前引入增强(织入),那这个规则就是切点
  • target(目标):引入增强的目标对象
  • weave(织入):将增强的代码引入到目标对象的过程
  • aspect(切面):一个含有增强和切点的对象

介绍完这些后,我们来看下AOP的技术实现。

AOP技术实现简介

根据AOP的织入时期和实现方式实现方式不同,AOP可以分为以下几种:

方法原理优点缺点
静态AOP在编译期,切面的字节码直接被织入到目标对象的字节码中无性能损耗,AspectJ使用的就是该方法灵活性不足,需要使用特殊的编译器
动态代理基于JDK动态代理,在运行期,目标类加载后,基于接口动态生成代理类,将切面织入到代理类中更灵活需要实现JDK的invocationHandler接口
动态字节码生成在类加载期,基于CGLIB等,在目标类加载前/后,对原有字节码进行修改,将切面织入到字节码中可对几乎所有类进行增强

上述三个方法各有利弊,不过思路是一致的,都是通过某种方式,在不改变原有代码的情况下,将切面代码织入到业务代码中,这个思路也是面向切面编程的核心原理。

二、spring AOP的使用

AOP是spring框架三大基石之一,spring为用户提供了简单易用的AOP功能,让编写AOP变得轻而易举,即使我们对AOP的原理一无所知,也能编写出满足自己要求的AOP代码。

spring为我们提供了两种方式的AOP,一种是基于JDK动态代理实现的,一种是基于字节码增强技术实现的,在这里,我们需要先澄清一下spring aop和AspectJ的关系。AspectJ作为在spring之前使用最广泛的aop框架,其提出了一系列AOP的基本概念,并提供了一套标准的AOP注解,spring为了方便开发者理解,使用了aspectJ的注解来实现aop,但是,aspectJ的底层aop实现spring都没有使用到。

首先,pom中引入spring-aop依赖:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>

创建一个业务对象

@Service
public class FooBar {
	public void foo() {
		System.out.println("foo");
	}
}
public interface IBar {
	void bar();
}
@Service
public class BarImpl implements IBar {
	@Override
	public void bar() {
		System.out.println("bar");
	}
}

在spring配置文件中开启注解支持和扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:component-scan base-package="com.jim2954981.springaop.*">
    </context:component-scan>
</beans>

创建一个main方法,执行一下

public class Main {
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		FooBar fooBar = ctx.getBean(FooBar.class);
		fooBar.foo();
		BarImpl bar = ctx.getBean(BarImpl.class);
		bar.bar();
	}
}

此时打印的结果是

foo
bar

我们想在代码执行前后进行日志打印,使用spring aop,首先需要在spring配置文件中开启aop功能

<aop:aspectj-autoproxy proxy-target-class="true"/>

其次,创建一个配置aop的bean

@Aspect // 添加该注解声明这个类用于spring aop配置
@Component
public class LogAspect {
	@Pointcut("within(com.jim2954981.springaop..*)")// 用@pointcut注解声明公共连接点
	public void pointcut() {
	}

	@Before("pointcut()")// @before声明在公共连接点执行之前进行增强,这个方法就是一个adviser,注解的属性”pointcut()“就是一个切点
	// 入参joinPoint可以让我们获取到连接点的信息,比如方法签名、方法入参等等,这里我们通过方法签名拿到连接点的方法名
	public void beforeLog(JoinPoint joinPoint) {
		System.out.println("before Logger=========>" + joinPoint.getSignature().getName());
	}

	@After("pointcut()")// @After声明在公共连接点执行之后进行增强
	public void afterLog(JoinPoint joinPoint) {
		System.out.println("after Logger===========>" + joinPoint.getSignature().getName());
	}
}

配置好后,我们再执行以下Main方法,打印结果为

before Logger=========>foo
foo
after Logger===========>foo
before Logger=========>bar
bar
after Logger===========>bar

如此,我们就完成了一个简单的面向切面编程。这个例子也可以让大家感受到aop的强大,对我们业务代码零侵入,同时将一些公共行为增强到了我们的业务代码中。
接下来,我们通过源码,对spring的aop能力进行探究。

三、spring AOP的实现

springAop的实现可以分为两个步骤:

  1. 注册
  2. 创建代理
注册

从上文中可以看到,我们要使用spring aop,就需要在配置文件中添加这么一个标签:

<aop:aspectj-autoproxy proxy-target-class="true"/>

如果是使用启动类对spring容器进行启动,则我们可以直接添加这个注解:

@EnableAspectJAutoProxy(proxyTargetClass = true)

其中的proxyTargetClass属性是用来告诉spring要使用什么方式进行aop,上文中介绍到,spring支持基于JDK动态代理的aop和CGLIB字节码增强的aop,如果设置为true,则会使用CGLIB进行aop实现,否则,使用JDK动态代理实现。有一点需要提醒的是,因为JDK动态代理都是依赖接口实现的,如果这个属性设置为false,我们要增强一个非接口类时,就会抛出异常

从哪里找起呢,给大家提供一个思路,对于spring配置文件来说,我们要是用某个功能,会引入某个spring模块的xsd配置,spring会提供对应的namespaceHandler进行解析,详细情况可看下我的另一篇文章:spring与xml schema,这里不再赘述。
根据这个思路,我们可以找到这么一个类:AopNamespaceHandler,进去看下:

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    public AopNamespaceHandler() {
    }

    public void init() {
        this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
        this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
}

可以看到,当spring容器启动时,对配置文件、注解进行扫描,如果发现了aspectj-autoproxy,就会在容器中注册AspectJAutoProxyBeanDefinitionParser解析器进行解析,进入到这类中,我们看到关键方法:

@Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
  // 注册aspectj注解解析器      
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        this.extendBeanDefinition(element, parserContext);
        return null;
    }

进入registerAspectJAnnotationAutoProxyCreatorIfNecessary方法:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
// 注册AspectJ代理类创建器
        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对target-proxy-class属性进行处理
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册组件并通知容器,容器的相关监听器会做好准备        
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

首先我们看下registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,这个方法就是注册一个代理创建器,spring为我们提供了一个默认的代理创建器,当然,如果我们自定义了其他代理创建器,这里的注册会有所不同。

@Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            //如果当前的代理创建器不是spring默认的代理创建器,则需要根据优先级确定使用哪个代理创建器
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        } else {
        // 如果当前没有代理创建器,则使用spring默认的代理创建器
            RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
            beanDefinition.setSource(source);
            beanDefinition.getPropertyValues().add("order", -2147483648);
            beanDefinition.setRole(2);
            registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
            return beanDefinition;
        }
    }

执行完这个方法后,spring已经注册了一个代理创建器,回到上层方法,我们看第二步:useClassProxyingIfNecessary

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
        if (sourceElement != null) {
        // 判断是否设置了proxy-target-class=true,如果是,则将该属性赋予到代理创建器的BeanDefinition中,该属性不是我们探究的重点,因此先忽略
            boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute("proxy-target-class"));
            if (proxyTargetClass) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
// 判断是否设置了expose-proxy=true
            boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute("expose-proxy"));
            if (exposeProxy) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }

    }

进入forceAutoProxyCreatorToUseClassProxying方法中:

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
            BeanDefinition definition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
            // 如果是默认的spring代理创建器,则在该创建器中添加属性:proxyTargetClass为true
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }

最后一个方法看一下:registerComponentIfNecessary

private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
        if (beanDefinition != null) {
            parserContext.registerComponent(new BeanComponentDefinition(beanDefinition, "org.springframework.aop.config.internalAutoProxyCreator"));
        }
    }

这个方法,就是将代理创建器注册到spring容器当中
至此,aop的注册流程结束。

创建代理

上面提到,spring aop的代理类的创建是由代理创建器完成的,因此,我们看下创建器类:AnotationAwareAspectJAutoProxyCreator,先看下类图:
在这里插入图片描述
从上图中可以看到,这个类实现了BeanPostProcessor接口,根据我们之前对spring beans的描述,实现了该接口,在bean实例化前,会调用该接口的postProcessAfterInitialization方法,在其父类AbstractAutoProxyCreator中可以看到该方法的实现:

 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            // 当容器发现没有缓存被代理的bean代理类时,就会检查、创建该bean的代理类
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return this.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;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {		// 如果该bean无需被代理,则直接返回	
            return bean;
             // 如果该bean不是基础设施类且没有被指定忽略代理,则对bean进行代理类创建
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
       // 获取这个bean的增强方法
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                // 创建代理类
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }

从上面代码中,我们可以看到,创建代理有两个步骤,第一个是获取增强方法,第二个是创建代理类。我们先看如何获取增强方法getAdvicesAndAdvisorsForBean
一路向下调用,就会调用到这个方法,这个方法做了两件事情:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //1. 获取所有增强器
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        // 2.匹配对应该bean的增强器
        List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        this.extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
        }

        return eligibleAdvisors;
    }

我们先看下spring如何寻找增强器:

protected List<Advisor> findCandidateAdvisors() {
// 先调用父类方法,找到已经创建的Advisor
        List<Advisor> advisors = super.findCandidateAdvisors();
        if (this.aspectJAdvisorsBuilder != null) {
        // 根据bean的注解创建新的增强器
           advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }
  public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = this.aspectBeanNames;
        if (aspectNames == null) {
            synchronized(this) {
                aspectNames = this.aspectBeanNames;
                if (aspectNames == null) {
                    List<Advisor> advisors = new ArrayList();
                    List<String> aspectNames = new ArrayList();
                    // 获取所有bean名称
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                    String[] var18 = beanNames;
                    int var19 = beanNames.length;

                    for(int var7 = 0; var7 < var19; ++var7) {
                        String beanName = var18[var7];
                        if (this.isEligibleBean(beanName)) {
                        // 根据bean名称获取对应的bean类型
                            Class<?> beanType = this.beanFactory.getType(beanName);
                            // 如果该bean类型被@AspectJ注解了
                            if (beanType != null && this.advisorFactory.isAspect(beanType)) {
                                aspectNames.add(beanName);
                                AspectMetadata amd = new AspectMetadata(beanType, beanName);
                                if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                    MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                    // 解析并创建增强器
                                    List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                    if (this.beanFactory.isSingleton(beanName)) {
                                    // 缓存
                                        this.advisorsCache.put(beanName, classAdvisors);
                                    } else {
                                        this.aspectFactoryCache.put(beanName, factory);
                                    }

                                    advisors.addAll(classAdvisors);
                                } else {
                                    if (this.beanFactory.isSingleton(beanName)) {
                                        throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
                                    }

                                    MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                    this.aspectFactoryCache.put(beanName, factory);
                                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                                }
                            }
                        }
                    }

                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
        }

        if (aspectNames.isEmpty()) {
            return Collections.emptyList();
        } else {
            List<Advisor> advisors = new ArrayList();
            Iterator var3 = aspectNames.iterator();

            while(var3.hasNext()) {
                String aspectName = (String)var3.next();
                List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
                if (cachedAdvisors != null) {
                    advisors.addAll(cachedAdvisors);
                } else {
                    MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
                    advisors.addAll(this.advisorFactory.getAdvisors(factory));
                }
            }

            return advisors;
        }
    }

我们看下spring如何创建并解析增强器的:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        this.validate(aspectClass);
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        List<Advisor> advisors = new ArrayList();
        // 获取被注解标注的切面类的所有方法
        Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();
		// 遍历并创建不同的增强器
        while(var6.hasNext()) {
            Method method = (Method)var6.next();
            Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
        ......
    }

这个方法首先要把被@AspectJ标注的切面类的每一个方法进行解析,然后再根据标注的不同注解,创建不同的增强器。我们先来看下getAdvisorMethods方法:

private List<Method> getAdvisorMethods(Class<?> aspectClass) {
        List<Method> methods = new ArrayList();
        ReflectionUtils.doWithMethods(aspectClass, (method) -> {
        // 通过反射拿到切面类的所有方法,并排除被@Pointcut注解的方法
            if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                methods.add(method);
            }

        });
        methods.sort(METHOD_COMPARATOR);
        return methods;
    }

@Pointcut注解我们上文提到过,这个注解只是声明连接点,并不参与织入,因此不属于增强器,我们要关注的是切点,也就是上文被@Before、@After等注解修饰的方法。
我们再看下getAdvisor方法:

@Nullable
    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {
// 校验
        this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
        // 获取切点
        AspectJExpressionPointcut expressionPointcut = this.getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
        return expressionPointcut == null ? null : new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }
@Nullable
    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   // 根据切点方法找到对应的注解 
        AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        } else {
        
            AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
    // 从注解中提取我们在注解属性里面设置的表达式   
            ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
            if (this.beanFactory != null) {
                ajexp.setBeanFactory(this.beanFactory);
            }
            return ajexp;
        }
    }

spring中所有的增强类都由InstantiationModelAwarePointcutAdvisorImpl类进行封装,我们看下这个类的构造方法:

 public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        this.declaredPointcut = declaredPointcut;
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        this.methodName = aspectJAdviceMethod.getName();
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
        this.aspectInstanceFactory = aspectInstanceFactory;
        this.declarationOrder = declarationOrder;
        this.aspectName = aspectName;
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        } else {
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            // 初始化增强器
            this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
        }

    }

大部分工作都是属性的赋值,最后一行真正对增强器进行初始化,进入这个方法:

 private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
        Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
        return advice != null ? advice : EMPTY_ADVICE;
    }
 @Nullable
    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        this.validate(candidateAspectClass);
        AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        } else if (!this.isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
        } else {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
            }

            Object springAdvice;
            switch(aspectJAnnotation.getAnnotationType()) {
            case AtPointcut:
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }

                return null;
                // 根据注解不同,初始化不同类型增强器
            case AtAround:
                springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    ((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
                }
                break;
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    ((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            default:
                throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
            }

            ((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
            ((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrder);
            String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
            if (argNames != null) {
                ((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
            }

            ((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
            return (Advice)springAdvice;
        }
    }

在这里我们看到,spring会根据注解的不同,初始化不同的增强器,例如@Before注解,就会初始化AspectJMethodBeforeAdvice增强器,

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }

    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
    // 激活增强方法
        this.invokeAdviceMethod(this.getJoinPointMatch(), (Object)null, (Throwable)null);
    }

    public boolean isBeforeAdvice() {
        return true;
    }

    public boolean isAfterAdvice() {
        return false;
    }
}

这里可以看到,增强器提供了before方法,这个方法就会调用增强的方法,也就是我们上文的beforeLog方法,在这里已经看出来代理的意思了。至此,增强器都被找到了,接下来我们看下增强器如何被匹配的。看下findAdvisorsThatCanApply方法:

  protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
        ProxyCreationContext.setCurrentProxiedBeanName(beanName);

        List var4;
        try {
        // 主要逻辑
            var4 = AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        } finally {
            ProxyCreationContext.setCurrentProxiedBeanName((String)null);
        }

        return var4;
    }
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        } else {
            List<Advisor> eligibleAdvisors = new ArrayList();
            Iterator var3 = candidateAdvisors.iterator();
			// 遍历增强器并匹配
            while(var3.hasNext()) {
                Advisor candidate = (Advisor)var3.next();
                // 处理引介增强,引介增强不是我们介绍的重点,暂不赘述
                if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                    eligibleAdvisors.add(candidate);
                }
            }

            boolean hasIntroductions = !eligibleAdvisors.isEmpty();
            Iterator var7 = candidateAdvisors.iterator();

            while(var7.hasNext()) {
                Advisor candidate = (Advisor)var7.next();
                // 处理普通增强类
                if (!(candidate instanceof IntroductionAdvisor) && canApply(candidate, clazz, hasIntroductions)) {
                    eligibleAdvisors.add(candidate);
                }
            }
            return eligibleAdvisors;
        }
    }

看下canApply方法:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        // 匹配类型
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        } else {
        // 匹配方法
            MethodMatcher methodMatcher = pc.getMethodMatcher();
            if (methodMatcher == MethodMatcher.TRUE) {
                return true;
            } else {
                IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
                if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
                    introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher)methodMatcher;
                }

                Set<Class<?>> classes = new LinkedHashSet();
                if (!Proxy.isProxyClass(targetClass)) {
                    classes.add(ClassUtils.getUserClass(targetClass));
                }

                classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
                Iterator var6 = classes.iterator();

                while(var6.hasNext()) {
                    Class<?> clazz = (Class)var6.next();
                    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
                    Method[] var9 = methods;
                    int var10 = methods.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        Method method = var9[var11];
                        if (introductionAwareMethodMatcher != null) {
                            if (introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) {
                                return true;
                            }
                        } else if (methodMatcher.matches(method, targetClass)) {
                            return true;
                        }
                    }
                }

                return false;
            }
        }
    }

总结一下,spring如何寻找匹配增强器的:1.从容器中所有的bean里面找到被@Aspect注解的切面类;2.遍历解析切面类的所有方法,根据注解不同创建不同的增强器,并将我们在注解中输入的匹配表达式解析;3.根据匹配表达式匹配目标bean所需的增强器。

上面我们介绍了如何寻找增强器,接下来我们进入createProxy方法,看下spring如何实现代理的:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
        }
		
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        // 确定使用何种代理方式
        if (!proxyFactory.isProxyTargetClass()) {
            if (this.shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            } else {
                this.evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
        Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
        // 加入刚才找到的增强器
        proxyFactory.addAdvisors(advisors);
        // 设置要代理的类
        proxyFactory.setTargetSource(targetSource);
        // 定制代理
        this.customizeProxyFactory(proxyFactory);
        proxyFactory.setFrozen(this.freezeProxy);
        if (this.advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
		// 获取代理类
        return proxyFactory.getProxy(this.getProxyClassLoader());
    }

最重要的方法就是getProxy了,我们进去看下:

public Object getProxy(@Nullable ClassLoader classLoader) {
        return this.createAopProxy().getProxy(classLoader);
    }

这里就要根据类的情况和我们的设置,确定用和中方式创建代理类,我们看下createAopProxy方法:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 如果没有配置targeproxyclass为true,且是接口,则直接使用JDK动态代理
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);
        } else {
        // 否则,使用cglib进行代理类创建
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

接下来我们分别看下两种方式如何创建的:

  • JDK动态代理

众所周知,对于JDK动态代理,我们要实现JDK提供的InvocationHandler接口,然后有三个重要方法:

  1. 构造方法:传入被代理类对象
  2. getProxy:返回代理对象,供上层调用
  3. invoke:真正的增强逻辑实现在这个方法里

JdkDyanmicAopProxy类的源码中,我们看到这个类确确实实按照上面的描述来编写的:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  	......
    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        } else {
            this.advised = config;
        }
    }
    public Object getProxy(@Nullable ClassLoader classLoader) {
			.....
	}
	public Object invoke(Object proxy, Method method, Object[] args) 	throws Throwable{
		.....
	}

我们看下最重要的invoke方法:

 @Nullable
    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;

        Object retVal;
        try {
       	// 处理equals方法
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                Boolean var19 = this.equals(args[0]);
                return var19;
            }
		// 处理hashcode方法
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                Integer var18 = this.hashCode();
                return var18;
            }

            if (method.getDeclaringClass() == DecoratingProxy.class) {
                Class var17 = AopProxyUtils.ultimateTargetClass(this.advised);
                return var17;
            }

            if (this.advised.opaque || !method.getDeclaringClass().isInterface() || !method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
                // 获取当前方法的拦截器链
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                if (chain.isEmpty()) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                } else {
                    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    // 执行拦截器链
                    retVal = invocation.proceed();
                }
		// 获取返回结果
                Class<?> returnType = method.getReturnType();
                if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    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);
                }

                Object var13 = retVal;
                return var13;
            }

            retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        } finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }

            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }

        }

        return retVal;
    }

最重要的方法是Jointpoint.proceed,进去看一下:

@Nullable
    public Object proceed() throws Throwable {
    // 拦截器链执行完毕,返回
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.invokeJoinpoint();
        } else {
        // 获取链上的增强器
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // 匹配并执行增强代码
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }
  • CGLIB代理

CGLIB是非常强大的字节码增强工具,在各大中间件、框架中都有使用,其原理是通过创建一个被增强类的子类,并将增强代码写入子类中,将子类的字节码创建并加载而实现代理的。首先先介绍下CGLIB的用法:

public class Main {
	public static void main(String[] args) {
	// 创建CGLIB增强器
		Enhancer enhancer = new Enhancer();
		// 将增强器的父类设置成被代理类
		enhancer.setSuperclass(FooBar.class);
		// 设置拦截器,这是cglib的代理核心
		enhancer.setCallback(new CallBackImpl());
		// 创建代理类
		FooBar fooBar = (FooBar)enhancer.create();
		fooBar.foo();
	}
	// 我们通过实现cglib提供的MethodInterceptor接口,将我们的增强代码交给cglib进行代理,因此cglib代理最核心的方法就是intercept了
	private static class CallBackImpl implements MethodInterceptor {
		@Override
		public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
			System.out.println("before invoke==========");
			methodProxy.invokeSuper(o, objects);
			System.out.println("after invoke===========");
			return null;
		}
	}
}

输出结果是:

before invoke==========
foo
after invoke===========

可以看到,确实是增强成功了。仔细看下,我们Main方法中使用的FooBar对象,已经不是我们自己定义的对象了,而是被CGLIB进行字节码增强后的新对象:

class com.jim2954981.springaop.core.FooBar$$EnhancerByCGLIB$$d4736e24

了解了CGLIB代理的使用方法,我们回头看下spring的CGLIB代理类:CglibAopProxy

  public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }

        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
            Class<?> proxySuperClass = rootClass;
            int x;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                Class[] var5 = additionalInterfaces;
                int var6 = additionalInterfaces.length;

                for(x = 0; x < var6; ++x) {
                    Class<?> additionalInterface = var5[x];
                    this.advised.addInterface(additionalInterface);
                }
            }

            this.validateClassIfNecessary(proxySuperClass, classLoader);
            // 创建Enhancer类
            Enhancer enhancer = this.createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader && ((SmartClassLoader)classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
			// 将被代理类设置为Enhancer的父类
            enhancer.setSuperclass(proxySuperClass);
           
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new CglibAopProxy.ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
			// 获取拦截器
            Callback[] callbacks = this.getCallbacks(rootClass);
            Class<?>[] types = new Class[callbacks.length];

            for(x = 0; x < types.length; ++x) {
                types[x] = callbacks[x].getClass();
            }
			// 设置拦截器
            enhancer.setCallbackFilter(new CglibAopProxy.ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
            // 创建代理
            return this.createProxyClassAndInstance(enhancer, callbacks);
        } catch (IllegalArgumentException | CodeGenerationException var9) {
            throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", var9);
        } catch (Throwable var10) {
            throw new AopConfigException("Unexpected AOP exception", var10);
        }
    }

最重要的方法就是getCallBack获取拦截器,在这里,spring会把上文提到的增强器链放到拦截器中:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();
        // 将增强器封装成拦截器
        Callback aopInterceptor = new CglibAopProxy.DynamicAdvisedInterceptor(this.advised);
        Object targetInterceptor;
        if (exposeProxy) {
            targetInterceptor = isStatic ? new CglibAopProxy.StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new CglibAopProxy.DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
        } else {
            targetInterceptor = isStatic ? new CglibAopProxy.StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new CglibAopProxy.DynamicUnadvisedInterceptor(this.advised.getTargetSource());
        }

        Callback targetDispatcher = isStatic ? new CglibAopProxy.StaticDispatcher(this.advised.getTargetSource().getTarget()) : new CglibAopProxy.SerializableNoOp();
        // 将拦截器加入到callback中
        Callback[] mainCallbacks = new Callback[]{aopInterceptor, (Callback)targetInterceptor, new CglibAopProxy.SerializableNoOp(), (Callback)targetDispatcher, this.advisedDispatcher, new CglibAopProxy.EqualsInterceptor(this.advised), new CglibAopProxy.HashCodeInterceptor(this.advised)};
        Callback[] callbacks;
        if (isStatic && isFrozen) {
            Method[] methods = rootClass.getMethods();
            Callback[] fixedCallbacks = new Callback[methods.length];
            this.fixedInterceptorMap = new HashMap(methods.length);

            for(int x = 0; x < methods.length; ++x) {
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
                fixedCallbacks[x] = new CglibAopProxy.FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
                this.fixedInterceptorMap.put(methods[x].toString(), x);
            }

            callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
            System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
            System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
            this.fixedInterceptorOffset = mainCallbacks.length;
        } else {
            callbacks = mainCallbacks;
        }

        return callbacks;
    }

最后,我们看下封装的拦截器的intercept方法,上面提到过,这个方法里面就是增强的代码:

  @Nullable
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();

            Object var16;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }

                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                } else {
                // 这里就是调用增强器链了
                    retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
                }

                retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
                var16 = retVal;
            } finally {
                if (target != null && !targetSource.isStatic()) {
                    targetSource.releaseTarget(target);
                }

                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }

            }
            return var16;
        }

至此,springaop的源码流程介绍完毕。

一点补充

除了上面的jdk动态代理和字节码动态生成之外,spring其实还支持另一种增强方式,就是在类加载期进行增强,也就是spring的LTW(load time waving)技术,也可以达到aop的目的,这里简单介绍下。

LTW技术主要依赖于java提供的JVMTI技术,其原理就是通过在类加载期间对字节码进行修改并替换原类字节码进行加载,通过@EnableLoadTimeWeaving注解可以开启LTW。具体使用方法和实现原理大家自行查阅,在此不再赘述。

四、总结

本文介绍了spring aop的使用和相关原理,重点通过阅读源码介绍了spring aop的大致实现,希望对大家了解spring aop有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值