Spring源码解析之AOP篇

什么是AOP?

AOP(Aspect Oriented Programming):面向切面编程,与面向对象编程OOP的关键单位是类不一样,它的关键单位是切面,它通过提供改变程序结构的方式来补充OOP。通俗点就是说我们可以通过预编译或者运行时动态代理在不修改方法源码的情况下增强方法的功能。

实际开发中,AOP的出现方便了业务需求和系统功能之间的解耦和扩展,比如日志、事务、安全、权限等等系统功能,大大减少了重复的代码,以及维护的复杂,提高了开发效率。

而AOP的这种解决方式的本质就是代理机制。

准备工作

之前解析Spring源码或者在实际开发过程中,相信或多或少都对AOP代理有一些认识,那么在接下来解析源码前,我们需要先对一些相关基础作更全面的了解。

基础概念

切面(Aspect):介绍切面之前,我们先了解切面里面的连接点(JoinPoin),一个连接点就是代表一个类的执行方法;而切面就是连接多个类的连接点的一个横切面(官文称为跨越多个类的关注点的模块化模块化)。通常使用@Aspect注解声明的就是一个切面。

切点(Pointcut):切面中所有连接点匹配的规则定义,由多种切点表达式定义,而Spring AOP支持以下j几种切点表达式,

  • execution: 匹配方法执行的任何连接点。

  • within:匹配指定类型内的方法执行的任何连接点。

  • this: 匹配代理实现指定类型接口的任何连接点。

  • target: 匹配目标对象实现指定类型接口的任何连接点。

  • args: 匹配指定类型参数的方法执行的连接点。

  • @target: 匹配目标对象实现指定类型接口的具有指定注解的任何连接点。

  • @args:匹配指定类型参数的具有指定注解的方法执行的连接点。

  • @within: 匹配指定类型内的具有指定注解的方法执行的任何连接点。

  • @annotation:匹配指定注解的方法执行的连接点。

    其中execution和@annotation在实际开发中可能更为常用,至于不同表达式的书写规则请参考具体的官方文档说明。

通知:匹配的切点周围的拦截器方法,Spring中包括一下几种不同类型的通知,

  • 前置通知:方法执行的拦截。
  • 后置通知:方法执行后的拦截,无论正常返回还是异常退出。
  • 返回通知:方法正常执行完的拦截。
  • 环绕通知:方法执行时前后的自定义拦截,手动控制调用的方法。
  • 异常通知:方法执行时异常退出的拦截。

目标对象:被一个或者多个切面通知的对象,它始终是一个被代理的对象。

使用方式

Spring中有两种配置使用AOP的方式,分别是:

  • 基于XML的配置:配置繁琐,不同的需求集中配置不符合单一职责原则,无法组合声明的命名切入点。
  • 基于@Aspect的注解:配置简单,支持更丰富的配置组合,同时具有单元模块化。这也是开发中主要的使用方式。
代理机制

Spring中的代理机制分为两种:

  • JDK动态代理:内置在JDK中,通过拦截和反射来实现,被代理的对象必须要实现接口。
  • CGLIB动态代理:一个开源类的定义库,通过ASM字节码生成的方式生成代理类,默认代理没有实现的接口的对象,不能对final修饰的方法进行代理;Spring中可以通过设置@EnableAspectJAutoProxy(proxyTargetClass = true)强制使用用CGLIB进行动态代理。

调用流程

了解完上述一些相关基础之后,接下来我们将会从源码来分析AOP的初始化到实际调用的过程。

而完成AOP的初始化其实也是穿插在IOC容器的初始化过程中,前面我们已经了解过IOC容器,包括依赖注入的初始化过程,再来看AOP的相关源码应该是更加容易下手的,同时这也是对之前内容的补充。

这里我们主要以注解的方式来使用并分析源码过程。

首先我们写个简单的测试类来看下AOP的使用和应用效果,

@Configuration
@EnableAspectJAutoProxy
public class Config {
}

@Component
public class AopA{
    public void print(){
        System.out.println("AAAAAAAAAAAAAA");
    }
}

@Aspect
@Component
public class AspectA {

    @Pointcut("execution(* com.test.spring.entity.AopA.*(..))")
    public void point(){
    }

    @Before("point()")
    public void before(){
        System.out.println("before");
    }

    @After("point()")
    public void after() throws Exception {
        System.out.println("after");
    }

    @Around("point()")
    public void around(JoinPoint joinPoint){
        System.out.println("aroundBefore");
        try {
            ((ProceedingJoinPoint)joinPoint).proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("aroundAfter");
    }

    @AfterReturning("point()")
    public void afterReturn(){
        System.out.println("afterReturn");
    }

    @AfterThrowing("point()")
    public void afterThrow(){
        System.out.println("afterThrow");
    }

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext("com.test.spring");
        AopA aopA = ac.getBean(AopA.class);
        aopA.print();
    }

}

运行代码,控制台打印如下,

aroundBefore
before
AAAAAAAAAAAAAA
aroundAfter
after
afterReturn

如果方法执行过程中抛出异常,则会打印配置的异常通知中的afterThrow

启用自动代理

Spring内置了很多丰富且强大的功能,但是并不是每个功能都是默认开启的,比如AOP自动代理,我们首先得告诉Spring容器去启用自动代理,这样它才会扫描、注册、应用我们配置的相关注解类。而通过注解的方式启用自动代理也很简单,就是通过上面测试类中配置的@EnableAspectJAutoProxy注解来启用。

还是从0到1,我们就从@EnableAspectJAutoProxy注解来入手。

我们先看下这个注解的源码,

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;
}

其中类上加了一个注解@Import({AspectJAutoProxyRegistrar.class}),而我们知道@Import注解代表AspectJAutoProxyRegistrar对象可以通过配置的@EnableAspectJAutoProxy注解的类去加载,也就是说IOC实例化Config对象的时候也会实例化AspectJAutoProxyRegistrar

同时这里我们也能看到EnableAspectJAutoProxy具有两个配置属性,

  • proxyTargetClass:默认false,Spring会根据被代理对象的实现接口情况去自动选择JDK或者CGLIB动态代理机制;设置为true时,代表代理机制强制使用CGLIB动态代理,但这样会导致无法对final修饰的方法进行拦截通知,因为它不能被覆写。
  • exposeProxy:是否暴露当前代理对象为 ThreadLocal以便目标可以访问它。比如方法a()、b()都被拦截通知,默认false时,方法a()中调用b()方法的话,则调用的方法b()不会被拦截通知,如果设置为true时,调用的方法b()也会被拦截通知。

后过头来,我们继续分析下配置完启动注解之后会发生什么?以及AspectJAutoProxyRegistrar类的作用。那我们就又要从IOC容器的初始化开始说了,首先我们这里的测试类都是基于Annotation来完成IOC容器的初始化。前面IOC源码解析中,我们已经知道第一步会调用scan()方法来扫描配置的指定包路径下的所有Bean对象,并封装成BeanDefinition对象存放beanDefinitionMap中,之后会调用refresh()方法去载入Bean的配置资源,而这里主要先关注其中的两个方法,分别是,

//实例化并调用所有已注册的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPost事件处理器
registerBeanPostProcessors(beanFactory);

我们先看invokeBeanFactoryPostProcessors(),通过注释大概知道它的作用了,那我们来看看源码,

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

我们能看出真正的实现是委托PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors()来完成的,

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		Set<String> processedBeans = new HashSet<>();
         // Invoke BeanDefinitionRegistryPostProcessors first, if any.
		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}
			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				for (String ppName : postProcessorNames) {
					if (!processedBeans.contains(ppName)) {
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				registryProcessors.addAll(currentRegistryProcessors);
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
				currentRegistryProcessors.clear();
			}
			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}
		else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}
		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (processedBeans.contains(ppName)) {
				// skip - already processed in first phase above
			}
			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}
		// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : orderedPostProcessorNames) {
			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
		// Finally, invoke all other BeanFactoryPostProcessors.
		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : nonOrderedPostProcessorNames) {
			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
		// Clear cached merged bean definitions since the post-processors might have
		// modified the original metadata, e.g. replacing placeholders in values...
		beanFactory.clearMetadataCache();
	}

一眼望去感觉实现的逻辑相当繁琐复杂,其实你细看发现很多代码处理几乎是重复的,先获取postProcessorNames的数组,然后遍历调用getBean()实例化并添加到currentRegistryProcessors临时集合中,接着传参到invokeBeanFactoryPostProcessors()并调用,最后clear()临时集合。这里面主要是将实现 PriorityOrdered、Ordered 和其余的 BeanDefinitionRegistryPostProcessor 分开处理。首先会实例化并调用实现PriorityOrdered接口ConfigurationClassPostProcessor,它是用来引导处理配置类(配置@Configuration的类),比如配置@Configuration,通常是会默认注册的,也可以使用任何其他 BeanFactoryPostProcessor 去手动声明,它会在任何其他 BeanFactoryPostProcessor 执行之前注册了配置类中声明的Bean定义。我们之前在配置@EnableAspectJAutoProxy的类上也加了@Configuration注解的作用也是因为这个。

其实分析到这里,接下来的流程处理可能就已经柳暗花明了,再往下调用的源码牵扯的细节比较多,而且继续贴源码也会显得比较繁琐不容易抓住主流程,这里不一一展开了,我会用时序图来展示后面的流程,

最终就是为了通过AspectJAutoProxyRegistrar的registerBeanDefinitions()方法把对象AnnotationAwareAspectJAutoProxyCreator封装成BeanDefinition并注册到容器中去,同时将proxyTargetClass和exposeProxy的属性值注入进去。

注册后置处理器

那我们为什么启动仅仅是通过配置类最终把AnnotationAwareAspectJAutoProxyCreator注册到容器中去呢?其实我们看下它的类图结构就知道了,

image-20210811101949856

它实现了BeanPostProcessor接口,我们看下它的源码,

public interface BeanPostProcessor {
	//Bean的初始化前回调
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	//Bean的初始化之后回调
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

而我们知道BeanPostProcessor的作用是:定义了Bean的初始化回调方法,在其实例化、配置和初始化之后实现一些自定义逻辑。你可以理解为Bean对象的拦截器,如果你想扩展Bean的功能或对其修改包装等,就可以通过实现它去完成。所以AnnotationAwareAspectJAutoProxyCreator的作用就很清楚了,它就是为了在Bean初始化之后对其做一些自定义的处理,至于怎么样的处理我们后面细讲。

但是这里只是将它注册到容器中去,我们还需要把它实例化并添加到集合List<BeanPostProcessor> beanPostProcessors中去,所以上面的registerBeanPostProcessors()方法就是完成这个事的,

	protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
	}

同样真正的实现还是交给PostProcessorRegistrationDelegate去完成的,

	public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}
		// First, register the BeanPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
		// Next, register the BeanPostProcessors that implement Ordered.
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);
		// Now, register all regular BeanPostProcessors.
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
		// Finally, re-register all internal BeanPostProcessors.
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);
		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

	private static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors){
		for (BeanPostProcessor postProcessor : postProcessors) {
			beanFactory.addBeanPostProcessor(postProcessor);
		}
	}

	@Override
	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
		Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
		this.beanPostProcessors.remove(beanPostProcessor);
		this.beanPostProcessors.add(beanPostProcessor);
		if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
			this.hasInstantiationAwareBeanPostProcessors = true;
		}
		if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
			this.hasDestructionAwareBeanPostProcessors = true;
		}
	}

我们能看到这里就是将实现 PriorityOrdered、Ordered 和其余的 BeanDefinitionRegistryPostProcessor 的BeanPostProcessor分开添加到集合beanPostProcessors中;而我们知道AnnotationAwareAspectJAutoProxyCreator是Ordered的子类,这里我们主要看对orderedPostProcessorNames的处理:

  1. 首先会遍历orderedPostProcessorNames集合,拿到AnnotationAwareAspectJAutoProxyCreator;
  2. 然后调用getBean()方法对其实例化;
  3. 完成之后调用registerBeanPostProcessors()将AnnotationAwareAspectJAutoProxyCreator添加到BeanPostProcessor的集合中去。

其实到这里才会最终完成自动代理的配置的生效启用,接下来我们应该也能猜到,将会通过配置的切面类来对匹配规则的目标Bean在其初始化之后对其做代理的相应处理。

解析@Aspect切面配置

做完上面的这些之后,我们知道IOC容器接下来会调用finishBeanFactoryInitialization()方法对容器中的单例Bean进行预实例化,也就是依赖注入的过程了。

那接下来是怎么解析配置的切面类以及其中定义的切点和通知呢(也就是解析我们配置的AspectA类)?

我们继续来分析,我们知道在创建Bean之前,会调用resolveBeforeInstantiation()方法来应用解析Bean配置的实例化前后处理器,

	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

	@Nullable
	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

也是就会遍历之前集合添加的BeanPostProcessor并调用父类是InstantiationAwareBeanPostProcessor的子类实现的postProcessBeforeInstantiation()方法,从上面类图中我们知道,之前添加的AnnotationAwareAspectAutoProxyCreator就是它的子类,而这里真正调用的是AnnotationAwareAspectAutoProxyCreator的父类AbstractAutoProxyCreator的方法实现,

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);
		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}
		//存在自定义的TargetSource则创建代理
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

这里先来看下isInfrastructureClass()方法,

	protected boolean isInfrastructureClass(Class<?> beanClass) {
		boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
				Pointcut.class.isAssignableFrom(beanClass) ||
				Advisor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass);
		if (retVal && logger.isTraceEnabled()) {
			logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
		}
		return retVal;
	}

它会判断当前的Bean是否是不能代理的基础设施类,比如Advice、PointCut、Advisor等接口的实现类;这里其实就是来筛选我们测试代码中的切面配置类AspectA,并将它放到Map<Object, Boolean> advisedBeans缓存中。

同时还会调用shouldSkip()方法判断当前Bean是否应该跳过,这里面就关系着配置@Aspect注解的切面类的配置的提前解析,而这实际委托的是子类AspectJAwareAdvisorAutoProxyCreator实现的,我们往下看,

	@Override
	protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true;
				}
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}

这里面实现很简单,就是调用findCandidateAdvisors()方法获取通知Advisor的集合,然后遍历Advisor去检查配置Pointcurt切点的类的beanName是否和当前Bean的beanName一致,一致则返回true,否则调用父类的shouldSkip()获取默认的false实现。

我们重点需要看的是findCandidateAdvisors()方法,这里实际调用的是AnnotationAwareAspectJAutoProxyCreator中实现,

	@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

发现原来是对父类方法的增强,它有个很重要的作用:既保留了父类的获取xml配置文件定义的通知外,又增加了获取注解配置的通知。由于我们是通过注解去配置的,我们直接看由aspectJAdvisorsBuilder的buildAspectJAdvisors()方法实现就行,至于findCandidateAdvisors()中的实现感兴趣可以自己研究下,你会发现其实两种的实现代码都大同小异,

	public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;
		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					List<Advisor> advisors = new LinkedList<>();
					aspectNames = new LinkedList<>();
                      // 获取容器中注册的所有beanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					for (String beanName : beanNames) {
						if (!isEligibleBean(beanName)) {
							continue;
						}
						// We must be careful not to instantiate beans eagerly as in this case they
						// would be cached by the Spring container but would not have been weaved.
						Class<?> beanType = this.beanFactory.getType(beanName);
						if (beanType == null) {
							continue;
						}
                          //判断Bean是否有@Aspect注解
						if (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 {
								// Per target or per this.
								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();
		}
         //缓存通知方法
		List<Advisor> advisors = new LinkedList<>();
		for (String aspectName : aspectNames) {
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			if (cachedAdvisors != null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory));
			}
		}
		return advisors;
	}

这个方法的实现流程如下:

  1. 获取容器中注册的所有beanName;
  2. 遍历所有beanName,并找到配置@Aspect注解的切面类;
  3. 解析获取切面类中配置的通知方法;
  4. 缓存最后获取的通知方法。

而其中通知方法的解析是交给ReflectiveAspectJAdvisorFactory的getAdvisors()方法是实现的,

	@Override
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		validate(aspectClass);
		// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
		// so that it will only instantiate once.
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
		List<Advisor> advisors = new LinkedList<>();
         // 遍历配置@Pointcut的方法
		for (Method method : getAdvisorMethods(aspectClass)) {
             //获取配置切面的通知方法
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}
		// If it's a per target aspect, emit the dummy instantiating aspect.
		if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
			Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
			advisors.add(0, instantiationAdvisor);
		}
		// Find introduction fields.
		for (Field field : aspectClass.getDeclaredFields()) {
			Advisor advisor = getDeclareParentsAdvisor(field);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}

		return advisors;
	}

这里有两个重要方法:一个是getAdvisorMethods(),获取切面类中配置@Pointcut的方法,

	private List<Method> getAdvisorMethods(Class<?> aspectClass) {
		final List<Method> methods = new LinkedList<>();
		ReflectionUtils.doWithMethods(aspectClass, method -> {
			// Exclude pointcuts
			if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
				methods.add(method);
			}
		});
		Collections.sort(methods, METHOD_COMPARATOR);
		return methods;
	}

一个是getAdvisor(),获取配置切点@Pointcut的几种通知方法(Before、Around、After、AfterReturning、 AfterThrowing),

	@Override
	@Nullable
	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
			int declarationOrderInAspect, String aspectName) {
		validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
		AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
		if (expressionPointcut == null) {
			return null;
		}
         //封装切点和通知方法信息
		return 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;
		}
         //封装对象
		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;
	}

	@Nullable
	protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
		Class<?>[] classesToLookFor = new Class<?>[] {
				Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
		for (Class<?> c : classesToLookFor) {
			AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
			if (foundAnnotation != null) {
				return foundAnnotation;
			}
		}
		return null;
	}

这几个方法的主要作用就是将获取的切点及通知方法信息封装成InstantiationModelAwarePointcutAdvisorImpl对象,同时这个对象里面定义了不同注解通知的实现策略(感兴趣就可以看下实现类的源码),分别是:

  • AspectJMethodBeforeAdvice
  • AspectJAfterAdvice
  • AspectJAfterReturningAdvice
  • AspectJAfterThrowingAdvice
  • AspectJAroundAdvice

后面调用相应通知的代理方法时就是由它们去处理实现的。

生成代理对象

完成上面的切面类的解析之后,那下一步肯定就需要根据定义的切点表达式筛选出目标类,并替换为生成的代理对象。

那我们继续往下分析,我们知道在Bean创建完成后,会先进行属性注入,再调用initializeBean()方法来初始化,而initializeBean()方法中会调用applyBeanPostProcessorsAfterInitialization()方法,来应用之前集合中添加的BeanPostProcessor后置处理器进行初始化处理回调,

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
		//遍历BeanPostProcessor后置处理器
		for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
			Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

能看到这里会遍历BeanPostProcessor实现类,并调用其实现的postProcessAfterInitialization()方法,而这里我们主要关注的就是之前添加的AnnotationAwareAspectAutoProxyCreator,但是我们看它的源码,是没有这个方法的,其实具体的方法实现是在它的父类AbstractAutoProxyCreator中,

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
		if (bean != null) {
             //获取缓存的beanName
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

这里先通过earlyProxyReferences来对beanName做缓存判断,如果你了解过依赖注入中循环依赖的解决过程,你就知道这段代码的作用:在对象Bean创建完成后,会先把这个对象的ObjectFactory的放到singletonFactories缓存中来让有依赖的Bean提前拿到对象的引用,而这个就是ObjectFactory封装的就是调用getEarlyBeanReference()返回的Bean,这个方法中也会遍历BeanPostProcessor,并调用getEarlyBeanReference()方法,而它真正调用实现是在AbstractAutoProxyCreator中,

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			this.earlyProxyReferences.add(cacheKey);
		}
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

是不是发现同样的都会调用wrapIfNecessary()方法,所以这里缓存判断的作用就很明显了,就是为了防止重复调用wrapIfNecessary()方法。而这个方法也是我们重点去了解的,

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			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;
	}

是不是发现代码很是熟悉,之前Bean实例化之前就已经提前处理过了并缓存了,我们直接看getAdvicesAndAdvisorsForBean()方法,

	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
         //获取缓存的通知
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
         //获取Bean匹配切点定义的通知
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

这里的作用比较容易理解,就是获取之前缓存的Advisor的封装InstantiationModelAwarePointcutAdvisorImpl类的集合,再匹配当前Bean满足切点定义的通知,结束上层的方法,如果通知为空,则无需要对当前Bean进行代理,反正则会调用createProxy()方法创建当前Bean的代理对象。

我们来看createProxy()方法的实现,

	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);
         //判断配置的proxyTargetClass
		if (!proxyFactory.isProxyTargetClass()) {
             //确定Bean是否应该用它的目标类而不是它的接口来代理
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
                  //有接口则添加代理接口,没有则设置proxyTargetClass为true
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
         //加入通知拦截器
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);
		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		return proxyFactory.getProxy(getProxyClassLoader());
	}

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

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			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.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

方法的最后就是通过选择不同的代理机制来创建代理类,主要就是JDK和CGLIB两种动态代理机制,前文也已经简要介绍过了它们的不同点,至于两种代理机制更详细的区别及实现,有机会单独再写一篇了。

到此代理对象的生成就结束了。

调用代理方法

等IOC容器初始化完成之后,断点一下你会发现所有被代理的对象的引用都是l类似这样$Proxy27@2699,那么当我们调用其中被通知的方法会是怎样一个流程呢?

以JDK动态代理机制为例,我们来探究下,首先会调用JdkDynamicAopProxy的invoke()方法,

	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation;
		Object oldProxy = null;
		boolean setProxyContext = false;
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;
		try {
			//判断是否是eqauls()方法
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			//判断是否是hashCode()方法
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}
             //判断是否是代理类
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			//判断是否是Advised接口或者其父接口中定义的方法
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}
			Object retVal;
			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()) {
				// 直接反射调用
				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);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

在方法中,先获取代理类中的通知拦截器链,再调用ReflectiveMethodInvocation的proceed()方法;我们先看下getInterceptorsAndDynamicInterceptionAdvice()方法中是怎么把Advisor转换成

	@Nullable
	public Object proceed() throws Throwable {
		//执行完Interceptor则执行joinPoint
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
		Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		//如果要动态匹配joinPoint
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			//匹配运行时参数
			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				//调用下一个Interceptor
				return proceed();
			}
		}
		else {
			//执行当前Intercetpor
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

这里的获取的interceptorOrInterceptionAdvice其实就是之前说的几种不同注解通知的策略类,分别会调用它们实现的invoke()方法(不同策略的方法实现可以去看看,这里就不贴源码了),完成配置的通知方法内的自定义实现,并且在完成前后会调用invokeJoinpoint()方法,而invokeJoinpoint()方法的本质就是直接通过反射调用被代理类中的目标方法。

到此Spring中完整的AOP实现过程就结束了。


把一件事做到极致就是天分!

公众号:风动草

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值