一文学习 Spring AOP 源码全过程

一是 在spring aop 最初的入口是在哪?

二是 spring 如何解析配置, 如何封装关于AOP概念配置对象的?

三是 spring 如何根据配置对象创建代理对象?

四是 调用代理对象方法的过程, spring 是如何拦截的?

一、Spring AOP 的入口

AOP 的入口点 (XML)

在XML中配置 <aop: ../>启用Aop标签, 在解析XML自定义标签时, 会拿到 AopNamespaceHandler 命名空间处理器:

复制代码
public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.5+ XSDs
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace in 2.5+
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());其内部的注册的 ConfigBeanDefinitionParser 做了两件事

  1. 注册一个名称为org.springframework.aop.config.internalAutoProxyCreator 的Bean Definition(对AOP处理);
    对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST, 系统会按优先级注册以下三个自动代理创建器之一:

    1. AnnotationAwareAspectJAutoProxyCreator (最高优先级)
    • 当项目中存在AspectJ依赖时启用
    • 支持@Aspect注解的切面、@Before、@After等通知注解
    • 继承自 AspectJAwareAdvisorAutoProxyCreator
    • 能够识别并处理基于注解的切面定义
    1. AspectJAwareAdvisorAutoProxyCreator (第二优先级)
    • 当项目中存在AspectJ依赖但不需要注解支持时启用
    • 主要处理XML配置的切面定义
    • 继承自 InfrastructureAdvisorAutoProxyCreator
    • 提供对AspectJ切点表达式的解析能力
    1. InfrastructureAdvisorAutoProxyCreator (最低优先级)
    • 当项目中不存在AspectJ依赖时启用
    • 只处理基础的advisor,不支持切面编程
    • 是最基础的自动代理创建器
    • 适用于简单的拦截器场景
      注册过程通过AopConfigUtils.registerAutoProxyCreatorIfNecessary方法实现,该方法会根据classpath中是否存在AspectJ相关类来决定注册哪个具体的实现类。

这里对应的是 AspectJAwareAdvisorAutoProxyCreator

AOP 的入口点 (注解)

复制代码
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(AopConfiguration.class);
BicycleService bean = acac.getBean(BicycleService.class);
bean.doProduct("黑色");

关键是在 AnnotationConfigApplicationContext 构造 reader (负责解析注解的) 成员变量时
org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()

复制代码
	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		/**
		 * 构造负责解析注解的对象
		 * 1. 创建了一个标准环境对象
		 * {@link AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry)}
		 * 2. 注册了 internalConfigurationAnnotationProcessor (重点)
		 * {@link org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.core.env.Environment)}
		 *
		 * {@link AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)}
		 *
		 */
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
  1. 创建了一个标准环境对象
  2. 注册了 internalConfigurationAnnotationProcessor
    注册 BeanDefinition 名称是 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 对应的 class 是 ConfigurationClassPostProcessor.class 其实现了 BeanDefinitionRegistryPostProcessor 接口, 它是对注解支持核心Bean

主要处理代码
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

复制代码
/**
	 * Apply processing and build a complete {@link ConfigurationClass} by reading the
	 * annotations, members and methods from the source class. This method can be called
	 * multiple times as relevant sources are discovered.
	 * @param configClass the configuration class being build
	 * @param sourceClass a source class
	 * @return the superclass, or {@code null} if none found or previously processed
	 */
	@Nullable
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		/**
		 * 加了 @Component 注解的处理.
		 *
		 * 处理内部类有 Candidate 的注解情况:
		 * <code>
		 * @Component
		 * class User{
		 *		...
		 * 		@Component
		 *     class Inner{
		 *     }
		 * }
		 *</code>
		 *
		 */
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			/**
			 * 这里面又会递归回来
			 **/
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		/**
		 * 加了 @PropertySource 注解的处理
		 * 总得来说: 它加载到配置文件到 StandardEnvironment 中去, 分类的名称是文件名, 参考: [[StandardEnvironment 变量默认分两类: ]]
		 * 注意这时候仅仅是加载配置文件, 未到填充属性的时候
		 */
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), org.springframework.context.annotation.PropertySource.class,
				PropertySources.class, true)) {
			if (this.propertySourceRegistry != null) {
				/**
				 * 又倒几手去处理
				 */
				this.propertySourceRegistry.processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		/**
		 * 加了 @ComponentScan 注解的处理
		 * 总得来说是: 拿到对应 待扫码的包名等信息, 最终是 用 ClassPathBeanDefinitionScanner 去扫描, 并注册到Registry中
		 *   返回扫描到的 BeanDefinitionHolder
		 */
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class);
		if (!componentScans.isEmpty() &&
				//又来判断下是否跳过
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {// 拿到 @ComponentScan 的注解属性
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				/**
				 * 注意, 倒了一手 用ComponentScanAnnotationParser 解析
				 */
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		/**
		 * 加了 @Import 注解的处理
		 *  1. getImports(sourceClass): 递归 遍历注解中的所有 @Import 注解拿到,收集到对应的 class
		 *  2. 实例化某些需要的 class
		 */
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		/**
		 * 加了 @ImportResource 注解的处理
		 *
		 * @ImportResource 注解用于导入 Spring 的配置文件,如:spring-mvc.xml、application-Context.xm
		 */
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			//拿到 ImportResource 的配置路径数组
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				//同样 处理路径中带变量表达式的(application-${username})
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);

				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		/**
		 * 方法加了 @Bean 注解的处理
		 * 简而言之: 拿到所有 被@Bean注解 的方法 将该方法转 元信息对象(MethodMetadata) 将其添加到 configClass中
		 */
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		/**
		 * 由于JDK 1.8+, 接口也可以有方法实现
		 * 所以这里处理接口default方法带 @Bean 注解的, 也转为元信息对象(MethodMetadata) 添加到 configClass中
		 *
		 */
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

以上仅是注解的支持, 还缺少一个 internalAutoProxyCreator

  • @EnableAspectJAutoProxy 注解内部包含 @Import(AspectJAutoProxyRegistrar.class)
    它负责主要就是负责添加 internalAutoProxyCreator

    复制代码
      @Nullable
      private static BeanDefinition registerOrEscalateApcAsRequired(
      		Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    
      	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
      	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      		BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      		if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
      			int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
      			int requiredPriority = findPriorityForClass(cls);
      			if (currentPriority < requiredPriority) {
      				apcDefinition.setBeanClassName(cls.getName());
      			}
      		}
      		return null;
      	}
    
      	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
      	beanDefinition.setSource(source);
      	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
      	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
      	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
      	return beanDefinition;
      }

具体需要的 BeanDefinition 是一样的, 无非就是换成注解的形式配置和解析

二、AOP 相关 BeanDefinition 的解析过程 (XML)

https://img2024.cnblogs.com/blog/3723410/202603/3723410-20260319222734184-1498589285.png

AOP 三剑客的包含关系

org.springframework.aop.config.ConfigBeanDefinitionParser 解析 <aop:config> 标签的子元素 (pointcut, advisor, aspect)

每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 的 BeanDefinition 将其注册到 BeanFactory
org.springframework.aop.config.ConfigBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)

复制代码
private void parseAspect(Element aspectElement, ParserContext parserContext) {
	/**
	 * 切面 id 和 ref 属性
	 * <aop:aspect ref="..." id="beforeExample">
	 */
	String aspectId = aspectElement.getAttribute(ID);
	String aspectName = aspectElement.getAttribute(REF);

	try {
		this.parseState.push(new AspectEntry(aspectId, aspectName));
		List<BeanDefinition> beanDefinitions = new ArrayList<>();
		List<BeanReference> beanReferences = new ArrayList<>();

		List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
		for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
			Element declareParentsElement = declareParents.get(i);
			beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
		}

		// We have to parse "advice" and all the advice kinds in one loop, to get the
		// ordering semantics right.
		/**
		 * 1. 解析切面的 子元素
		 * <aop:aspect ...
		 */
		NodeList nodeList = aspectElement.getChildNodes();
		boolean adviceFoundAlready = false;
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node node = nodeList.item(i);
			/**
			 * 是否是通知(Advice)配置元素:
			 *  '<aop:before', '<aop:after', '<aop:after-returning', '<aop:after-throwing' or '<aop:around'.
			 */
			if (isAdviceNode(node, parserContext)) {
				if (!adviceFoundAlready) {
					adviceFoundAlready = true;
					if (!StringUtils.hasText(aspectName)) {
						parserContext.getReaderContext().error(
								"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
								aspectElement, this.parseState.snapshot());
						return;
					}
					beanReferences.add(new RuntimeBeanReference(aspectName));
				}
				/**
				 * 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 类型的BeanDefinition 然后将其注册到 BeanFactory
				 * 	AspectJPointcutAdvisor 内部包含五种通知:  AspectJAfterReturningAdvice AspectJAfterAdvice AspectJAroundAdvice AspectJMethodBeforeAdvice AspectJAfterThrowingAdvice
				 *  AspectJPointcutAdvisor 主要有三个关键属性, 包括:
				 *  1. java.lang.reflect.Method(通知切面的方法)
				 *	2. org.springframework.aop.aspectj.AspectJExpressionPointcut(切入点表达式)
				 * 	3. org.springframework.aop.aspectj.AspectInstanceFactory (切面实例工厂)
				 *
				 */
				AbstractBeanDefinition advisorDefinition = parseAdvice(
						aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
				beanDefinitions.add(advisorDefinition);
			}
		}

		AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
				aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
		parserContext.pushContainingComponent(aspectComponentDefinition);

		List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
		for (Element pointcutElement : pointcuts) {
			parsePointcut(pointcutElement, parserContext);
		}

		parserContext.popAndRegisterContainingComponent();
	}
	finally {
		this.parseState.pop();
	}
}

三、AOP 代理对象 的创建过程

https://img2024.cnblogs.com/blog/3723410/202603/3723410-20260307173316974-1553110752.jpg

AOP 处理入口点 (BPP)

ApplicationContext 在容器创建bean流程中 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, RootBeanDefinition, Object[])} 会调用接口方法

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

如果其该返回不为null, 则直接使用这个bean实例返回, 不走正常实例bean流程.

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition)

复制代码
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// 如果是合成的(mbd是AOP的时候为true) 并且实现 InstantiationAwareBeanPostProcessor 接口
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					/**
					 * 调用 InstantiationAwareBeanPostProcessor
					 * #postProcessBeforeInstantiation
					 * #postProcessAfterInitialization
					 */
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

AspectJAwareAdvisorAutoProxyCreator 创建代理对象

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary

复制代码
/**
	 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
	 * @param bean the raw bean instance
	 * @param beanName the name of the bean
	 * @param cacheKey the cache key for metadata access
	 * @return a proxy wrapping the bean, or the raw bean instance as-is
	 */
	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;
		}
		/**
		 * 拿到所有匹配织入当前bean的 所有通知器(Advisor)
		 * 做了三件事, 见: {@link org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(java.lang.Class, java.lang.String, org.springframework.aop.TargetSource)}
		 * 1. 往返回 `AspectJXXXAdvice`列表数组`0`索引 插入一个{@link org.springframework.aop.interceptor.ExposeInvocationInterceptor} 实例
		 * 方便传递参数用的
		 *
		 * 2. 怎么匹配(Advisor)?
		 * Advisor中的 `AspectJExpressionPointcut` 是实现 {@link ClassFilter} 和 {@link org.springframework.aop.MethodMatcher} 接口
		 * 一个进行类匹配, 一个进行方法匹配.
		 *
		 * 3.  排序, 基于 `有向无环` 图进行排序; 可能匹配到多个切面(aspect)
		 *
		 */
		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			/**
			 *{@link org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#advisedBeans}
			 * 这个变量缓存所有处理过的 bean名称, value 为 boolean值, 如果为false 则不处理
			 */
			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;
	}
织入

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#buildProxy

复制代码
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
			AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
		}
		/**
		 *
		 * 1. 创建 代理工厂
		 * {@link org.springframework.aop.framework.ProxyFactory}
		 */
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
			if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 尝试获取所有接口
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		/**
		 * 所有匹配的 Advisor
		 */
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		/**
		 * 被代理的对象
		 */
		proxyFactory.setTargetSource(targetSource);
		/**
		 * 2.定制化 ProxyFactory (预留扩展的)
		 */
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = smartClassLoader.getOriginalClassLoader();
		}
		/**
		 * 3.根据 classOnly 决定返回的是: 代理对象的class, 还是 代理对象的实例
		 */
		return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
	}
  • 根据实现方式, 创建代理对象
  • CGLIB 见 @link org.springframework.aop.framework.CglibAopProxy#buildProxy(java.lang.ClassLoader, boolean)
  • JDK 见 @link org.opspringframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)

org.springframework.aop.framework.CglibAopProxy#buildProxy

复制代码
	/**
	 *
	 * @param classLoader
	 * @param classOnly
	 * @return
	 */
	private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: " + 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;
			if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			/**
			 * Enhancer 对象
			 */
			// Configure CGLIB Enhancer...
			Enhancer enhancer = createEnhancer();
			if (classLoader != null) {
				enhancer.setClassLoader(classLoader);
				if (classLoader instanceof SmartClassLoader smartClassLoader &&
						smartClassLoader.isClassReloadable(proxySuperClass)) {
					enhancer.setUseCache(false);
				}
			}
			/**
			 * 设置父类
			 */
			enhancer.setSuperclass(proxySuperClass);
			enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
			// 命名策略, 生成 Class 的名称标签
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			enhancer.setAttemptLoad(true);
			enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
			/**
			 * cglib
			 * 设置回调; 到时候代理类方法调用的就是这些回调, 有七个 分别是:
			 * @see org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor@2ed2d9cb
			 * @see org.springframework.aop.framework.CglibAopProxy.StaticUnadvisedInterceptor@d5b810e
			 * @see org.springframework.aop.framework.CglibAopProxy.SerializableNoOp@43dac38f
			 * @see org.springframework.aop.framework.CglibAopProxy.StaticDispatcher@342c38f8
			 * @see org.springframework.aop.framework.CglibAopProxy.AdvisedDispatcher@c88a337
			 * @see org.springframework.aop.framework.CglibAopProxy.EqualsInterceptor@5d0a1059
			 * @see org.springframework.aop.framework.CglibAopProxy.HashCodeInterceptor@485966cc
			 */
			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			/**
			 * 回调过滤器, 它来绝对使用哪一个回调
			 */
			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			ProxyCallbackFilter filter = new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);
			enhancer.setCallbackFilter(filter);
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			// ProxyCallbackFilter has method introspection capability with Advisor access.
			try {
				return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
			}
			finally {
				// Reduce ProxyCallbackFilter to key-only state for its class cache role
				// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...
				filter.advised.reduceToAdvisorKey();
			}
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			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",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

AOP 的两种织入原理

1.静态AOP (AspectJ)

静态AOP:在编译期,切面直接以字节码的形式编译到目标字节码文件中。

AspectJ 属于静态AOP,是在编译时进行增强,会在编译的时候将AOP逻辑织入到代码中,需要专有的编译器和织入器。

优点:被织入的类性能不受影响。

缺点:不够灵活

javassist

\[Java 字节码指令\]

2.动态AOP
JDK 动态代理

\[10.结构型 - 代理模式 (Proxy Pattern)\]

在正常开发中, 基于 依赖倒置原则(Dependence Inversion Principle), 通常需要接口解决 上层与类的耦合问题;

即A类调用B类, 为了解决A依赖与B, 抽取B做成C接口. 变成A依赖以C接口;

在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。

Java从1.3引入动态代理。实现原理是为被代理的业务接口生成代理类,将AOP逻辑写入到代理类中,在运行时动态织入AOP,使用反射执行织入的逻辑。

主要实现方式依赖 java.lang.reflect 包下的InvocationHandler和Proxy类。

优点:Java标准库原生支持,使用简单,无需引用额外的包。相对于静态AOP更灵活。

缺点:带代理的类必须是接口,灵活性受到一些限制;使用反射会影响一些性能。

JDK 动态代理必须需要接口, 它的原理是动态的生成class, 类似实现原始类的接口

复制代码
/**
 * JDK 动态代理实现
 */
public static final void JDK_PROXY(){
	/**
	 * 目标类
	 */
	ProductServiceInter carService  = new CarService();
	// 拦截回调处理器
	InvocationHandler handler = new InvocationHandler() {
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			Object result ;
			System.out.println("增强处理 前");
			//调用方法
			result = method.invoke(carService, args);
			System.out.println("增强处理 后");
			return result;
		}
	};
	ProductServiceInter proxyInstance = (ProductServiceInter) Proxy.newProxyInstance(CarService.class.getClassLoader()
			, CarService.class.getInterfaces(), handler);
	proxyInstance.doProduct("白色");

}
CGLIB 动态代理

CGLIB(Code Generation Library)是一个开源项目,是一个强大的, 高性能, 高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口, 其实就是cglib可以在运行时动态生成字节码;

使用CGLib实现动态代理, 完全不受代理类必须实现接口的限制, 而且CGLib底层采用ASM字节码生成框架, 使用字节码技术生成代理类, 比使用Java反射效率要高; 唯一需要注意的是, CGLib不能对声明为final的方法进行代理, 因为CGLib原理是动态生成被代理类的子类;

它的原理是: 动态的创建一份class(代理类) 继承/实现被代理的 类/接口

  • net.sf.cglib.proxy.Enhancer -- 主要的增强类

  • net.sf.cglib.proxy.MethodInterceptor -- 主要的方法拦截类

    它是Callback接口的子接口, 需要用户实现

  • net.sf.cglib.proxy.MethodProxyJDK的java.lang.reflect.Method类的代理类

    可以方便的实现对源对象方法的调用,如使用: Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象, 也不会出现死循环的问题;

  • net.sf.cglib.proxy.MethodInterceptor 接口是最通用的回调(callback)类型

    它经常被基于代理的AOP用来实现拦截(intercept)方法的调用; 这个接口只定义了一个方法,public Object intercept(Object object, java.lang.reflect.Method method,Object[] args, MethodProxy proxy) throws Throwable;

    第一个参数是代理对像, 第二和第三个参数分别是拦截的方法和方法的参数; 原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用, 或者使用 net.sf.cglib.proxy.MethodProxy对象调用;

  • sf.cglib.proxy.MethodProxy通常被首选使用, 因为它更快;

  • 一个动态代理例子

    /**

    • CGLIB 动态代理实现
      /
      public static final void CJLIB_PROXY(){
      Enhancer enhancer = new Enhancer();
      /
      *
      • 设置父类
        */
        enhancer.setSuperclass(CarService.class);
        // 拦截回调处理器
        MethodInterceptor handler = new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object result ;
        System.out.println("增强处理 前");
        //调用父对象 方法
        result = proxy.invokeSuper(obj, args);
        System.out.println("增强处理 后");
        return result;
        }
        };
        // 设置 拦截回调处理器; 它可以是: * @see MethodInterceptor * @see NoOp * @see LazyLoader * @see Dispatcher * @see org.springframework.cglib.proxy.InvocationHandler * @see FixedValue
        enhancer.setCallback(handler);
        CarService proxyInstance = (CarService) enhancer.create();
        proxyInstance.doProduct("粉色");
        }

四、AOP 代理对象的方法调用过程

https://img2024.cnblogs.com/blog/3723410/202602/3723410-20260224191135364-2132190076.png

ExposeInvocationInterceptor 的作用

把它放在第0位优先调用它, 把 AOP 调用的核心上下文(MethodInvocation) 存入 ThreadLocal<MethodInvocation>
org.springframework.aop.interceptor.ExposeInvocationInterceptor

复制代码
public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {

	//全局单例实例(Spring AOP 所有代理都会默认添加这个拦截器)
	/** Singleton instance of this class. */
	public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();

	/**
	 * Singleton advisor for this class. Use in preference to INSTANCE when using
	 * Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
	 */
	public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
		@Override
		public String toString() {
			return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
		}
	};

	// 存储当前线程的 MethodInvocation 上下文
	private static final ThreadLocal<MethodInvocation> invocation =
			new NamedThreadLocal<>("Current AOP method invocation");
	/**
	 * Ensures that only the canonical instance can be created.
	 */
	private ExposeInvocationInterceptor() {
	}
	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		//存储在ThreadLocal的上下文关键信息 mi.getMethod();// 当前调用的方法  mi.getArguments();// 方法参数  mi.getThis();   // 目标对象(原 Bean)
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}
...

源码调试主入口

主入口构成一个责任链的形式
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

复制代码
@Override
	@Nullable
	public Object proceed() throws Throwable {
		/**
		 * 这里 循环责任链 的调用
		 */
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
		/**
		 * 这个`this.interceptorsAndDynamicMethodMatchers` 数组 就是通知(advised)列表 AspectJXXXAdvice (这里再封装为 XXXAdviceInterceptor)
		 * 第0个是此前插入的 `ExposeInvocationInterceptor` 它哈都没干, 直接 proceed(); 回调回来;
		 * 
		 * 这里累加 ++this.currentInterceptorIndex, 每次回调回来, 拿到下一个 `XXXAdviceInterceptor` 调用
		 */
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher dm) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.matcher().matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor().invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			/**
			 * 将 `this` 传递进去, 各 XXXAdviceInterceptor 将使用这个参数, 回调回来
			 */
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
Around 通知

org.springframework.aop.aspectj.AspectJAroundAdvice#invoke

复制代码
@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
	if (!(mi instanceof ProxyMethodInvocation pmi)) {
		throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
	}
	/**
	 * 封装一个 ProceedingJoinPoint 参数, 这个就是给 环绕通知方法 接受的参数
	 */
	ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
	JoinPointMatch jpm = getJoinPointMatch(pmi);
	// 环绕通知内部, 调用 `joinPoint.proceed();` 其实还是会回去责任链那里的, 并不是真正的调用原始方法
	return invokeAdviceMethod(pjp, jpm, null, null);
}
After 通知

没啥重点
org.springframework.aop.aspectj.AspectJAfterReturningAdvice#afterReturning

复制代码
	@Override
	public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
		if (shouldInvokeOnReturnValueOf(method, returnValue)) {
			invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
		}
	}
Throwing 通知

org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke

复制代码
@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}

Spring AOP 的使用实例

一个案例

An AOP Example - https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/example.html

  1. 例 有一个 ProductServiceInter 接口

    public interface ProductServiceInter {
    void doProduct(String color);
    }

  2. 有两个实现

    public class CarService implements ProductServiceInter {
    public void doProduct(String color){
    System.out.println("生产一辆 " + color + " 颜色的汽车");
    }
    }

    public class BicycleService implements ProductServiceInter {
    public void doProduct(String color){
    System.out.println("生产一辆 " + color + " 颜色的自行车");
    }
    }

业务需求

  1. 调用 BicycleService#doProduct 方法前, 需要记录日志
  2. 调用 CarService#doProduct 方法前, 同样也需要记录日志

解决方式(过程式)

复制代码
ProductServiceInter carService  = new CarService();
ProductServiceInter bicycleService = new BicycleService();
//....写日志
carService.doProduct("红色");  
//....写日志
bicycleService.doProduct("黑色");
问题

有几点不合理的地方:

  1. 写日志部分代码重复
  2. 大量的 service 都有此需要,又有少量的 service 没这个需要, 不好修改

AOP 概念术语

AOP Concepts - https://docs.spring.io/spring-framework/reference/core/aop/introduction-defn.html

  • Advice

    /通知/建议/增强处理/... //即: 增强功能这一部分代码

  • joinpoint

    /连接点 //和方法有关的前前后后(抛出异常), 都是连接点;

  • Pointcut

    /切入点 //筛选连接点, 即: 哪些方法需要被代理

  • Aspect

    /切面 //切面是通知(写日志)切入点(要被代理的方法) 的结合, 即:修改后的整个方法 //(有正常需要调用的(doProduct)代码,又有写日志的)

  • target

    /目标/目标对象 //即: Service 它们都没有 写日志的功能

  • weaving

    /织入 //把切面应用到目标对象来创建新的代理对象的过程;

  • AOP代理类

    /AOP修改后的类

基于XML 的配置方式

Schema-based AOP Support

复制代码
public class AdviceService {
	public void before(){
		System.out.println("前置通知(Before),它在执行方法之前调用");
	}
	public void after(){
		System.out.println("后置通知(After returning advice),它在执行匹配pointcut的方法之后调用");
	}
	public void after_throwing(Exception ex){
		System.out.println("异常通知(After throwing advice),它在执行匹配pointcut的方法时出现异常调用");
		System.out.println("异常信息:"+ ex.getMessage() );
	}
	
	public Object around(ProceedingJoinPoint mi){
		System.out.println("环绕通知(Around advice)之前");
		Object result = null;
		try {
			result =  mi.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("环绕通知(Around advice)之后");
		return result;
	}

}

<!-- 三个普通的bean -->
<bean id="bicycleService" class="org.yang.learn.spring.aop.service.BicycleService"></bean>
<bean id="carService" class="org.yang.learn.spring.aop.service.CarService"></bean>
<bean id="adviceService" class="org.yang.learn.spring.aop.aspect.AdviceService"></bean>

<!-- 需要引入aop 命名空间
 xmlns:aop="http://www.springframework.org/schema/aop"
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<aop:config>
	<!-- 把容器中的普通bean 变成一个切面bean -->
	<!-- 方式一: 切点, 切面 散装形式的定义
	为了方便使用可以先定义 pointcut (切入点)
		<aop:pointcut expression="execution(* com.yang.service.Test*_Service.*(..))" id="bbb"/>
		<aop:aspect ref="myAdvice">
			<aop:before method="log" pointcut-ref="bbb" />
		</aop:aspect>
	-->
	<!-- 把容器中的普通bean 变成一个切面bean -->
	<!-- 方式二: 切点, 切面 集成形式的定义-->
	<aop:aspect ref="adviceService" id="beforeExample">

	<!--
	Before Advice: https://docs.spring.io/spring-framework/reference/core/aop/schema.html#aop-schema-advice-before
	调用 pointcut 之前会 调用 aspect bean 的 before 方法
	-->
		<aop:before method="before"
					pointcut="execution(* org.yang.learn.spring.aop.service.*.*(..))" />
	<!--
	After Returning Advice: https://docs.spring.io/spring-framework/reference/core/aop/schema.html#aop-schema-advice-after-returning
	调用 pointcut 之后会 调用 aspect bean 的 after 方法
	-->
		<aop:after-returning method="after"
					pointcut="execution(* org.yang.learn.spring.aop.service.*.*(..))" />
					
	<aop:around method="around"  
					 pointcut="execution(* org.yang.learn.spring.aop.service.*.*(..))" />

	</aop:aspect>
</aop:config>


public static void main(String[] args) {
	System.out.println("==========================================================");  
	System.out.println("");  
	ApplicationContext context = new ClassPathXmlApplicationContext("application-aop.xml");  
	/**  
	* 如果目标bean有接口的话, 会使用JDK动态代理实现;  
	* 获取的也是接口 (ProductServiceInter)  
	*/
	ProductServiceInter carService = (ProductServiceInter) context.getBean("carService");  
	carService.doProduct("五颜六色");  
	System.out.println("");  
	System.out.println("==========================================================");
}
表达式 (execution)

使用 execution 表达式, 需要引入aop项目的 optional("org.aspectj:aspectjweaver") 依赖

execution 表达式的格式, 括号中各个pattern分别表示:

修饰符匹配(modifier-pattern?)

返回值匹配(ret-type-pattern)

类路径匹配(declaring-type-pattern?)

方法名匹配(name-pattern)

参数匹配 (param-pattern)

异常类型匹配(throws-pattern?)

其中后面跟着"?"的是可选项 定义时, 还可以使用&&, ||, !

其他通知 (Advice)

spring有如下几种 Advice (通知)

  • 前置通知(Before advice)

    在某连接点之前执行的通知.
    https://docs.spring.io/spring-framework/reference/core/aop/schema.html#aop-schema-advice-before

  • 后置通知(After returning advice)

    在某连接点正常完成后执行的通知.
    https://docs.spring.io/spring-framework/reference/core/aop/schema.html#aop-schema-advice-after-returning

  • 异常通知(After throwing advice):

    在方法抛出异常退出时执行的通知;

  • 最终通知(After (finally) advice):

    当某连接点退出的时候执行的通知(不论是正常返回还是异常退出);

  • 环绕通知(Around Advice):

    包围一个连接点的通知, 如方法调用; 这是最强大的一种通知类型; 环绕通知可以在方法调用前后完成自定义的行为; 它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行;

    <bean id="bicycleService" class="org.yang.learn.spring.aop.service.BicycleService"></bean>
    <bean id="carService" class="org.yang.learn.spring.aop.service.CarService"></bean>

    <bean id="myAdvice" class="org.yang.learn.spring.aop.aspect.AdviceService"></bean>

    aop:config

    复制代码
      <aop:aspect ref="myAdvice">
      	<!-- 
      		前置通知(Before)
      		后置通知(After returning advice)
      		异常通知(After throwing advice)
      		最终通知(After (finally) advice)
      		环绕通知(Around Advice)
      		method 指定织入 
      		pointcut 指定匹配目标类,方法 * 号为通配符 (..)不限定参数
      		-->
      		
      	<!--前置通知(Before) 只要 Before Advice执行完成,
      		// 目标方法总会被调用
      		// 可以通过抛出异常来阻止目标方法的调用
      	<aop:before method="before" pointcut="execution(* com.yang.service.Test*_Service.before(..))" />
      	-->
      	
      	<!--后置通知(After returning advice)
      		// 在目标方法调用之后,无论是否成功结束织入增加处理
      		// 不能阻止目标方法的调用
      		// 它有点类似 finlly 块
      	<aop:after method="after" pointcut="execution(* com.yang.service.Test*_Service.*(..))" />	
      	-->
      	
      	<!--必须正常return, returning参考异常通知ex,
      		// 即使没有返回值 也会执行,
      		// 不同的是它可以访问返回值
      	<aop:after-returning method="" returning="ret"/> 
      	-->
      	
      	<!--异常通知(After throwing advice),它在执行匹配pointcut的方法时出现异常调用
      		// throwing="ex" 意味着after_throwing方法可以,public void after_throwing(ArithmeticException ex)
      		// 定义 ex 参数接受该异常,注意 这个ex类型,只有发生ArithmeticException异常,类型相同,它才会调用
      	<aop:after-throwing method="after_throwing" pointcut="execution(* com.yang.service.Test1_Service.*(..))" 
      		throwing="ex"/>	
      	-->
      	
      	<!-- 最终通知(After (finally) advice) 自己看文档-->
    
      	
      	<!-- 环绕通知(Around Advice,在目标方法之前调用,它的处理方法必须包含ProceedingJoinPoint的形参
      		// 最强大通知,
      		// 可以阻止目标方法的调用
      		// 可以方法,参数,返回值
      		// 甚至可以修改,参数,返回值
      	<aop:around method="around" pointcut="execution(* com.yang.service.Test1_Service.*(..))" />
      		-->
      </aop:aspect>

    </aop:config>

基于注解 的配置方式

@AspectJ support - https://docs.spring.io/spring-framework/reference/core/aop/ataspectj.html

启用注解支持
复制代码
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
配置切面 Aspect
复制代码
@Aspect
@Component
public class MyAdvice {
}
配置切点 Pointcut
复制代码
@Aspect
@Component
public class MyAdvice {
   /**
     * 定义 pointcut (切入点)
     */
    // @Pointcut("@annotation(net.jk.cloud.log.aop.log.Log)") //按照注解的方式, 只要在方法上加`net.jk.cloud.log.aop.log.Log`注解, 即会被织入
	@Pointcut("execution(* com.yang.service.Test*_Service.*(..))")//按照表达式
    public void logPointcut() {
        // 该方法无方法体,主要为了使用此切入点
    }

	/**
	@Around("logPointcut()") 环绕通知使用 logPointcut() 切点; 相当于XML的
	<aop:aspect ref="myAdvice">
		<aop:around method="logAround" 
			pointcut="execution(* com.yang.service.Test*_Service.*(..))" />
	</aop:aspect>

	例如 相对应的异常通知
	 @AfterThrowing(pointcut = "logPointcut()", throwing = "e") = <aop:after-throwing method="logAround" ...
	**/
	@Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        currentTime = System.currentTimeMillis();
        result = joinPoint.proceed();
        Log log = new Log(Log.TYRPE_INFO,System.currentTimeMillis() - currentTime);
        logService.save(getUsername(), StringUtils.getIP(RequestHolder.getHttpServletRequest()),joinPoint, log);
        return result;
    }
}
配置通知 Advice
复制代码
@Aspect
@Component
public class MyAdvice {
   /**
     * 定义 pointcut (切入点)
     */
    // @Pointcut("@annotation(net.jk.cloud.log.aop.log.Log)") //按照注解的方式, 只要在方法上加`net.jk.cloud.log.aop.log.Log`注解, 即会被织入
	@Pointcut("execution(* com.yang.service.Test*_Service.*(..))")//按照表达式
    public void logPointcut() {
        // 该方法无方法体,主要为了使用此切入点
    }

	/**
	@Around("logPointcut()") 环绕通知使用 logPointcut() 切点; 相当于XML的
	<aop:aspect ref="myAdvice">
		<aop:around method="logAround" 
			pointcut="execution(* com.yang.service.Test*_Service.*(..))" />
	</aop:aspect>
	
	例如, 相对应的异常通知
	 @AfterThrowing(pointcut = "logPointcut()", throwing = "e") = <aop:after-throwing method="logAround" ...
	**/
	@Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        currentTime = System.currentTimeMillis();
        result = joinPoint.proceed();
        Log log = new Log(Log.TYRPE_INFO,System.currentTimeMillis() - currentTime);
        logService.save(getUsername(), StringUtils.getIP(RequestHolder.getHttpServletRequest()),joinPoint, log);
        return result;
    }
}

踩坑指南

spring boot 项目使用了AOP代理的Bean CglibAopProxy 方法是存在缓存的, 注意缓存 注意缓存 注意缓存!

相关推荐
Jay Kay2 小时前
生成式推荐模型学习记录part1
学习
Nyarlathotep01132 小时前
ThreadLocal
java·后端
菜鸟小九3 小时前
JUC(入门1-3章)
java·juc
LJianK13 小时前
Java中的类、普通类,抽象类,接口的区别
java·开发语言
LiLiYuan.3 小时前
【Java线程 vs 虚拟机线程】
java·开发语言
2402_881319304 小时前
跨服务通信兜底机制-Java 回传失败无持久重试队列,报告可能静默丢失。
java·开发语言·python
明灯伴古佛4 小时前
面试:对Spring AOP的理解
java·spring·面试
Nyarlathotep01134 小时前
ConcurrentHashMap源码分析
java·后端
正经教主4 小时前
【docker基础】0、系统学习docker之总计划
学习·docker·容器