Spring AOP实现原理(1)-AOP如何实现切入代码

本文基于Spring Boot 2.4.0,深入剖析Spring AOP实现原理。介绍了@EnableAspectJAutoProxy注解,分析AnnotationAwareAspectJAutoProxyCreator对bean的处理,阐述Spring如何为满足条件的bean生成代理实例。还解答开篇问题,指出同一类方法调用增强不执行的原因,并给出解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

如下代码在配置完全正常的情况下控制台会有几条输出?

@SpringBootTest
class SpringaopApplicationTests {
    @Autowired
    private UserService userService;
    @Test
    void userTest() {
        userService.save();
    }

}

简单业务代码

public interface UserService {
    void save();
    void saveContacts();
}


@Service
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("save user logic");
        saveContacts();
    }

    @Override
    public void saveContacts() {
        System.out.println("save user contacts logic");
    }
}

 AOP代码

@Aspect
@Component
public class UserAOP {
    @Pointcut("execution(public * com.learn.springaop.service..*(..))")
    public void servicePointcut() {
    }

    @After("servicePointcut()")
    public void after(JoinPoint joinPoint) throws Throwable {
        String name=joinPoint.getSignature().toString();
        System.out.println(String.format("日志记录 %s",name));
    }
}

对Spring比较了解的同学,答案应该不难得出。答案还是需要卖下关子,等分享完Spring AOP后再揭晓。

我这里不讲高大上的理论,也不讲什么是AOP,AOP与OOP的思想。只讲我们写了一个Spring AOP后为什么能实现这样的结果,spring是如何帮忙我们实现的。本篇是属于AOP系列的第一篇。

这里分享的是基于Spring Boot 2.4.0。

首先看@EnableAspectJAutoProxy 这个注解,注解上的@Import AspectJAutoProxyRegistrar.class

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}

 org.springframework.context.annotation.AspectJAutoProxyRegistrar

@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

下面是AnnotationAwareAspectJAutoProxyCreator类的UML图,从UML图我们发现AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor的接口。大家都知道BeanPostProcessor接口在Spring的容器里是Bean实例生命周期里非常重要的一步。BeanPostProcessor的postProcessBeforeInitialization(Object bean, String beanName),postProcessAfterInitialization(Object bean, String beanName)方法可以给开发者对bean实例初始化前和初始化后可以做些自定义的处理。那AnnotationAwareAspectJAutoProxyCreator又对bean做了什么特殊的处理呢?

通过阅读源码我们可以在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator类实现了BeanPostProcessor的postProcessAfterInitialization方法。从该方法的注释可以看出这个bean的后置处理器是要针对满足条件的bean创建一个代理。其中getCacheKey是获取一个可以缓存的key。主要看下wrapIfNecessary(bean, beanName, cacheKey)这个方法干了啥

/**
	 * Create a proxy with the configured interceptors if the bean is
	 * identified as one to proxy by the subclass.
	 * @see #getAdvicesAndAdvisorsForBean
	 */
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

从wrapIfNecessary里代码可以看到首先从当前bean的class上获取切面和通知,如果获取到则表示当前实例需要执行AOP切面。需要执行AOP后,Spring就针对当前实例生成了一个代理,并且把这个代理实例返回给Spring容器。我们通过getBean操作获取到的就是这代理类的实例。

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.
        //从当前bean实例上获取通知和切面
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
       //如果获取的结果不等余null,则为当前bean创建一个代理类
		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;
	}

org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean解释了如何获取一个类是否满足AOP的条件。

protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
       //如果没找到满足的切面则返回null
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}


	/**
	 * Find all eligible Advisors for auto-proxying this class.
	 * @param beanClass the clazz to find advisors for
	 * @param beanName the name of the currently proxied bean
	 * @return the empty List, not {@code null},
	 * if there are no pointcuts or interceptors
	 * @see #findCandidateAdvisors
	 * @see #sortAdvisors
	 * @see #extendAdvisors
	 */
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
       //找出所有候选的切面,即在BeanFactory里所有类型是Advisor的实例
       //具体@Aspectj如何解析成Advisor待分析
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
       //从所有候选的切面里找满足当前实例的切面
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply,Pointcut表达式配规则参考org.aspectj.weaver.patterns.SignaturePattern

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
        //如果 切面是Introduction类型的切面判断当前实例是否满足条件,Introduction类型一般用得比较少
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				eligibleAdvisors.add(candidate);
			}
		}
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
         //剩余的其他普通切面是否满足当前实例
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
    //返回所有匹配的切面
		return eligibleAdvisors;
	}

	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
      //如果是PointcutAdvisor类型的切面,则获取切面的Pointcut配置,判断当前实例是否满足Pointcut配置的条件
		else if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pca = (PointcutAdvisor) advisor;
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
    //这里的pc类型是AspectJExpressionPointcut类型是切点,AspectJExpressionPointcut会根据Pointcut表达式生成匹配器,判断当前实例的class是否满足表达式的类匹配
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		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));
       //判断当前实例的方法是否满足切面的条件,只要有一个方法满足Pointcut表达式则返回true
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

找到合适的切面后org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy就为当前实例生成一个Proxy

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 (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				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());
	}

org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)

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

org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (!IN_NATIVE_IMAGE &&
				(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.");
			}
         //如果当前实例实现了接口则用JdkDynamicAopProxy,否则Cglib的方式
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

这里主要看下JdkDynamicAopProxy方法是如何实现的,重点代码是 Proxy.newProxyInstance,大家都知道Spring的AOP的实现是通过代理实现的,原来是这样代理的。从 Proxy.newProxyInstance传的参数看JdkDynamicAopProxy实现了InvocationHandler接口,代理在执行的时候会调用InvocationHandler的invoke方法,下面看看invoke方法的内容

	@Override
	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
     //这里不就是JDK的动态代理的实现吗
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

 可以看到invoke方法里执行的都是切面的通知,具体这些通知如何调用,还是比较复杂的,下一篇分析AOP通知的具体执行过程。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

总结: Spring boot引入spring-boot-starter-aop stater后,开启自动配置会在容器里注册一个实现了BeanPostProcessor的AnnotationAwareAspectJAutoProxyCreator实例。在容器创建每个bean后且实例化后,spring 容器会遍历所有的BeanPostProcessor并执行对应processor的postProcessAfterInitialization方法。AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization会获取所有定义的切面增强(advisors)作为候选,然后循环判断每一个pointcut表达式是否满足当前的实例,如果有满足的advisor,说明该实例需要被AOP增强,则为该实例生成代理实例,并加入Spring容器里,如果没有任何advisor满足则返回原实例。

开篇问题的答案是3条输出:

  • save user logic
  • save user contacts logic
  • 日志记录 void com.learn.springaop.service.impl.UserServiceImpl.save()

结论:既然AOP是通过代理来实现的,且容器里存的实例也是代理后的实例,那么要想增强的部分代码被执行,只要是调用代理的实例肯定都会被执行。那么开篇的问题的结果为什么saveContacts()方法的增强部分没有被执行呢。我们知道调用代理类实例的执行顺序是 Caller->Proxy执行增强的逻辑->真实的实例调用。而在同一个类里面方法间的调用等同与this.saveContacts();而这里的this是指真实的实例而不是被代理的实例。所以他的增强部分代码不会被执行。

破局:如果真实场景遇到这种情况要怎么解决呢。

  1. 尽量规避这种情况(Spring官方推荐)
  2. 在对应class里注入实例自己,在方法内部调用时通过注入的实例调用。或者实现ApplicationContextAware通过Object getBean(String name)方式获取实例调用,所有能实现从容器里获取实例的方式都可以。
  3. 强制转换AopContext.currentProxy()为对应实例的类型后进行调用。Spring官方不推荐使用,因为这样就把代码与AOP耦合了。

延伸:下面是AOP能实现和不能实现的情况

被代理类AOP实现方式private 方法finall方法protected方法default方法不在接口里的方法prototype类型
实现了接口JDK动态代理不存在这种情况不存在这种情况Interface必须publicInterface必须public是(每次生成不同实例的代理)
未实现接口Cglib(生成子类)否(方法不能被重写)否(finall方法不能被重写)是(满足java的可见性规则的调用即可,且pointcut表达式允许)是(满足java的可见性规则的调用即可,且pointcut表达式允许)不存在这种情况是(每次生成不同实例的代理)

         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值