|
|
|
@ -24,7 +24,6 @@ import java.lang.reflect.InvocationTargetException; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.Modifier; |
|
|
|
import java.lang.reflect.Modifier; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
|
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.LinkedHashSet; |
|
|
|
import java.util.LinkedHashSet; |
|
|
|
@ -79,6 +78,7 @@ import org.springframework.util.ReflectionUtils; |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Stephane Nicoll |
|
|
|
* @author Stephane Nicoll |
|
|
|
* @author Phillip Webb |
|
|
|
* @author Phillip Webb |
|
|
|
|
|
|
|
* @author Sam Brannen |
|
|
|
* @since 2.5 |
|
|
|
* @since 2.5 |
|
|
|
* @see #setInitAnnotationType |
|
|
|
* @see #setInitAnnotationType |
|
|
|
* @see #setDestroyAnnotationType |
|
|
|
* @see #setDestroyAnnotationType |
|
|
|
@ -153,7 +153,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { |
|
|
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { |
|
|
|
findInjectionMetadata(beanDefinition, beanType); |
|
|
|
findLifecycleMetadata(beanDefinition, beanType); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -161,7 +161,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
|
|
|
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { |
|
|
|
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); |
|
|
|
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); |
|
|
|
beanDefinition.resolveDestroyMethodIfNecessary(); |
|
|
|
beanDefinition.resolveDestroyMethodIfNecessary(); |
|
|
|
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, registeredBean.getBeanClass()); |
|
|
|
LifecycleMetadata metadata = findLifecycleMetadata(beanDefinition, registeredBean.getBeanClass()); |
|
|
|
if (!CollectionUtils.isEmpty(metadata.initMethods)) { |
|
|
|
if (!CollectionUtils.isEmpty(metadata.initMethods)) { |
|
|
|
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods); |
|
|
|
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods); |
|
|
|
beanDefinition.setInitMethodNames(initMethodNames); |
|
|
|
beanDefinition.setInitMethodNames(initMethodNames); |
|
|
|
@ -173,14 +173,14 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private LifecycleMetadata findInjectionMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) { |
|
|
|
private LifecycleMetadata findLifecycleMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) { |
|
|
|
LifecycleMetadata metadata = findLifecycleMetadata(beanType); |
|
|
|
LifecycleMetadata metadata = findLifecycleMetadata(beanType); |
|
|
|
metadata.checkInitDestroyMethods(beanDefinition); |
|
|
|
metadata.checkInitDestroyMethods(beanDefinition); |
|
|
|
return metadata; |
|
|
|
return metadata; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String[] safeMerge(@Nullable String[] existingNames, Collection<LifecycleElement> detectedElements) { |
|
|
|
private static String[] safeMerge(@Nullable String[] existingNames, Collection<LifecycleMethod> detectedMethods) { |
|
|
|
Stream<String> detectedNames = detectedElements.stream().map(LifecycleElement::getIdentifier); |
|
|
|
Stream<String> detectedNames = detectedMethods.stream().map(LifecycleMethod::getIdentifier); |
|
|
|
Stream<String> mergedNames = (existingNames != null ? |
|
|
|
Stream<String> mergedNames = (existingNames != null ? |
|
|
|
Stream.concat(Stream.of(existingNames), detectedNames) : detectedNames); |
|
|
|
Stream.concat(Stream.of(existingNames), detectedNames) : detectedNames); |
|
|
|
return mergedNames.distinct().toArray(String[]::new); |
|
|
|
return mergedNames.distinct().toArray(String[]::new); |
|
|
|
@ -217,12 +217,14 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
if (logger.isDebugEnabled()) { |
|
|
|
logger.warn(msg, ex.getTargetException()); |
|
|
|
logger.warn(msg, ex.getTargetException()); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else if (logger.isWarnEnabled()) { |
|
|
|
logger.warn(msg + ": " + ex.getTargetException()); |
|
|
|
logger.warn(msg + ": " + ex.getTargetException()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
catch (Throwable ex) { |
|
|
|
catch (Throwable ex) { |
|
|
|
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); |
|
|
|
if (logger.isWarnEnabled()) { |
|
|
|
|
|
|
|
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -253,28 +255,27 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) { |
|
|
|
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) { |
|
|
|
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { |
|
|
|
if (!AnnotationUtils.isCandidateClass(clazz, List.of(this.initAnnotationType, this.destroyAnnotationType))) { |
|
|
|
return this.emptyLifecycleMetadata; |
|
|
|
return this.emptyLifecycleMetadata; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
List<LifecycleElement> initMethods = new ArrayList<>(); |
|
|
|
List<LifecycleMethod> initMethods = new ArrayList<>(); |
|
|
|
List<LifecycleElement> destroyMethods = new ArrayList<>(); |
|
|
|
List<LifecycleMethod> destroyMethods = new ArrayList<>(); |
|
|
|
Class<?> targetClass = clazz; |
|
|
|
Class<?> targetClass = clazz; |
|
|
|
|
|
|
|
|
|
|
|
do { |
|
|
|
do { |
|
|
|
final List<LifecycleElement> currInitMethods = new ArrayList<>(); |
|
|
|
final List<LifecycleMethod> currInitMethods = new ArrayList<>(); |
|
|
|
final List<LifecycleElement> currDestroyMethods = new ArrayList<>(); |
|
|
|
final List<LifecycleMethod> currDestroyMethods = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
ReflectionUtils.doWithLocalMethods(targetClass, method -> { |
|
|
|
ReflectionUtils.doWithLocalMethods(targetClass, method -> { |
|
|
|
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { |
|
|
|
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { |
|
|
|
LifecycleElement element = new LifecycleElement(method); |
|
|
|
currInitMethods.add(new LifecycleMethod(method)); |
|
|
|
currInitMethods.add(element); |
|
|
|
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); |
|
|
|
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { |
|
|
|
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { |
|
|
|
currDestroyMethods.add(new LifecycleElement(method)); |
|
|
|
currDestroyMethods.add(new LifecycleMethod(method)); |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); |
|
|
|
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -312,18 +313,18 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
|
|
|
|
|
|
|
|
private final Class<?> targetClass; |
|
|
|
private final Class<?> targetClass; |
|
|
|
|
|
|
|
|
|
|
|
private final Collection<LifecycleElement> initMethods; |
|
|
|
private final Collection<LifecycleMethod> initMethods; |
|
|
|
|
|
|
|
|
|
|
|
private final Collection<LifecycleElement> destroyMethods; |
|
|
|
private final Collection<LifecycleMethod> destroyMethods; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private volatile Set<LifecycleElement> checkedInitMethods; |
|
|
|
private volatile Set<LifecycleMethod> checkedInitMethods; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private volatile Set<LifecycleElement> checkedDestroyMethods; |
|
|
|
private volatile Set<LifecycleMethod> checkedDestroyMethods; |
|
|
|
|
|
|
|
|
|
|
|
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods, |
|
|
|
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleMethod> initMethods, |
|
|
|
Collection<LifecycleElement> destroyMethods) { |
|
|
|
Collection<LifecycleMethod> destroyMethods) { |
|
|
|
|
|
|
|
|
|
|
|
this.targetClass = targetClass; |
|
|
|
this.targetClass = targetClass; |
|
|
|
this.initMethods = initMethods; |
|
|
|
this.initMethods = initMethods; |
|
|
|
@ -331,23 +332,23 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void checkInitDestroyMethods(RootBeanDefinition beanDefinition) { |
|
|
|
public void checkInitDestroyMethods(RootBeanDefinition beanDefinition) { |
|
|
|
Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size()); |
|
|
|
Set<LifecycleMethod> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size()); |
|
|
|
for (LifecycleElement element : this.initMethods) { |
|
|
|
for (LifecycleMethod lifecycleMethod : this.initMethods) { |
|
|
|
String methodIdentifier = element.getIdentifier(); |
|
|
|
String methodIdentifier = lifecycleMethod.getIdentifier(); |
|
|
|
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) { |
|
|
|
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) { |
|
|
|
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier); |
|
|
|
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier); |
|
|
|
checkedInitMethods.add(element); |
|
|
|
checkedInitMethods.add(lifecycleMethod); |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + methodIdentifier); |
|
|
|
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + methodIdentifier); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size()); |
|
|
|
Set<LifecycleMethod> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size()); |
|
|
|
for (LifecycleElement element : this.destroyMethods) { |
|
|
|
for (LifecycleMethod lifecycleMethod : this.destroyMethods) { |
|
|
|
String methodIdentifier = element.getIdentifier(); |
|
|
|
String methodIdentifier = lifecycleMethod.getIdentifier(); |
|
|
|
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) { |
|
|
|
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) { |
|
|
|
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier); |
|
|
|
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier); |
|
|
|
checkedDestroyMethods.add(element); |
|
|
|
checkedDestroyMethods.add(lifecycleMethod); |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + methodIdentifier); |
|
|
|
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + methodIdentifier); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -358,36 +359,36 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void invokeInitMethods(Object target, String beanName) throws Throwable { |
|
|
|
public void invokeInitMethods(Object target, String beanName) throws Throwable { |
|
|
|
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods; |
|
|
|
Collection<LifecycleMethod> checkedInitMethods = this.checkedInitMethods; |
|
|
|
Collection<LifecycleElement> initMethodsToIterate = |
|
|
|
Collection<LifecycleMethod> initMethodsToIterate = |
|
|
|
(checkedInitMethods != null ? checkedInitMethods : this.initMethods); |
|
|
|
(checkedInitMethods != null ? checkedInitMethods : this.initMethods); |
|
|
|
if (!initMethodsToIterate.isEmpty()) { |
|
|
|
if (!initMethodsToIterate.isEmpty()) { |
|
|
|
for (LifecycleElement element : initMethodsToIterate) { |
|
|
|
for (LifecycleMethod lifecycleMethod : initMethodsToIterate) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod()); |
|
|
|
logger.trace("Invoking init method on bean '" + beanName + "': " + lifecycleMethod.getMethod()); |
|
|
|
} |
|
|
|
} |
|
|
|
element.invoke(target); |
|
|
|
lifecycleMethod.invoke(target); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void invokeDestroyMethods(Object target, String beanName) throws Throwable { |
|
|
|
public void invokeDestroyMethods(Object target, String beanName) throws Throwable { |
|
|
|
Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods; |
|
|
|
Collection<LifecycleMethod> checkedDestroyMethods = this.checkedDestroyMethods; |
|
|
|
Collection<LifecycleElement> destroyMethodsToUse = |
|
|
|
Collection<LifecycleMethod> destroyMethodsToUse = |
|
|
|
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); |
|
|
|
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); |
|
|
|
if (!destroyMethodsToUse.isEmpty()) { |
|
|
|
if (!destroyMethodsToUse.isEmpty()) { |
|
|
|
for (LifecycleElement element : destroyMethodsToUse) { |
|
|
|
for (LifecycleMethod lifecycleMethod : destroyMethodsToUse) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); |
|
|
|
logger.trace("Invoking destroy method on bean '" + beanName + "': " + lifecycleMethod.getMethod()); |
|
|
|
} |
|
|
|
} |
|
|
|
element.invoke(target); |
|
|
|
lifecycleMethod.invoke(target); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public boolean hasDestroyMethods() { |
|
|
|
public boolean hasDestroyMethods() { |
|
|
|
Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods; |
|
|
|
Collection<LifecycleMethod> checkedDestroyMethods = this.checkedDestroyMethods; |
|
|
|
Collection<LifecycleElement> destroyMethodsToUse = |
|
|
|
Collection<LifecycleMethod> destroyMethodsToUse = |
|
|
|
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); |
|
|
|
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); |
|
|
|
return !destroyMethodsToUse.isEmpty(); |
|
|
|
return !destroyMethodsToUse.isEmpty(); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -395,17 +396,17 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Class representing injection information about an annotated method. |
|
|
|
* Class representing an annotated init or destroy method. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static class LifecycleElement { |
|
|
|
private static class LifecycleMethod { |
|
|
|
|
|
|
|
|
|
|
|
private final Method method; |
|
|
|
private final Method method; |
|
|
|
|
|
|
|
|
|
|
|
private final String identifier; |
|
|
|
private final String identifier; |
|
|
|
|
|
|
|
|
|
|
|
public LifecycleElement(Method method) { |
|
|
|
public LifecycleMethod(Method method) { |
|
|
|
if (method.getParameterCount() != 0) { |
|
|
|
if (method.getParameterCount() != 0) { |
|
|
|
throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method); |
|
|
|
throw new IllegalStateException("Lifecycle annotation requires a no-arg method: " + method); |
|
|
|
} |
|
|
|
} |
|
|
|
this.method = method; |
|
|
|
this.method = method; |
|
|
|
this.identifier = (Modifier.isPrivate(method.getModifiers()) ? |
|
|
|
this.identifier = (Modifier.isPrivate(method.getModifiers()) ? |
|
|
|
@ -422,18 +423,13 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB |
|
|
|
|
|
|
|
|
|
|
|
public void invoke(Object target) throws Throwable { |
|
|
|
public void invoke(Object target) throws Throwable { |
|
|
|
ReflectionUtils.makeAccessible(this.method); |
|
|
|
ReflectionUtils.makeAccessible(this.method); |
|
|
|
this.method.invoke(target, (Object[]) null); |
|
|
|
this.method.invoke(target); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean equals(@Nullable Object other) { |
|
|
|
public boolean equals(@Nullable Object other) { |
|
|
|
if (this == other) { |
|
|
|
return (this == other || (other instanceof LifecycleMethod that && |
|
|
|
return true; |
|
|
|
this.identifier.equals(that.identifier))); |
|
|
|
} |
|
|
|
|
|
|
|
if (!(other instanceof LifecycleElement otherElement)) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return (this.identifier.equals(otherElement.identifier)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
|