From c0ddaae5c0272af8b4eeb330bb8660da7411e99d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Mar 2019 22:45:24 +0100 Subject: [PATCH] Shared empty InjectionMetadata/LifecycleMetadata instance Closes gh-22570 --- .../AutowiredAnnotationBeanPostProcessor.java | 4 +- ...nitDestroyAnnotationBeanPostProcessor.java | 23 +++++++++- .../factory/annotation/InjectionMetadata.java | 46 ++++++++++++++++++- .../CommonAnnotationBeanPostProcessor.java | 4 +- ...ersistenceAnnotationBeanPostProcessor.java | 5 +- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index d19d2340b0c..5f71ada4b3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -443,7 +443,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean private InjectionMetadata buildAutowiringMetadata(final Class clazz) { if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { - return new InjectionMetadata(clazz, Collections.emptyList()); + return InjectionMetadata.EMPTY; } List elements = new ArrayList<>(); @@ -496,7 +496,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean } while (targetClass != null && targetClass != Object.class); - return new InjectionMetadata(clazz, elements); + return InjectionMetadata.forElements(elements, clazz); } @Nullable diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java index 162e464a690..914204e8f1a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java @@ -80,6 +80,24 @@ import org.springframework.util.ReflectionUtils; public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable { + private final transient LifecycleMetadata emptyLifecycleMetadata = + new LifecycleMetadata(Object.class, Collections.emptyList(), Collections.emptyList()) { + @Override + public void checkConfigMembers(RootBeanDefinition beanDefinition) { + } + @Override + public void invokeInitMethods(Object target, String beanName) { + } + @Override + public void invokeDestroyMethods(Object target, String beanName) { + } + @Override + public boolean hasDestroyMethods() { + return false; + } + }; + + protected transient Log logger = LogFactory.getLog(getClass()); @Nullable @@ -200,7 +218,7 @@ public class InitDestroyAnnotationBeanPostProcessor private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { - return new LifecycleMetadata(clazz, Collections.emptyList(), Collections.emptyList()); + return this.emptyLifecycleMetadata; } List initMethods = new ArrayList<>(); @@ -233,7 +251,8 @@ public class InitDestroyAnnotationBeanPostProcessor } while (targetClass != null && targetClass != Object.class); - return new LifecycleMetadata(clazz, initMethods, destroyMethods); + return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : + new LifecycleMetadata(clazz, initMethods, destroyMethods)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java index 1c68ff1b622..cc0ddc52e1c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -47,6 +48,23 @@ import org.springframework.util.ReflectionUtils; */ public class InjectionMetadata { + /** + * An empty {@code InjectionMetadata} instance with no-op callbacks. + * @since 5.2 + */ + public static final InjectionMetadata EMPTY = new InjectionMetadata(Object.class, Collections.emptyList()) { + @Override + public void checkConfigMembers(RootBeanDefinition beanDefinition) { + } + @Override + public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) { + } + @Override + public void clear(@Nullable PropertyValues pvs) { + } + }; + + private static final Log logger = LogFactory.getLog(InjectionMetadata.class); private final Class targetClass; @@ -57,6 +75,14 @@ public class InjectionMetadata { private volatile Set checkedElements; + /** + * Create a new {@code InjectionMetadata instance}. + *

Preferably use {@link #forElements} for reusing the {@link #EMPTY} + * instance in case of no elements. + * @param targetClass the target class + * @param elements the associated elements to inject + * @see #forElements + */ public InjectionMetadata(Class targetClass, Collection elements) { this.targetClass = targetClass; this.injectedElements = elements; @@ -108,6 +134,24 @@ public class InjectionMetadata { } + /** + * Return an {@code InjectionMetadata} instance, possibly for empty elements. + * @param elements the elements to inject (possibly empty) + * @param clazz the target class + * @return a new {@code InjectionMetadata} instance, + * or {@link #EMPTY} in case of no elements + * @since 5.2 + */ + public static InjectionMetadata forElements(Collection elements, Class clazz) { + return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements)); + } + + /** + * Check whether the given injection metadata needs to be refreshed. + * @param metadata the existing metadata instance + * @param clazz the current target class + * @return {@code true} indicating a refresh, {@code false} otherwise + */ public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class clazz) { return (metadata == null || metadata.targetClass != clazz); } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java index 19d4bdd98c3..d70bc8a7687 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java @@ -369,7 +369,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean private InjectionMetadata buildResourceMetadata(final Class clazz) { if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) { - return new InjectionMetadata(clazz, Collections.emptyList()); + return InjectionMetadata.EMPTY; } List elements = new ArrayList<>(); @@ -448,7 +448,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean } while (targetClass != null && targetClass != Object.class); - return new InjectionMetadata(clazz, elements); + return InjectionMetadata.forElements(elements, clazz); } /** diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index 094f6ca70d7..06e4b99d276 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -24,7 +24,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -416,7 +415,7 @@ public class PersistenceAnnotationBeanPostProcessor private InjectionMetadata buildPersistenceMetadata(final Class clazz) { if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(PersistenceContext.class, PersistenceUnit.class))) { - return new InjectionMetadata(clazz, Collections.emptyList()); + return InjectionMetadata.EMPTY; } List elements = new ArrayList<>(); @@ -459,7 +458,7 @@ public class PersistenceAnnotationBeanPostProcessor } while (targetClass != null && targetClass != Object.class); - return new InjectionMetadata(clazz, elements); + return InjectionMetadata.forElements(elements, clazz); } /**