Merge branch '6.0.x'
This commit is contained in:
commit
83447663b4
|
|
@ -152,8 +152,8 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanClass, String beanName) {
|
||||||
findLifecycleMetadata(beanDefinition, beanType);
|
findLifecycleMetadata(beanDefinition, beanClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -173,8 +173,8 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LifecycleMetadata findLifecycleMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) {
|
private LifecycleMetadata findLifecycleMetadata(RootBeanDefinition beanDefinition, Class<?> beanClass) {
|
||||||
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
|
LifecycleMetadata metadata = findLifecycleMetadata(beanClass);
|
||||||
metadata.checkInitDestroyMethods(beanDefinition);
|
metadata.checkInitDestroyMethods(beanDefinition);
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
@ -234,19 +234,19 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
|
private LifecycleMetadata findLifecycleMetadata(Class<?> beanClass) {
|
||||||
if (this.lifecycleMetadataCache == null) {
|
if (this.lifecycleMetadataCache == null) {
|
||||||
// Happens after deserialization, during destruction...
|
// Happens after deserialization, during destruction...
|
||||||
return buildLifecycleMetadata(clazz);
|
return buildLifecycleMetadata(beanClass);
|
||||||
}
|
}
|
||||||
// Quick check on the concurrent map first, with minimal locking.
|
// Quick check on the concurrent map first, with minimal locking.
|
||||||
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
|
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(beanClass);
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
synchronized (this.lifecycleMetadataCache) {
|
synchronized (this.lifecycleMetadataCache) {
|
||||||
metadata = this.lifecycleMetadataCache.get(clazz);
|
metadata = this.lifecycleMetadataCache.get(beanClass);
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
metadata = buildLifecycleMetadata(clazz);
|
metadata = buildLifecycleMetadata(beanClass);
|
||||||
this.lifecycleMetadataCache.put(clazz, metadata);
|
this.lifecycleMetadataCache.put(beanClass, metadata);
|
||||||
}
|
}
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
@ -254,42 +254,42 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
|
private LifecycleMetadata buildLifecycleMetadata(final Class<?> beanClass) {
|
||||||
if (!AnnotationUtils.isCandidateClass(clazz, List.of(this.initAnnotationType, this.destroyAnnotationType))) {
|
if (!AnnotationUtils.isCandidateClass(beanClass, List.of(this.initAnnotationType, this.destroyAnnotationType))) {
|
||||||
return this.emptyLifecycleMetadata;
|
return this.emptyLifecycleMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<LifecycleMethod> initMethods = new ArrayList<>();
|
List<LifecycleMethod> initMethods = new ArrayList<>();
|
||||||
List<LifecycleMethod> destroyMethods = new ArrayList<>();
|
List<LifecycleMethod> destroyMethods = new ArrayList<>();
|
||||||
Class<?> targetClass = clazz;
|
Class<?> currentClass = beanClass;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
final List<LifecycleMethod> currInitMethods = new ArrayList<>();
|
final List<LifecycleMethod> currInitMethods = new ArrayList<>();
|
||||||
final List<LifecycleMethod> currDestroyMethods = new ArrayList<>();
|
final List<LifecycleMethod> currDestroyMethods = new ArrayList<>();
|
||||||
|
|
||||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
ReflectionUtils.doWithLocalMethods(currentClass, method -> {
|
||||||
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
|
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
|
||||||
currInitMethods.add(new LifecycleMethod(method));
|
currInitMethods.add(new LifecycleMethod(method, beanClass));
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
|
logger.trace("Found init method on class [" + beanClass.getName() + "]: " + method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
|
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
|
||||||
currDestroyMethods.add(new LifecycleMethod(method));
|
currDestroyMethods.add(new LifecycleMethod(method, beanClass));
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
|
logger.trace("Found destroy method on class [" + beanClass.getName() + "]: " + method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
initMethods.addAll(0, currInitMethods);
|
initMethods.addAll(0, currInitMethods);
|
||||||
destroyMethods.addAll(currDestroyMethods);
|
destroyMethods.addAll(currDestroyMethods);
|
||||||
targetClass = targetClass.getSuperclass();
|
currentClass = currentClass.getSuperclass();
|
||||||
}
|
}
|
||||||
while (targetClass != null && targetClass != Object.class);
|
while (currentClass != null && currentClass != Object.class);
|
||||||
|
|
||||||
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
|
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
|
||||||
new LifecycleMetadata(clazz, initMethods, destroyMethods));
|
new LifecycleMetadata(beanClass, initMethods, destroyMethods));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -311,7 +311,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
*/
|
*/
|
||||||
private class LifecycleMetadata {
|
private class LifecycleMetadata {
|
||||||
|
|
||||||
private final Class<?> targetClass;
|
private final Class<?> beanClass;
|
||||||
|
|
||||||
private final Collection<LifecycleMethod> initMethods;
|
private final Collection<LifecycleMethod> initMethods;
|
||||||
|
|
||||||
|
|
@ -323,10 +323,10 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile Set<LifecycleMethod> checkedDestroyMethods;
|
private volatile Set<LifecycleMethod> checkedDestroyMethods;
|
||||||
|
|
||||||
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleMethod> initMethods,
|
public LifecycleMetadata(Class<?> beanClass, Collection<LifecycleMethod> initMethods,
|
||||||
Collection<LifecycleMethod> destroyMethods) {
|
Collection<LifecycleMethod> destroyMethods) {
|
||||||
|
|
||||||
this.targetClass = targetClass;
|
this.beanClass = beanClass;
|
||||||
this.initMethods = initMethods;
|
this.initMethods = initMethods;
|
||||||
this.destroyMethods = destroyMethods;
|
this.destroyMethods = destroyMethods;
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +339,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
|
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
|
||||||
checkedInitMethods.add(lifecycleMethod);
|
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.beanClass.getName() + "]: " + methodIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -350,7 +350,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
|
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
|
||||||
checkedDestroyMethods.add(lifecycleMethod);
|
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.beanClass.getName() + "]: " + methodIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -404,12 +404,12 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
|
|
||||||
private final String identifier;
|
private final String identifier;
|
||||||
|
|
||||||
public LifecycleMethod(Method method) {
|
public LifecycleMethod(Method method, Class<?> beanClass) {
|
||||||
if (method.getParameterCount() != 0) {
|
if (method.getParameterCount() != 0) {
|
||||||
throw new IllegalStateException("Lifecycle 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 = (isPrivateOrNotVisible(method, beanClass) ?
|
||||||
ClassUtils.getQualifiedMethodName(method) : method.getName());
|
ClassUtils.getQualifiedMethodName(method) : method.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,6 +436,23 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return this.identifier.hashCode();
|
return this.identifier.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the supplied lifecycle {@link Method} is private or not
|
||||||
|
* visible to the supplied bean {@link Class}.
|
||||||
|
* @since 6.0.11
|
||||||
|
*/
|
||||||
|
private static boolean isPrivateOrNotVisible(Method method, Class<?> beanClass) {
|
||||||
|
int modifiers = method.getModifiers();
|
||||||
|
if (Modifier.isPrivate(modifiers)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Method is declared in a class that resides in a different package
|
||||||
|
// than the bean class and the method is neither public nor protected?
|
||||||
|
return (!method.getDeclaringClass().getPackageName().equals(beanClass.getPackageName()) &&
|
||||||
|
!(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -497,14 +497,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an externally managed configuration initialization method —
|
* Register an externally managed configuration initialization method —
|
||||||
* for example, a method annotated with JSR-250's
|
* for example, a method annotated with JSR-250's {@link javax.annotation.PostConstruct}
|
||||||
* {@link jakarta.annotation.PostConstruct} annotation.
|
* or Jakarta's {@link jakarta.annotation.PostConstruct} annotation.
|
||||||
* <p>The supplied {@code initMethod} may be the
|
* <p>The supplied {@code initMethod} may be a
|
||||||
* {@linkplain Method#getName() simple method name} for non-private methods or the
|
* {@linkplain Method#getName() simple method name} or a
|
||||||
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
|
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
|
||||||
* qualified method name} for {@code private} methods. A qualified name is
|
* qualified method name} for package-private and {@code private} methods.
|
||||||
* necessary for {@code private} methods in order to disambiguate between
|
* A qualified name is necessary for package-private and {@code private} methods
|
||||||
* multiple private methods with the same name within a class hierarchy.
|
* in order to disambiguate between multiple such methods with the same name
|
||||||
|
* within a type hierarchy.
|
||||||
*/
|
*/
|
||||||
public void registerExternallyManagedInitMethod(String initMethod) {
|
public void registerExternallyManagedInitMethod(String initMethod) {
|
||||||
synchronized (this.postProcessingLock) {
|
synchronized (this.postProcessingLock) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
|
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.annotation.lifecyclemethods.InitDestroyBean;
|
||||||
|
import org.springframework.context.annotation.lifecyclemethods.PackagePrivateInitDestroyBean;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
|
@ -53,11 +55,11 @@ class InitDestroyMethodLifecycleTests {
|
||||||
@Test
|
@Test
|
||||||
void initDestroyMethods() {
|
void initDestroyMethods() {
|
||||||
Class<?> beanClass = InitDestroyBean.class;
|
Class<?> beanClass = InitDestroyBean.class;
|
||||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
|
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "initMethod", "destroyMethod");
|
||||||
InitDestroyBean bean = beanFactory.getBean(InitDestroyBean.class);
|
InitDestroyBean bean = beanFactory.getBean(InitDestroyBean.class);
|
||||||
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet");
|
assertThat(bean.initMethods).as("init-methods").containsExactly("initMethod");
|
||||||
beanFactory.destroySingletons();
|
beanFactory.destroySingletons();
|
||||||
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy");
|
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroyMethod");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -132,6 +134,26 @@ class InitDestroyMethodLifecycleTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jakartaAnnotationsCustomPackagePrivateInitDestroyMethodsWithTheSameMethodNames() {
|
||||||
|
Class<?> beanClass = SubPackagePrivateInitDestroyBean.class;
|
||||||
|
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "initMethod", "destroyMethod");
|
||||||
|
SubPackagePrivateInitDestroyBean bean = beanFactory.getBean(SubPackagePrivateInitDestroyBean.class);
|
||||||
|
|
||||||
|
assertThat(bean.initMethods).as("init-methods").containsExactly(
|
||||||
|
"PackagePrivateInitDestroyBean.postConstruct",
|
||||||
|
"SubPackagePrivateInitDestroyBean.postConstruct",
|
||||||
|
"initMethod"
|
||||||
|
);
|
||||||
|
|
||||||
|
beanFactory.destroySingletons();
|
||||||
|
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly(
|
||||||
|
"SubPackagePrivateInitDestroyBean.preDestroy",
|
||||||
|
"PackagePrivateInitDestroyBean.preDestroy",
|
||||||
|
"destroyMethod"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void allLifecycleMechanismsAtOnce() {
|
void allLifecycleMechanismsAtOnce() {
|
||||||
Class<?> beanClass = AllInOneBean.class;
|
Class<?> beanClass = AllInOneBean.class;
|
||||||
|
|
@ -165,21 +187,6 @@ class InitDestroyMethodLifecycleTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class InitDestroyBean {
|
|
||||||
|
|
||||||
final List<String> initMethods = new ArrayList<>();
|
|
||||||
final List<String> destroyMethods = new ArrayList<>();
|
|
||||||
|
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
|
||||||
this.initMethods.add("afterPropertiesSet");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
this.destroyMethods.add("destroy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
|
static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
|
||||||
InitializingBean, DisposableBean {
|
InitializingBean, DisposableBean {
|
||||||
|
|
||||||
|
|
@ -296,4 +303,18 @@ class InitDestroyMethodLifecycleTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SubPackagePrivateInitDestroyBean extends PackagePrivateInitDestroyBean {
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void postConstruct() {
|
||||||
|
this.initMethods.add("SubPackagePrivateInitDestroyBean.postConstruct");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
void preDestroy() {
|
||||||
|
this.destroyMethods.add("SubPackagePrivateInitDestroyBean.preDestroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.context.annotation.lifecyclemethods;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InitDestroyBean {
|
||||||
|
|
||||||
|
public final List<String> initMethods = new ArrayList<>();
|
||||||
|
public final List<String> destroyMethods = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
public void initMethod() {
|
||||||
|
this.initMethods.add("initMethod");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroyMethod() {
|
||||||
|
this.destroyMethods.add("destroyMethod");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.context.annotation.lifecyclemethods;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
|
||||||
|
public class PackagePrivateInitDestroyBean extends InitDestroyBean {
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
void postConstruct() {
|
||||||
|
this.initMethods.add("PackagePrivateInitDestroyBean.postConstruct");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
void preDestroy() {
|
||||||
|
this.destroyMethods.add("PackagePrivateInitDestroyBean.preDestroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue