Add support of init and destroy methods
This commit updates InitDestroyBeanPostProcessor so that it contributes init or destroy method names to the `RootBeanDefinition`. This is then used by the generator to provide these methods to the optimized AOT context. Invocation of those init methods still happen using reflection so dedicated hints are contributed for them. Closes gh-28151
This commit is contained in:
@@ -32,6 +32,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -39,6 +40,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor;
|
||||
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -46,6 +49,7 @@ import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
@@ -72,13 +76,14 @@ import org.springframework.util.ReflectionUtils;
|
||||
* for annotation-driven injection of named beans.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.5
|
||||
* @see #setInitAnnotationType
|
||||
* @see #setDestroyAnnotationType
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class InitDestroyAnnotationBeanPostProcessor
|
||||
implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
|
||||
public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor,
|
||||
MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, PriorityOrdered, Serializable {
|
||||
|
||||
private final transient LifecycleMetadata emptyLifecycleMetadata =
|
||||
new LifecycleMetadata(Object.class, Collections.emptyList(), Collections.emptyList()) {
|
||||
@@ -146,8 +151,36 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
findInjectionMetadata(beanDefinition, beanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, beanType);
|
||||
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
|
||||
String[] initMethodNames = safeMerge(
|
||||
beanDefinition.getInitMethodNames(), metadata.initMethods);
|
||||
beanDefinition.setInitMethodNames(initMethodNames);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(metadata.destroyMethods)) {
|
||||
String[] destroyMethodNames = safeMerge(
|
||||
beanDefinition.getDestroyMethodNames(), metadata.destroyMethods);
|
||||
beanDefinition.setDestroyMethodNames(destroyMethodNames);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LifecycleMetadata findInjectionMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) {
|
||||
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
|
||||
metadata.checkConfigMembers(beanDefinition);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private String[] safeMerge(@Nullable String[] existingNames, Collection<LifecycleElement> detectedElements) {
|
||||
Stream<String> detectedNames = detectedElements.stream().map(LifecycleElement::getIdentifier);
|
||||
Stream<String> mergedNames = (existingNames != null
|
||||
? Stream.concat(Stream.of(existingNames), detectedNames) : detectedNames);
|
||||
return mergedNames.distinct().toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.javapoet.support.MultiStatement;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,14 @@ public class BeanRegistrationBeanFactoryContribution implements BeanFactoryContr
|
||||
* @param runtimeHints the runtime hints to use
|
||||
*/
|
||||
void registerRuntimeHints(RuntimeHints runtimeHints) {
|
||||
String[] initMethodNames = this.beanDefinition.getInitMethodNames();
|
||||
if (!ObjectUtils.isEmpty(initMethodNames)) {
|
||||
registerInitDestroyMethodsRuntimeHints(initMethodNames, runtimeHints);
|
||||
}
|
||||
String[] destroyMethodNames = this.beanDefinition.getDestroyMethodNames();
|
||||
if (!ObjectUtils.isEmpty(destroyMethodNames)) {
|
||||
registerInitDestroyMethodsRuntimeHints(destroyMethodNames, runtimeHints);
|
||||
}
|
||||
registerPropertyValuesRuntimeHints(runtimeHints);
|
||||
}
|
||||
|
||||
@@ -191,6 +200,15 @@ public class BeanRegistrationBeanFactoryContribution implements BeanFactoryContr
|
||||
return this.beanInstantiationGenerator.generateBeanInstantiation(runtimeHints);
|
||||
}
|
||||
|
||||
private void registerInitDestroyMethodsRuntimeHints(String[] methodNames, RuntimeHints runtimeHints) {
|
||||
for (String methodName : methodNames) {
|
||||
Method method = ReflectionUtils.findMethod(getUserBeanClass(), methodName);
|
||||
if (method != null) {
|
||||
runtimeHints.reflection().registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPropertyValuesRuntimeHints(RuntimeHints runtimeHints) {
|
||||
if (!this.beanDefinition.hasPropertyValues()) {
|
||||
return;
|
||||
@@ -357,6 +375,14 @@ public class BeanRegistrationBeanFactoryContribution implements BeanFactoryContr
|
||||
private void handleBeanDefinitionMetadata(Builder code) {
|
||||
String bdVariable = determineVariableName("bd");
|
||||
MultiStatement statements = new MultiStatement();
|
||||
String[] initMethodNames = this.beanDefinition.getInitMethodNames();
|
||||
if (!ObjectUtils.isEmpty(initMethodNames)) {
|
||||
handleInitMethodNames(statements, bdVariable, initMethodNames);
|
||||
}
|
||||
String[] destroyMethodNames = this.beanDefinition.getDestroyMethodNames();
|
||||
if (!ObjectUtils.isEmpty(destroyMethodNames)) {
|
||||
handleDestroyMethodNames(statements, bdVariable, destroyMethodNames);
|
||||
}
|
||||
if (this.beanDefinition.isPrimary()) {
|
||||
statements.addStatement("$L.setPrimary(true)", bdVariable);
|
||||
}
|
||||
@@ -399,6 +425,26 @@ public class BeanRegistrationBeanFactoryContribution implements BeanFactoryContr
|
||||
code.add(")");
|
||||
}
|
||||
|
||||
private void handleInitMethodNames(MultiStatement statements, String bdVariable, String[] initMethodNames) {
|
||||
if (initMethodNames.length == 1) {
|
||||
statements.addStatement("$L.setInitMethodName($S)", bdVariable, initMethodNames[0]);
|
||||
}
|
||||
else {
|
||||
statements.addStatement("$L.setInitMethodNames($L)", bdVariable,
|
||||
this.parameterGenerator.generateParameterValue(initMethodNames));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDestroyMethodNames(MultiStatement statements, String bdVariable, String[] destroyMethodNames) {
|
||||
if (destroyMethodNames.length == 1) {
|
||||
statements.addStatement("$L.setDestroyMethodName($S)", bdVariable, destroyMethodNames[0]);
|
||||
}
|
||||
else {
|
||||
statements.addStatement("$L.setDestroyMethodNames($L)", bdVariable,
|
||||
this.parameterGenerator.generateParameterValue(destroyMethodNames));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleArgumentValues(MultiStatement statements, String bdVariable,
|
||||
Map<Integer, ValueHolder> indexedArgumentValues) {
|
||||
if (indexedArgumentValues.size() == 1) {
|
||||
|
||||
Reference in New Issue
Block a user