Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support [package-]private init/destroy methods in AOT mode
Prior to this commit, private (and non-visible package-private) init/destroy methods were not supported in AOT mode. The reason is that such methods are tracked using their fully-qualified method names, and the AOT support for init/destroy method previously did not take fully-qualified method names into account. In addition, the invocation order of init/destroy methods differed between standard JVM mode and AOT mode. This commit addresses these issues in the following ways. - AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(), DisposableBeanAdapter.determineDestroyMethod(), and BeanDefinitionPropertiesCodeGenerator.addInitDestroyHint() now parse fully-qualified method names to locate the correct init/destroy methods. - AbstractAutowireCapableBeanFactory and DisposableBeanAdapter delegate to a new InitDestroyMethodUtils class which encapsulates the parsing of fully-qualified method names; however, BeanDefinitionPropertiesCodeGenerator duplicates this logic since it resides in a different package, and we do not currently want to make the functionality in InitDestroyMethodUtils public. - Init/destroy methods detected via annotations (such as @PostConstruct and @PreDestroy) are now invoked prior to init/destroy methods that are explicitly configured by name or convention. This aligns with the invocation order in standard JVM mode; however, InitializingBean#afterPropertiesSet() and DisposableBean#destroy() are still invoked before annotated init/destroy methods in AOT mode which differs from standard JVM mode. - Unit and integration tests have been updated to test the revised behavior. Closes gh-30692
- Loading branch information
Showing
9 changed files
with
266 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
...beans/src/main/java/org/springframework/beans/factory/support/InitDestroyMethodUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Copyright 2002-2023 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.beans.factory.support; | ||
|
||
import org.springframework.util.ClassUtils; | ||
|
||
/** | ||
* Internal utilities for working with init/destroy methods. | ||
* | ||
* @author Sam Brannen | ||
* @since 6.0.11 | ||
*/ | ||
abstract class InitDestroyMethodUtils { | ||
|
||
/** | ||
* Create a {@link MethodDescriptor} for the supplied bean class and method | ||
* name. | ||
* <p>The supplied {@code methodName} may be a {@linkplain Method#getName() | ||
* simple method name} or a | ||
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method) | ||
* qualified method name} for package-private and {@code private} methods. | ||
* <p>If the method name is fully qualified, this utility will parse the | ||
* method name and its declaring class from the qualified method and name | ||
* and then attempt to load the method's declaring class using the | ||
* {@link ClassLoader} of the supplied {@code beanClass}. Otherwise, the | ||
* returned descriptor will reference the supplied {@code beanClass} and | ||
* {@code methodName}. | ||
* @param beanName the bean name in the factory (for debugging purposes) | ||
* @param beanClass the bean class | ||
* @param methodName the name of the method | ||
* @return a new {@code MethodDescriptor}; never {@code null} | ||
*/ | ||
static MethodDescriptor createMethodDescriptor(String beanName, Class<?> beanClass, String methodName) { | ||
try { | ||
Class<?> declaringClass = beanClass; | ||
String methodNameToUse = methodName; | ||
|
||
// Parse fully-qualified method name if necessary. | ||
int indexOfDot = methodName.lastIndexOf('.'); | ||
if (indexOfDot > 0) { | ||
String className = methodName.substring(0, indexOfDot); | ||
methodNameToUse = methodName.substring(indexOfDot + 1); | ||
if (!beanClass.getName().equals(className)) { | ||
declaringClass = ClassUtils.forName(className, beanClass.getClassLoader()); | ||
} | ||
} | ||
return new MethodDescriptor(declaringClass, methodNameToUse); | ||
} | ||
catch (ClassNotFoundException | LinkageError | IllegalArgumentException ex) { | ||
throw new BeanDefinitionValidationException( | ||
"Could not create descriptor for method '%s' on bean with name '%s': %s" | ||
.formatted(methodName, beanName, ex.getMessage())); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Descriptor for a {@link java.lang.reflect.Method Method} which holds | ||
* a reference to the method's {@linkplain Class declaring class}, the | ||
* name of the method, and the method's parameter types. | ||
* | ||
* @param declaringClass the method's declaring class | ||
* @param methodName the name of the method | ||
* @param parameterTypes the types or parameters accepted by the method | ||
*/ | ||
record MethodDescriptor(Class<?> declaringClass, String methodName, Class<?>... parameterTypes) { | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.