Reference: https://mp.weixin.qq.com/s/-IiIZruQriZKUYyfyg7jFw
00 preface
When we were using Spring, we were probably taught not to use this in beans to call methods marked with @async, @Transactional, @cacheable, etc. This annotation is not valid.
So have you ever thought about the following question
-
Why are annotations not valid for methods called this
-
How do these annotations work
-
What if you do need to call a method of this class, and you want the annotation to work?
-
Can the proxy call this and the annotation take effect immediately?
Through this article, the above questions can be solved, and you can learn a lot of relevant principle knowledge, the amount of information is large, so start
01 phenomenon
In the case of the @Async annotation, methods marked by the @Async annotation are handled by AOP as asynchronous calls when executed, and are returned directly where this method is called. Methods marked by @Async are executed using other threads.
Use the Spring Boot driver
@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 async1() and the third line to async2(). You can see that SimpleAsyncTaskExecutor is used when asyncService.async2() is called directly, while this is used when async1() is called. The result is the main thread, the same as the original calling thread. This means that @async is not in effect when this is called.
02 Think & guess
For AOP dynamic proxies, non-interface classes are known to use CGLIB based dynamic proxies, which create a subclass based on an existing class and instantiate the subclass object. When a dynamic proxy object method is called, a subclass method is called first. The subclass method uses method-enhanced Advice or MethodInterceptor to handle the subclass method call, and optionally decides whether to execute the superclass method.
If the method async1 is called with an instance of a dynamically generated subclass, then this is an instance of a dynamically generated subclass based on the dynamic proxy. This can be intercepted by Advice or MethodInterceptor.
As a bold guess, this is not a subclass of the async1 method, but a primitive object, so this cannot be enhanced by dynamic proxies.
For more information about the use of CGLIB for AOP dynamic proxies, see the full Understanding of the Spring Framework AOP Implementation principles article.
https://my.oschina.net/guangshan/blog/1797461
Let’s start with a detailed analysis.
03 source code debugging analysis principle
The first thing to figure out is how @async works:
1. Analyze Async related components
From the effective entry @ EnableAsync annotations on the @ Import (AsyncConfigurationSelector. Class)
@ Import role is to put the @ behind the Configuration class, ImportSelector or ImportBeanDefinitionRegistrar Import content in automatic registration to the ApplicationContext. The three classes that can be imported will not be explained in detail here, but interested readers can check out the documentation on the Spring website or wait for my next article.
Here import AsyncConfigurationSelector and AsyncConfigurationSelector by default, will select out ProxyAsyncConfiguration class to import, The ProxyAsyncConfiguration class is configured into the ApplicationContext as the @Configuration class. So 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 function of this code is the AsyncAnnotationBeanPostProcessor as Bean registered to the Context. Then the core is the AsyncAnnotationBeanPostProcessor this BeanPostProcessor, also is the Spring of the well-known BPP.
After a Bean instance generation, will be handed over to the BPP postProcessBeforeInitialization methods for processing, this time can return to the other that is compatible with the Bean Bean instance, for example, is one of the most common here to return to the original object’s dynamic proxy objects.
After this method executes, the init-related methods of the Bean instance are called. The methods invoked are the afterPropertiesSet method of the InitializingBean interface and the initialization method specified by initMethod in the @Bean declaration.
After the call the init method is called the BPP rear postProcessAfterInitialization methods for processing. When handling with postProcessBeforeInitialization, also can replace the original bean instance.
Let’s see what the Async related BPP does:
// The potential handler does nothing to ensure that nothing has changed in the bean itself until init is called.
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// If it is an AOP-related base component bean, such as the ProxyProcessorSupport class and its subclasses, return it directly.
if (bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (bean instanceof Advised) {
// If it is already an instance of being dynamically proxied, add the advisor directly.
Advised advised = (Advised) bean;
if (! advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// If it is not frozen and Eligbile(appropriate), add it to the Advisor. The insertion position is determined based on the configuration.
// 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)) {
If Eligible, create an instance of the proxy class and return it.
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;
}
// Prepare the ProxyFactory object
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// Set the proxied bean to target, which is the real bean.
proxyFactory.setTarget(bean);
return proxyFactory;
}
The purpose of the first logical judgment bean instanceof an old proxy is to determine if the old proxy is already an old proxy class. If so, Add an Advisor enhancer to it.
If it is not a dynamically proxied object, the original bean needs to be converted to a dynamically proxied bean by AOP, since @Async is adding a proxy to the method and converting it to asynchronous execution. That’s the logic.
For details on how this code creates dynamic proxies, please refer to my article: Fully Understand AOP Implementation principles of the Spring Framework.
One more thing about @Async: The type of Advisor registered above is AsyncAnnotationAdvisor. Including the PointCut, type is AnnotationMatchingPointcut, specifies the only @ the method or class of Async mark this AOP enhancer to take effect. There is one Advice, used to enhance @ Async marking method, converted to asynchronous, type is AnnotationAsyncExecutionInterceptor, invoke method is really true calling, we are interested in can carefully study the contents, So you can figure out the actual execution logic of the @async method.
Now that the relevant components have been mentioned and briefly analyzed above, let’s move on to the next stage of analyzing why this call doesn’t work through the actual execution logic.
2. Drill down into the real call logic
@async is mostly marked in the class method, so AOP implementation is also based on CGLIB, the following to CGLIB dynamic proxy as an example to analyze the real call logic.
By fully understand AOP implementation principle of the Spring framework, this article, based on additional AOP dynamic proxy bean, the real execution logic is in 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) {
// Need is exposed
// 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…
// Focus: get the proxied target object
target = getTarget();
if (target ! = null) {
targetClass = target.getClass();
}
// Get interceptor chain
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.
// If the chain is empty and the method is public, it is called directly
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation…
// otherwise create a CglibMethodInvocation to drive the interceptor chain
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// Process the return value, the same as JDK dynamic proxy
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target ! = null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
Note that the actual call above, in the absence of an advisor, actually uses:
methodProxy.invoke(target, argsToUse)
In the case of an agent, this is used:
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
In CglibMethodInvocation, the actual method invokeJoinpoint is called when the invocation chain is checked out. In CglibMethodInvocation, the implementation of this method is
// CglibMethodInvocation implementation
protected Object invokeJoinpoint() throws Throwable {
if (this.publicMethod) {
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
return super.invokeJoinpoint();
}
}
// The superclass implementation is
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
Can see a method is called when the incoming instances are target, the target acquired from DynamicAdvisedInterceptor getTarget method, 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, the target is actually the proxied bean.
From the above analysis, we can conclude that a dynamically propped object, after all of AOP’s enhanced logic is executed, will eventually call the real method using the propped object as an instance, which is equivalent to calling the target.method() method. It follows that in the target.method() method, the this reference must be the target itself and not the generated dynamic proxy object instance.
In addition, Spring creates a Bean, wraps it and generates a dynamic proxy object after it is created, so it becomes an instance Bean of the real class, and then dynamically creates a dynamic proxy Bean, in which it holds an instance of the real Bean.
Using the top @async code example, we can see that this is the original AsyncService instance, not the proxy object instance:
Summary: Since AOP dynamic proxy methods are called in real terms, they use real proxied object instances for method calls, so this gets instances of the proxied real object in instance methods, not the proxy object itself.
3. Address several alternatives to the this call
Now that the reason is known, the solution is directed at getting the dynamic proxy object instead of using this to call it.
The following methods are provided:
1. Use ApplicationContext to obtain the dynamic proxy object
@Component
public class AsyncService implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void async1() {
System.out.println(“1:” + Thread.currentThread().getName());
// Use AppicationContext to get the beans of the dynamic proxy
this.applicationContext.getBean(AsyncService.class).async2();
}
@Async
public void async2() {
System.out.println(“2:” + Thread.currentThread().getName());
}
/ / into the ApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
The execution result is:
1:main
2:SimpleAsyncTaskExecutor-2
2:SimpleAsyncTaskExecutor-3
And you can see that perfection has served our purpose. Similarly, BeanFactoryAware can achieve the same effect.
2. Use AopContext to obtain the dynamic proxy object
@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 pretty neat, but it doesn’t work by default! Because currentProxy is not available in AopContext, it’s going to give a null pointer.
The logic can be seen where the source code is executed through the dynamic proxy above:
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
In the ProxyConfig class, there are comments to explain the role of exposeProxy, which is used to get dynamic proxy objects in a 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;
}
The proxy dynamic proxy object is only set to the AopContext context if exposeProxy is true, which is false by default. So how does this configuration change?
In the AGE of XML, 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, this configuration does not work with @async. Instead of using AspectJ’s automatic proxy, @async creates the proxy in a way that is fixed in the code.
This is valid if the @Transactional transaction annotation is used. Specific effect mechanism is through @ EnableTransactionManagement annotation of TransactionManagementConfigurationSelector class declaration, the declaration imports the AutoProxyRegistrar class, This class gets the proxy-related annotation configuration in the annotation and registers an AutoxyCreator in BeanDefinition that can be used to automatically generate proxy objects based on the configuration:
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
In @ EnableAspectJAutoProxy annotations, @ Import AspectJAutoProxyRegistrar class and put this BeanDefinition modified class, modify the exposeProxy attributes of them at the same time.
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 AutoxyCreator is replaced later, and the replacement logic is replaced by priority, which are as follows:
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
This logic in registerOrEscalateApcAsRequired, readers can have a check again.
Since the @Transactional annotation and aspectJ-related annotation generation dynamic proxy classes are handled using the same Bean, autoxyCreator above, The name of the bean is org. Springframework. Aop. Config. InternalAutoProxyCreator, they have the same common properties, reason for @ Transactional, The @enableAspectJAutoProxy attribute exposeProxy=true is also in effect. However, the @async annotations generate proxy classes that are not created through the AutoxyCreator, so they do not enjoy the above configuration.
3. Based on the above source code, we can get a third processing method
At a certain time, performed manually AopConfigUtils. ForceAutoProxyCreatorToExposeProxy (registry); Static methods, provided, of course, that there is a BeanDefinitionRegistry, called when the BeanDefinition has been created and the dynamic proxy object has not yet been generated.
In this way, you don’t need to use @enableAspectJAutoProxy.
This also doesn’t work for @async, it works for @Transactional.
4. Manually modify the attributes of various BeanPostProcessors
@ Async, for example, the generated by AsyncAnnotationBeanPostProcessor dynamic proxy classes, we just at the right time is the BPP has been created, but has not yet been used, modify the exposeProxy properties, Using AsyncAnnotationBeanPostProcessor. SetExposeProxy (true).
This approach specifically sets the exposeProxy attribute true for a particular bean. Applicable to @async, the observation principle can know that 3 and 4 are actually the same core, that is, set the exposed attribute of AutoProxyCreater to true. AsyncAnnotationBeanPostProcessor is actually a AutoProxyCreater, he is a subclass of ProxyProcessorSupport.
For @async you can use methods 1 or 4, and for @Transactional you can use any of these four methods.
You are welcome to add other methods.
4. Can this be done to make the dynamic proxy work
Based on our assumption, if the this reference is a dynamic proxy object, then the this call can actually call the parent method. As long as the parent method is called, then the dynamic proxy interception in the parent overridden method is valid. Does this scenario exist in Spring? The answer is yes. Spring provides an example of this scenario in the @Configuration class:
@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 and debug to observe the values of c1 and c2, which are returned by this.config(). You can see that c1 and c2 are the same object reference, rather than a new object each time the method is called.
So how does this call return the same instance multiple times? We continue to track debugging breakpoints, view the whole of the call stack, this method configOut calls and config method real call in is in the inner class BeanMethodInterceptor ConfigurationClassEnhancer, Why is it this way? Because the actual Configuration class is dynamically replaced with a subclass created based on CGLIB. And this @ the processing of the Configuration class, is based on the spring BeanFactoryPostProcessor ConfigurationClassPostProcessor processor to do, In ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry method, check all the beans, If the bean is annotated by one of @Configuration, @Component, @ComponentScan, @import, or @importResource, then this class is considered Configuration. In postProcessBeanDefinition approach, the @ the Configuration class dynamic proxy for a new class, the use of additional enhancer to enhance the Configuration class. Using ConfigurationClassEnhancer enhance method processing is a subclass of the original class, reference code:
/ * *
* Loads the specified class and generates a CGLIB subclass of it equipped with
* When a special Configuration class is loaded, generate a CGLIB subclass for it
* container-aware callbacks capable of respecting scoping and other bean semantics.
* to implement interception or enhancement of the @bean method
* @return the enhanced subclass
* /
public Class<? > enhance(Class<? > configClass, ClassLoader classLoader) {
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
// If the Configuration is already enhanced, skip it
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;
}
// Otherwise generate a new enhanced subclass
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.
* Create enhanced CGLIB subclasses
* /
private Enhancer newEnhancer(Class<? > superclass, ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(superclass);
// Add the interface to mark the enhanced subclass, and add the setBeanFactory method to set the internal member to BeanFactory.
enhancer.setInterfaces(new Class<? >[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
/ / BeanFactoryAwareGeneratorStrategy generation strategy add member variables to generate additional classes? beanFactory
// Set this variable to beanFactory in the current Context based on the setBeanFactory method of BeanFactoryAware, the parent interface of EnhancedConfiguration
// The purpose of this BeanFactory is to intercept the this call and get the target bean directly from the BeanFactory.
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// Set CALLBACK_FILTER,
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
// The filters to use when enhancing
// The callbacks to use. Note that these callbacks must be stateless.
private static final Callback[] CALLBACKS = new Callback[] {
// Used to intercept calls to the @bean method and get the target Bean directly from the BeanFactory instead of through the execution method.
new BeanMethodInterceptor(),
// The setBeanFactory method in the BeanFactoryAware interface is used to intercept the setBeanFactory method. The value of the beanFactory.
new BeanFactoryAwareMethodInterceptor(),
// Do nothing
NoOp.INSTANCE
};
/ * *
* Uses enhancer to generate a subclass of superclass,
* ensuring that callbacks are registered for the new subclass.
* Set callbacks to static variables, which are not instantiated yet, so they can only be placed in static variables.
* /
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;
}
As you can see, the callbacks are registered to the static of the generated subclass, where class is only generated and not instantiated.
Setting this class to the beanClass property of a BeanDefinition automatically initializes the subclasses when the BeanDefinition is initialized.
The key points above are CALLBACKS and CALLBACK_FILTER, which represent the enhancer and the enhancer’s filter, respectively.
The CGLIB dynamic proxy creation of the Configuration class can be compared to the CGLIB dynamic proxy creation of the SpringAOP architecture in terms of CALLBACKS and callback_filters.
Let’s use the aforementioned BeanMethodInterceptor as an example to show what it does and why this call can be intercepted by a dynamic proxy in this case. The code is as follows:
/ * *
EnhancedConfigInstance: An instance of a config class enhanced by CGLIB, that is, an instance of a dynamically generated CGLIB subclass
* beanMethod: the @bean tagged method, that is, the currently called method, filtered by the Accept method of the CallbackFilter, which can only be the @bean tagged method.
* beanMethodArgs: arguments to the method call
CglibMethodProxy: a proxy for cglib method calls that can be used to call the real methods of the parent class directly.
* /
@Override
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// Member variables generated by cglib in enhancedConfigInstance? BeanFactory gets beanFactory.
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// Verify the real beanName used to get the bean instance in the beanFactory
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
Scoped bean scoped bean scoped bean scoped bean scoped bean scoped
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.
// Intercepts calls to internal bean methods to check whether the bean instance has been generated
// 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.
// Check if it is a FactoryBean. When it is a FactoryBean, even this cannot be generated more than once
// More specifically, when you call FactoryBean’s getObject method, you can’t generate new beans more than once, otherwise you’ll end up with multiple beans, contrary to the singleton Bean scenario.
// If the bean returned by the current method is a FactoryBean, then the FactoryBean is propped
// The result of the proxy is to intercept the getObject method of the factoryBean instance and convert it to be called via the getBean method of the BeanFactory
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
// Add beanfactory. bean_prefix + beanName to determine if the current bean is a FactoryBean. In BeanFactory, the FACTORY_BEAN_PREFIX prefix is used to distinguish the type of target being determined.
// If it is a FACTORY_BEAN_PREFIX beanName, then it checks whether it is a FactoryBean. If it is a FACTORY_BEAN_PREFIX beanName, it is true; otherwise it is false.
// It also checks whether the current Bean is under creation, and returns true if it is not under creation. The first FactoryBean is definitely not being created. The second judgment is the one that actually works to determine if it’s being created.
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
// BeanFactory can only be called to get or create something if it is not already being created, otherwise it will be recursively called indefinitely.
// When the above call is fetched, the actual initialization will take place, and the method will be re-instantiated, but not executed in this logic because it will be marked as being created when re-entered. The actual initialization occurs when the @bean method is called in the following logic.
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)) {
// This is used to determine whether the current factory method, i.e. the @bean annotation method, is being called. If it is in a call, then the actual instantiation is required, in which case the parent class is called as a method to create the instance.
// 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())) {
// If it is of type BeanFactoryPostProcessor, a warning is raised indicating that the methods of BeanFactoryPostProcessor may not be executed correctly.
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()));
}
// Call the parent real method instantiation.
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// This method attempts to get the target bean from the beanFactory, so that all of the beans obtained by this method call are ultimately obtained from the beanFactory, achieving the singleton purpose.
return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName);
// When the Bean method A calls method B with this reference, the logic of the method is first entered, and the method has not been instantiated yet.
/ / isCurrentlyInvokedFactoryMethod (beanMethod) get married is false, it invokes the obtainBeanInstanceFromFactory, at this point from the beanFactory bean.
// When the Bean is obtained, the B method is called again, because the Bean needs to be generated by calling the @bean method. Before call is invoked on tag, again into the method of logic at the same time, the above judgment isCurrentlyInvokedFactoryMethod married to true, call the superclass method real instantiation.
}
/ * *
* This method returns a new instance of the propped FactoryBean, which intercepts the getObject method and obtains the singleton bean from the beanFactory.
* /
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());
// Determine the real FactoryBean type and getObject method. If it is final, the CGLIB proxy cannot be used
if (finalClass || finalMethod) {
if (exposedType.isInterface()) {
// If the method return type, exposedType, is an interface, then the interface is typically a FactoryBean, and the proxy is created using the JDK dynamic proxy
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 {
// If getObject generates a new object in this factoryBean, the resulting bean will not be the same instance.
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…
}
// You can use the CGLIB proxy class.
return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
// Suppose method A calls method B of @bean, which returns A FactoryBean instance
/ / when A call to B, can enter the BeanMethodInterceptor. Intercept method
// Determine in the method that the target bean is a FactoryBean and is not being created, then call getBean of beanFactory to try to get the target bean.
// In the process of obtaining, method B is finally executed, which is intercepted again into the Intercept method
// Since it is marked as being created, this goes into the following logic in creation, which calls the real method logic through invokeSuper to return the real FactoryBean.
// After the actual FactoryBean is returned, in the first Intercept method, the FactoryBean instance is propped up, returning A propped FactoryBean object to the logic in method A, This ensures that when you call factoryBean. getObject from A, you get the bean instance of the beanFactory.
}
Through BeanMethodInterceptor. Intercept method, we can see, Real method call is through cglibMethodProxy. InvokeSuper (enhancedConfigInstance beanMethodArgs) to perform, instances of subclasses enhancedConfigInstance is dynamic proxy generation, This calls the object’s parent method directly, which is equivalent to the real method being called, in contrast to the Spring AOP architecture of calling the real object target as an instance of the real call, which brings the above features to this call.
In this case, this is an instance of a subclass generated by CGLIB’s dynamic proxy. When we call this.method(), we actually call the method of the subclass instance. This method can be intercepted by the method interceptor, which does some processing in the intercepted logic. Instead of passing in an instance of a real dynamically proxied object to make the call, use invokeSuper directly to make the superclass method call. The real object is not actually created, that is, corresponding to Spring AOP, where target does not exist, only subclass objects dynamically proxy instances of themselves, not real object instances.
This gives us an idea of how the this call can be intercepted dynamically.
For the invocation of the Configuration class, see the following example. After debugging, you can better understand the problem.
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();
// Return the same instance
System.out.println(c1 == c2);
ConfigOut configOut = new ConfigOut(this.config());
FactoryBean ob1 = this.objectFactoryBean();
FactoryBean ob2 = this.objectFactoryBean();
// Here is the same example
System.out.println(ob1 == ob2);
MyObject myObject1 = this.objectFactoryBean().getObject();
MyObject myObject2 = this.objectFactoryBean().getObject();
// The two are the same if the objectFactoryBean method returns type FactoryBean
// If it is ObjectFactoryBean, the two are different, for which we have analyzed above
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 {}
}
04 afterword.
Based on the actual scenario, this article analyzes in detail why this call causes AOP failure and how to solve the problem. And extends the scenarios in which the this call can make AOP work. As long as we can understand the principle, we should be able to analyze the reason. At ordinary times, some code specifications that need to be complied with have their performance and reasons on the principle level. Analyzing the real reasons and getting the final conclusion, this process is the sublimation process of knowledge, I hope everyone can see and be happy.