See the principle through the phenomenon: explain in detail the reason why the this call of the bean in Spring causes the AOP to fail

foreword

When we use Spring, some predecessors may have taught us not to use this in beans to call methods annotated with @Async, @Transactional, @Cacheable and other annotations, and the annotations under this will not take effect.

So, have you ever thought about the following questions?

  1. Why does the annotation of the method called by this take effect?
  2. What is the principle of these annotations taking effect?
  3. What should I do if I really need to call the method of this class and the annotation needs to take effect?
  4. Can the proxy make this call annotation take effect directly?

Through this article, the above questions can be solved, and you can learn a lot of relevant principles and knowledge, and the amount of information is large, so let's start

Phenomenon

Taking the @Async annotation as an example, the method marked with the @Async annotation will be processed as an asynchronous call by AOP during execution, and the method will be returned directly when the method is called, and the method marked with @Async will be executed using other threads.

Driven by Spring Boot

@SpringBootApplication
@EnableAsync
public class Starter {

    public static void main(String[] args) {
        SpringApplication.run(Starter.class, args);
    }
}

@Component
public class AsyncService {

    public void async1() {
        System.out.println("1:" + Thread.currentThread().getName());
        this.async2();
    }

    @Async
    public void async2() {
        System.out.println("2:" + Thread.currentThread().getName());
    }
}

@RunWith(SpringRunner.class) 
@SpringBootTest(classes = Starter.class)
public class BaseTest {

    @Autowired
    AsyncService asyncService;

    @Test
    public void testAsync() {
        asyncService.async1();
        asyncService.async2();
    }

}

The output is:

1:main
2:main
2:SimpleAsyncTaskExecutor-2

The first line and the second line correspond to the async1() method, and the third line corresponds to the async2() method. You can see that the thread used when calling asyncService.async2() directly is SimpleAsyncTaskExecutor, and this is called in the async1() method. The result is the main thread, the original calling thread is the same. This means that @Async does not take effect when this is called.

Think & Guess

It is known that for AOP dynamic proxies, non-interface classes use CGLIB-based dynamic proxies, and CGLIB's dynamic proxies create a subclass based on an existing class and instantiate the subclass object. When calling the dynamic proxy object method, the subclass method is called first. In the subclass method, the method is used to enhance the Advice or the interceptor MethodInterceptor processes the subclass method call, and then selectively decides whether to execute the superclass method.

Then assuming that when calling the async1 method, an instance of a dynamically generated subclass is used, then this is actually a subclass instance object based on dynamic proxy, and this call can be intercepted by processing logic such as Advice or MethodInterceptor, so why the theoretical and Is it actually different?

Here is a bold guess. In fact, this in the async1 method is not a subclass object of dynamic proxy, but an original object, so this call cannot be enhanced by dynamic proxy.

Regarding the above AOP dynamic proxy using CGLIB, you can refer to this article to fully understand the AOP implementation principle of the Spring framework .

The detailed analysis begins below.

Source code debugging analysis principle

The first thing to figure out is how @Async works:

1. Analyze Async related components

Starting from the effective entry, the @EnableAsync annotation is marked with @Import(AsyncConfigurationSelector.class)

The role of @Import is to automatically register the imported content in the following @Configuration class, ImportSelector class or ImportBeanDefinitionRegistrar class into the ApplicationContext. Regarding these three classes that can be imported, I will not describe them in detail here. Interested readers can go to the Spring official website to view the documentation or wait for my follow-up articles.

AsyncConfigurationSelector is imported here, and AsyncConfigurationSelector will select the ProxyAsyncConfiguration class for import by default, that is, configure the ProxyAsyncConfiguration class as a @Configuration class into the ApplicationContext. Then the key here is the ProxyAsyncConfiguration class, look at the code

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
		if (this.executor != null) {
			bpp.setExecutor(this.executor);
		}
		if (this.exceptionHandler != null) {
			bpp.setExceptionHandler(this.exceptionHandler);
		}
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
		return bpp;
	}

}

The role of this code is to register the AsyncAnnotationBeanPostProcessor as a Bean in the Context. Then the core is the AsyncAnnotationBeanPostProcessor, the BeanPostProcessor, which is Spring's famous BPP.

After a bean instance is generated, it will be handed over to the postProcessBeforeInitialization method of BPP for processing. At this time, other bean instances compatible with the bean can be returned. For example, the most common one is to return the dynamic proxy object of the original object here.

After this method is executed, the init-related method of the Bean instance will be called. The method called is the afterPropertiesSet method of the InitializingBean interface and the initialization method specified by initMethod in the @Bean declaration.

After calling the init method, the postProcessAfterInitialization method of BPP will be called for post-processing. At this time, the processing is the same as postProcessBeforeInitialization, and the original bean instance can also be replaced.

Let's see what this Async-related BPP does:

// 潜质处理不做任何动作,可保证在调用bean的init之前,bean本身没有任何变化。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
	return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // 如果是AOP相关的基础组件bean,如ProxyProcessorSupport类及其子类,则直接返回。
	if (bean instanceof AopInfrastructureBean) {
		// Ignore AOP infrastructure such as scoped proxies.
		return bean;
	}

	if (bean instanceof Advised) {
	    // 如果已经是Advised的,即已经是被动态代理的实例,则直接添加advisor。
		Advised advised = (Advised) bean;
		if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
		    // 如果没有被frozen(即冷冻,不再做改动的动态代理实例)且是Eligbile(合适的),则把其添加到advisor中。根据配置决定插入位置。
			// Add our local Advisor to the existing proxy's Advisor chain...
			if (this.beforeExistingAdvisors) {
				advised.addAdvisor(0, this.advisor);
			}
			else {
			    advised.addAdvisor(this.advisor);
			}
			return bean;
		}
	}

	if (isEligible(bean, beanName)) {
	    // 如果是Eligible合适的,且还不是被代理的类,则创建一个代理类的实例并返回。
		ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
		if (!proxyFactory.isProxyTargetClass()) {
			evaluateProxyInterfaces(bean.getClass(), proxyFactory);
		}
		proxyFactory.addAdvisor(this.advisor);
		customizeProxyFactory(proxyFactory);
		return proxyFactory.getProxy(getProxyClassLoader());
	}

	// No async proxy needed.
	return bean;
}
// 准备ProxyFactory对象
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);
	// 设置被代理的bean为target,这个bean是真实的bean。
	proxyFactory.setTarget(bean);
	return proxyFactory;
}

After Spring performs AOP proxy on a class, it will add the Advised interface to this class, and the returned dynamic proxy object will be decorated with the Advised interface. Then the purpose of the first piece of logic to judge the bean instanceof Advised is to judge whether it has been dynamically proxied. class, and if so, add an Advisor enhancer to it.

If it is not an object of dynamic proxy, because @Async needs to add proxy to the method and convert it to asynchronous execution, it is necessary to convert the original bean into a bean dynamically proxied by AOP. That is the following logic.

For the detailed principle of the above code to create a dynamic proxy, please refer to my article: Fully understand the AOP implementation principle of the Spring framework .

One more point about @Async: The type of advisor registered above is AsyncAnnotationAdvisor. Including PointCut, the type is AnnotationMatchingPointcut, specifies that only @Async marked methods or similar AOP enhancers will take effect. There is also an Advice, which is used to enhance the method marked by @Async and convert it to asynchronous. The type is AnnotationAsyncExecutionInterceptor, and the invoke method is the place where the real method is actually called. If you are interested, you can study the content carefully, so that you can understand @ The real execution logic of the Async method is now.

The relevant components have been mentioned above and have been briefly analyzed. Now we enter the next stage and analyze the reason why this call does not take effect through the real execution logic.

2. Go deep into the real calling logic

Most of @Async are methods in marked classes, so the implementation of AOP is mostly based on CGLIB. The following takes CGLIB dynamic proxy as an example to analyze the real calling logic.

By fully understanding the article on the AOP implementation principle of the Spring framework , we can know that the real execution logic of a CGLIB-based AOP dynamic proxy bean is in the DynamicAdvisedInterceptor:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	Class<?> targetClass = null;
	Object target = null;
	try {
		if (this.advised.exposeProxy) {
		    // 需要则暴露
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
		// May be null. Get as late as possible to minimize the time we
		// "own" the target, in case it comes from a pool...
		// 重点:获取被代理的目标对象
		target = getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}
		// 获取拦截器链
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		// Check whether we only have one InvokerInterceptor: that is,
		// no real advice, but just reflective invocation of the target.
		if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
			// 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.
			// 如果链是空且是public方法,则直接调用
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = methodProxy.invoke(target, argsToUse);
		}
		else {
			// We need to create a method invocation...
			// 否则创建一个CglibMethodInvocation以便驱动拦截器链
			retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
		}
		// 处理返回值,同JDK动态代理
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
	finally {
		if (target != null) {
			releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

Pay attention to the part of the real call above. In the absence of an advisor, what is actually used is:

methodProxy.invoke(target, argsToUse)

In the case of a proxy, the use is:

new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

In CglibMethodInvocation, after checking the execution of the call chain, the real method will be called: invokeJoinpoint. In CglibMethodInvocation, the implementation of the method is

// CglibMethodInvocation中的实现
protected Object invokeJoinpoint() throws Throwable {
	if (this.publicMethod) {
		return this.methodProxy.invoke(this.target, this.arguments);
	}
	else {
		return super.invokeJoinpoint();
	}
}
// 父类实现是
protected Object invokeJoinpoint() throws Throwable {
	return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

It can be seen that when the method is called, the incoming instances are all targets. This target is obtained from the getTarget method of DynamicAdvisedInterceptor. The code is as follows

protected Object getTarget() throws Exception {
	return this.advised.getTargetSource().getTarget();
}

The advised target is set in the instance method of ProxyFactory: proxyFactory.setTarget(bean);

That is to say, the target is actually the real proxy bean.

Through the above analysis, we can conclude that in a dynamically proxied object, after executing all the enhancement logic of AOP, the proxied object will eventually be used as an instance to call the real method, which is equivalent to calling: target.method ()method. It is concluded from this that in the target.method() method, the this reference must be the target itself, not the generated dynamic proxy object instance.

In addition, after Spring creates a bean, it is a post-processing action to wrap it and generate a dynamic proxy object. Therefore, it will first generate an instance bean of the real class, and then dynamically create a dynamic proxy bean. In the dynamic proxy bean, it will hold the There are real bean instances.

Take the top @Async code example as an example, we can see that this is actually the original instance of AsyncService, not the proxy object instance:AsyncService debug info

Summary: Because the method of AOP dynamic proxy is actually called, it will use the real instance of the proxy object to call the method, so in the instance method, what is obtained through this is the instance of the real object being proxied, not the proxy object itself.

3. Several alternatives to resolve this call

Now that the cause is known, the solution is directed. The core is how to obtain the dynamic proxy object instead of using this to call.

The following methods are available:

  1. Obtain dynamic proxy objects through ApplicationContext
@Component
public class AsyncService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void async1() {
        System.out.println("1:" + Thread.currentThread().getName());
        // 使用AppicationContext来获得动态代理的bean
        this.applicationContext.getBean(AsyncService.class).async2();
    }

    @Async
    public void async2() {
        System.out.println("2:" + Thread.currentThread().getName());
    }

    // 注入ApplicationContext
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

The execution result is:

1:main
2:SimpleAsyncTaskExecutor-2
2:SimpleAsyncTaskExecutor-3

It can be seen that perfection achieves our purpose. In the same way, BeanFactoryAware can achieve the same effect.

  1. Get dynamic proxy object through AopContext
@Component
public class AsyncService {

    public void async1() {
        System.out.println("1:" + Thread.currentThread().getName());
        ((AsyncService) AopContext.currentProxy()).async2();
    }

    @Async
    public void async2() {
        System.out.println("2:" + Thread.currentThread().getName());
    }

}

This is very neat, but it doesn't work by default! Because the currentProxy is not available in AopContext, a null pointer will be reported.

The logic can be seen where the source code is executed by the dynamic proxy above:

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

In the ProxyConfig class, the following annotations are used to explain the role of exposeProxy, which is used to obtain the object of the dynamic proxy in the method.

/**
 * Set whether the proxy should be exposed by the AOP framework as a
 * ThreadLocal for retrieval via the AopContext class. This is useful
 * if an advised object needs to call another advised method on itself.
 * (If it uses {@code this}, the invocation will not be advised).
 * <p>Default is "false", in order to avoid unnecessary extra interception.
 * This means that no guarantees are provided that AopContext access will
 * work consistently within any method of the advised object.
 */
public void setExposeProxy(boolean exposeProxy) {
	this.exposeProxy = exposeProxy;
}

That is, only when exposeProxy is true, the proxy dynamic proxy object will be set to the AopContext context. This configuration defaults to false. So how to modify this configuration?

In the xml era, we can configure:

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

to modify the global exposure logic.

In annotation based configuration, we need to use

@EnableAspectJAutoProxy(proxyTargteClass = true, exposeProxy = true)

to configure.

Unfortunately, for @Async, this configuration still does not take effect . Because @Async does not use AspectJ's automatic proxy, but uses the fixed proxy creation method in the code for proxy creation.

If it is the @Transactional transaction annotation, it will take effect. The specific effective mechanism is declared through the TransactionManagementConfigurationSelector class in the @EnableTransactionManagement annotation, which declares that the AutoProxyRegistrar class is imported, which obtains the proxy-related annotation configuration in the annotation, and registers an AutoProxyCreator in the BeanDefinition that can be used to automatically generate proxy objects according to the configuration:

AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

In the @EnableAspectJAutoProxy annotation, the AspectJAutoProxyRegistrar class of @Import modifies the BeanDefinition class and modifies the exposeProxy attribute.

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
	return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

The previous AutoProxyCreator is replaced later. The replacement logic is to use priority replacement. The priorities are:

APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);

This logic is all in registerOrEscalateApcAsRequired, and readers can take a look at it for themselves.

Because the @Transactional annotation and AspectJ-related annotations generate dynamic proxy classes that use the same bean, that is, the AutoProxyCreator above, the bean's name is org.springframework.aop.config.internalAutoProxyCreator, and they share the same properties, so for For @Transactional, the property exposeProxy=true of @EnableAspectJAutoProxy is also valid. However, the proxy class generated by the @Async annotation is not generated by this autoProxyCreator, so it cannot enjoy the above configuration.

  1. Based on the above source code, we can get the third processing method

At a certain cut-in timing, manually execute the AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); static method, of course, if there is a BeanDefinitionRegistry, and the timing should be called when the BeanDefinition has been created and the dynamic proxy object has not been generated.

In this way, there is no need to use @EnableAspectJAutoProxy.

The same way doesn't work for @Async, it works for @Transactional.

  1. Manually modify the properties of various BeanPostProcessor

Taking @Async as an example, it uses AsyncAnnotationBeanPostProcessor to generate a dynamic proxy class. We only need to modify the exposeProxy property at the right time, that is, when the BPP has been created but not yet used, and use AsyncAnnotationBeanPostProcessor.setExposeProxy (true).

In this way, you need to set the exposeProxy property of a specific bean to true. Applicable to @Async, the observation principle shows that the cores of 3 and 4 are the same, that is, to set the exposed property of AutoProxyCreater to true. AsyncAnnotationBeanPostProcessor is actually an AutoProxyCreater, which is a subclass of ProxyProcessorSupport.

For @Async you can use 1 and 4 methods, and for @Transactional you can use any of these four methods.

Welcome to add other methods.

4. Is it possible to make this call to make the dynamic proxy take effect?

Based on our speculation, if this reference is a dynamic proxy object, then this call can actually call the method of the parent class. As long as the method of the parent class is called, the dynamic proxy interception added to the method overridden by the parent class is valid. Does this scenario exist in Spring? The answer is yes. In the @Configuration configuration class provided by Spring, there is an application of this scenario. See the example below:

@Configuration
public class TestConfig {
    
    @Bean
    public Config config() {
        return new Config();
    }
    
    @Bean
    public ConfigOut configOut() {
        Config c1 = this.config();
        Config c2 = this.config();
        System.out.println(c1 == c2);
        ConfigOut configOut = new ConfigOut(this.config());
        return configOut;
    }

    public static class Config {}
    
    public static class ConfigOut {
        
        private Config config;
        
        private ConfigOut(Config config) {
            this.config = config;
        }
        
    }
    
}

Add a breakpoint to the configOut method, debug and observe the values ​​of c1 and Cai, that is, the value returned by this.config(), you can see that c1 and c2 are the same object reference, instead of a new one every time the method is called object.Debug Config class screenshot.png

So how does this call return the same instance multiple times? We continue to track and debug breakpoints, check the overall call stack, and find that the call place of this method configOut and the real call place of the config method are in the inner class BeanMethodInterceptor of ConfigurationClassEnhancer. Why is this method? Because the real Configuration class is dynamically replaced with a subclass created based on CGLIB. The processing of this @Configuration class is based on the BeanFactoryPostProcessor processor, ConfigurationClassPostProcessor. In the postProcessBeanDefinitionRegistry method in ConfigurationClassPostProcessor, check all beans, if the bean is @Configuration, @Component, @ComponentScan, @Import, @ImportResource One of the annotations, then this class will be treated as a Configuration class. In the postProcessBeanDefinition method, the @Configuration class is dynamically proxied as a new class, and the Configuration class is enhanced with the enhancer of CGLIB. Use the enhance method of ConfigurationClassEnhancer to process as a subclass of the original class, reference code:

/**
 * Loads the specified class and generates a CGLIB subclass of it equipped with
 * 加载特殊的Configuration类时,为其生成一个CGLIB的子类
 * container-aware callbacks capable of respecting scoping and other bean semantics.
 * 以便实现对@Bean方法的拦截或者增强
 * @return the enhanced subclass
 */
public Class<?> enhance(Class<?> configClass, ClassLoader classLoader) {
	if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
	    // 如果已经是被增强的Configuration,则直接跳过
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Ignoring request to enhance %s as it has " +
					"already been enhanced. This usually indicates that more than one " +
					"ConfigurationClassPostProcessor has been registered (e.g. via " +
					"<context:annotation-config>). This is harmless, but you may " +
					"want check your configuration and remove one CCPP if possible",
					configClass.getName()));
		}
		return configClass;
	}
	// 否则生成增强后的新的子类
	Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
	if (logger.isDebugEnabled()) {
		logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
				configClass.getName(), enhancedClass.getName()));
	}
	return enhancedClass;
}

/**
 * Creates a new CGLIB {@link Enhancer} instance.
 * 创建增强的CGLIB子类
 */
private Enhancer newEnhancer(Class<?> superclass, ClassLoader classLoader) {
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(superclass);
	// 增加接口以标记是被增强的子类,同时增加setBeanFactory方法,设置内部成员为BeanFactory。
	enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
	enhancer.setUseFactory(false);
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	// BeanFactoryAwareGeneratorStrategy生成策略为生成的CGLIB类中添加成员变量$$beanFactory
	// 同时基于接口EnhancedConfiguration的父接口BeanFactoryAware中的setBeanFactory方法,设置此变量的值为当前Context中的beanFactory
	// 该BeanFactory的作用是在this调用时拦截该调用,并直接在beanFactory中获得目标bean。
	enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
	// 设置CALLBACK_FILTER,
	enhancer.setCallbackFilter(CALLBACK_FILTER);
	enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
	return enhancer;
}

// 增强时要使用的filters
// The callbacks to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS = new Callback[] {
        // 用于拦截@Bean方法的调用,并直接从BeanFactory中获取目标bean,而不是通过执行方法。
		new BeanMethodInterceptor(),
		// 用于拦截BeanFactoryAware接口中的setBeanFactory方法的嗲用,以便设置$$beanFactory的值。
		new BeanFactoryAwareMethodInterceptor(),
		// 不做任何操作
		NoOp.INSTANCE
};

/**
 * Uses enhancer to generate a subclass of superclass,
 * ensuring that callbacks are registered for the new subclass.
 * 设置callbacks到静态变量中,因为还没有实例化,所以只能放在静态变量中。
 */
private Class<?> createClass(Enhancer enhancer) {
	Class<?> subclass = enhancer.createClass();
	// Registering callbacks statically (as opposed to thread-local)
	// is critical for usage in an OSGi environment (SPR-5932)...
	Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
	return subclass;
}

You can see that the callbacks here are registered in the static of the generated subclass, here only the class is generated and not instantiated.

Set this class to the beanClass property in the BeanDefinition, and the subclass will be automatically initialized when the BeanDefinition is initialized.

The key above is CALLBACKS, CALLBACK_FILTER, which represent the enhancer and the filter of the enhancer, respectively.

The CGLIB dynamic proxy creation of the Configuration class can be compared with the CGLIB dynamic proxy created by the SpringAOP system . The difference is the CALLBACKS and CALLBACK_FILTER of the dynamic proxy here.

Here we take the BeanMethodInterceptor mentioned above as an example to illustrate its role and the reason why this call can be intercepted by dynamic proxy in this case. code show as below:

/**
 * enhancedConfigInstance: 被CGLIB增强的config类的实例,即CGLIB动态生成的子类的实例
 * beanMethod : @Bean标记的方法,即当前调用的方法,这个是通过CallbackFilter的accept方法筛选出来的,只可能是@Bean标注的方法。
 * beanMethodArgs : 方法调用的参数
 * cglibMethodProxy : cglib方法调用的代理,可以用来直接调用父类的真实方法。
*/
@Override
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
			MethodProxy cglibMethodProxy) throws Throwable {
    // 通过enhancedConfigInstance中cglib生成的成员变量$$beanFactory获得beanFactory。
	ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
	// 确认真实的beanName,用于在beanFactory中获得bean实例
	String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

	// Determine whether this bean is a scoped-proxy
	// 后面这个是确认是否是scoped作用域的bean,这里暂时不考虑,后续文章详细分析Scoped相关的逻辑和bean。
	Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
	if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
		String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
		if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
			beanName = scopedBeanName;
		}
	}

	// To handle the case of an inter-bean method reference, we must explicitly check the
	// container for already cached instances.
	// 拦截内部bean方法的调用,检查bean实例是否已经生成

	// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
	// proxy that intercepts calls to getObject() and returns any cached bean instance.
	// This ensures that the semantics of calling a FactoryBean from within @Bean methods
	// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
	// 检查是否是FactoryBean,当是FactoryBean时,即使是this调用也不能生成多次
	// 更特殊的,调用FactoryBean的getObject方法时,也不能生成多次新的Bean,否则取到的bean就是多个了,有违单例bean的场景。
	// 所以这里判断如果当前方法返回的bean,如果是FactoryBean的话,对FactoryBean进行代理
	// 代理的结果是拦截factoryBean实例的getObject方法,转化为通过BeanFactory的getBean方法来调用
	if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
			factoryContainsBean(beanFactory, beanName)) {
		// 上面加入BeanFactory.FACTORY_BEAN_PREFIX + beanName用来判断当前bean是否是一个FactoryBean。在BeanFactory中是通过FACTORY_BEAN_PREFIX前缀来区分当前要判断的目标类型的,
		// 如果是FACTORY_BEAN_PREFIX前缀的beanName,则获取之后会判断是否是FactoryBean,是则为true,否则为false。
		// 同时还判断了当前的Bean是否是在创建中,只有不是在创建中,才会返回true。第一个拿FactoryBean的name去判断,则肯定不在创建中。第二个的判断才是真正生效的可判断出是否在创建中的方法。
		Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
		// 只有不在创建中,才能调用BeanFactory去获取或者创建,否则会无限递归调用。
		// 上面的调用获取时,才会进行真正的初始化,实例化时还会再进一次这个方法,但是并不会执行到这个逻辑中,因为再进入时,会被标记为正在创建。真正的初始化时调用@Bean方法进行的,是在下面的逻辑中。
		if (factoryBean instanceof ScopedProxyFactoryBean) {
			// Scoped proxy factory beans are a special case and should not be further proxied
		}
		else {
			// It is a candidate FactoryBean - go ahead with enhancement
			return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
		}
	}

	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
	    // 上面这个用于判断当前的工厂方法,也就是@Bean标注的方法是否是在调用中。如果是在调用中,则说明需要真正的实例化了,此时调用父类真是方法来创建实例。
		// The factory is calling the bean method in order to instantiate and register the bean
		// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
		// create the bean instance.
		if (logger.isWarnEnabled() &&
				BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
		    // 如果是BeanFactoryPostProcessor类型的话则提出警告,表明可能并不能正确执行BeanFactoryPostProcessor的方法。
			logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
							"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
							"result in a failure to process annotations such as @Autowired, " +
							"@Resource and @PostConstruct within the method's declaring " +
							"@Configuration class. Add the 'static' modifier to this method to avoid " +
							"these container lifecycle issues; see @Bean javadoc for complete details.",
					beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
		}
		// 调用父类真实方法实例化。
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}
    // 这个方法尝试从beanFactory中获得目标bean,这样便可另所有此方法调用获得bean最终都是从beanFactory中获得的,达到了单例的目的。
	return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName);
	// 在Bean的方法A使用this引用调用方法B时,会先进入一次这个方法的逻辑,此时因为还没真正进行实例化,
	// isCurrentlyInvokedFactoryMethod(beanMethod)得到的结过是false,故会调用obtainBeanInstanceFromFactory,此时会从beanFactory中获得bean。
	// 在获得Bean时,会再次调用B方法,因为这个Bean需要调用@Bean的方法才能生成。调用前先打上正在调用的标记,同时再次进入这个方法逻辑,此时上面判断isCurrentlyInvokedFactoryMethod结过为true,调用父类方法进行真实的实例化。
}

/**
 * 该方法为FactoryBean返回被代理的新实例,新的实例拦截getObject方法,并从beanFactory中获得单例bean。
 */
private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
		final ConfigurableBeanFactory beanFactory, final String beanName) {

	try {
		Class<?> clazz = factoryBean.getClass();
		boolean finalClass = Modifier.isFinal(clazz.getModifiers());
		boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
		// 判断真实FactoryBean的类型和getObject方法,如果是final的,说明不能通过CGLIB代理,则尝试使用JDK代理
		if (finalClass || finalMethod) {
			if (exposedType.isInterface()) {
			    // 如果方法返回类型,即exposedType是接口,则这个接口一般都是FactoryBean,则通过jdk动态代理创建代理
				if (logger.isDebugEnabled()) {
					logger.debug("Creating interface proxy for FactoryBean '" + beanName + "' of type [" +
							clazz.getName() + "] for use within another @Bean method because its " +
							(finalClass ? "implementation class" : "getObject() method") +
							" is final: Otherwise a getObject() call would not be routed to the factory.");
				}
				return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
			}
			else {
			    // 不是接口就没办法了,只能直接返回原始的factoryBean,如果在这个factoryBean里getObject生成了新对象,多次调用生成的结果bean将不会是同一个实例。
				if (logger.isInfoEnabled()) {
					logger.info("Unable to proxy FactoryBean '" + beanName + "' of type [" +
							clazz.getName() + "] for use within another @Bean method because its " +
							(finalClass ? "implementation class" : "getObject() method") +
							" is final: A getObject() call will NOT be routed to the factory. " +
							"Consider declaring the return type as a FactoryBean interface.");
				}
				return factoryBean;
			}
		}
	}
	catch (NoSuchMethodException ex) {
		// No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
	}
    // 可以使用CGLIB代理类。
	return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
	// 假设A方法调用了@Bean的B方法,B方法返回FactoryBean实例
	// 那么在A调用B时,会先进入BeanMethodInterceptor.intercept方法
	// 在方法中判断目标bean是一个FactoryBean,且不是在创建中,则调用beanFactory的getBean尝试获取目标bean。
	// 在获取的过程中,最终又会执行方法B,此时被拦截再次进入这个intercept方法
	// 由于标记为创建中,故这里会进入下面的创建中逻辑,通过invokeSuper调用了真实的方法逻辑返回真实的FactoryBean。
	// 这个真实的FactoryBean返回之后,在第一次的intercept方法中,对这个FactoryBean实例进行代理,返回一个被代理的FactoryBean对象给方法A中的逻辑使用,这样就可以保证在A中调用FactoryBean.getObject时拿到的是beanFactory的bean实例了。
}

Through the BeanMethodInterceptor.intercept method, we can see that the real method invocation is performed through cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs), enhancedConfigInstance is an instance of the subclass generated by the dynamic proxy, and the parent class method of the object is directly called here, That is, it is equivalent to the real method of invocation, which is different from calling the real object target as a real invocation instance in the Spring AOP system. That is, this difference brings the above characteristics to this invocation.

That is, in this case, this is an instance of the subclass generated by the dynamic proxy of CGLIB. When calling this.method(), the method of the subclass instance is actually called. This method can be intercepted by the method interceptor. Do some processing in the interception logic. If you need to call the corresponding method of the real object, use invokeSuper directly to call the parent class method, instead of passing in the instance of the real dynamic proxy object to call. The real object is not actually created, that is to say, corresponding to Spring AOP, the target does not exist, only the instance of the subclass object dynamically proxy itself, and there is no real object instance.

From this, we understand how this call is dynamically intercepted.

For the invocation of the above Configuration class, you can refer to the following example, and you can understand the problem more deeply after comparison and debugging.

import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TestConfig {

    @Bean
    public ConfigOut configOut() {
        Config c1 = this.config();
        Config c2 = this.config();
        // 这里返回同一个实例
        System.out.println(c1 == c2);
        ConfigOut configOut = new ConfigOut(this.config());
        FactoryBean ob1 = this.objectFactoryBean();
        FactoryBean ob2 = this.objectFactoryBean();
        // 这里也是 同一个实例
        System.out.println(ob1 == ob2);
        MyObject myObject1 = this.objectFactoryBean().getObject();
        MyObject myObject2 = this.objectFactoryBean().getObject();
        // 如果objectFactoryBean方法返回类型为FactoryBean则这两个相同
        // 如果是ObjectFactoryBean则两个不相同,上面已分析过原因
        System.out.println(myObject1 == myObject2);
        return configOut;
    }

    @Bean
    public Config config() {
        return new Config();
    }

    @Bean
    public FactoryBean objectFactoryBean() {
        return new ObjectFactoryBean();
    }

    public static class Config {}

    public static class ConfigOut {

        private Config config;

        private ConfigOut(Config config) {
            this.config = config;
        }

    }

    public static final class ObjectFactoryBean implements FactoryBean<MyObject> {

        @Override
        public final MyObject getObject() {
            return new MyObject();
        }

        @Override
        public Class<?> getObjectType() {
            return MyObject.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }
    }

    public static class MyObject {}

}

postscript

Based on the actual scenario, this article analyzes in detail the reason why this call causes AOP to fail, and how to solve this problem. And extends the scenario where this call can make AOP take effect. As long as everyone can understand the principle, they should be able to analyze the reason. Usually some code specifications that need to be followed have their manifestations and reasons at the principle level. Analyze the real reasons to get the final conclusion. This process is a process of sublimation of knowledge. I hope everyone can see it happily.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325460821&siteId=291194637