Skip to content

Commit 589b704

Browse files
committed
Avoid synthesizable annotation creation for @Bean/@scope processing
Includes consistent (non-)use of AnnotationUtils/AnnotatedElementUtils. Issue: SPR-16933
1 parent 9b671f8 commit 589b704

File tree

6 files changed

+68
-47
lines changed

6 files changed

+68
-47
lines changed

spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
package org.springframework.context.annotation;
1818

1919
import java.lang.reflect.Method;
20+
import java.util.Map;
2021

2122
import org.springframework.core.annotation.AnnotatedElementUtils;
23+
import org.springframework.core.annotation.AnnotationAttributes;
24+
import org.springframework.util.ConcurrentReferenceHashMap;
2225

2326
/**
2427
* Utilities for processing {@link Bean}-annotated methods.
@@ -27,27 +30,45 @@
2730
* @author Juergen Hoeller
2831
* @since 3.1
2932
*/
30-
final class BeanAnnotationHelper {
33+
abstract class BeanAnnotationHelper {
3134

32-
private BeanAnnotationHelper() {
33-
}
35+
private static final Map<Method, String> beanNameCache = new ConcurrentReferenceHashMap<>();
36+
37+
private static final Map<Method, Boolean> scopedProxyCache = new ConcurrentReferenceHashMap<>();
3438

3539

3640
public static boolean isBeanAnnotated(Method method) {
3741
return AnnotatedElementUtils.hasAnnotation(method, Bean.class);
3842
}
3943

4044
public static String determineBeanNameFor(Method beanMethod) {
41-
// By default, the bean name is the name of the @Bean-annotated method
42-
String beanName = beanMethod.getName();
43-
44-
// Check to see if the user has explicitly set a custom bean name...
45-
Bean bean = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Bean.class);
46-
if (bean != null && bean.name().length > 0) {
47-
beanName = bean.name()[0];
45+
String beanName = beanNameCache.get(beanMethod);
46+
if (beanName == null) {
47+
// By default, the bean name is the name of the @Bean-annotated method
48+
beanName = beanMethod.getName();
49+
// Check to see if the user has explicitly set a custom bean name...
50+
AnnotationAttributes bean =
51+
AnnotatedElementUtils.findMergedAnnotationAttributes(beanMethod, Bean.class, false, false);
52+
if (bean != null) {
53+
String[] names = bean.getStringArray("name");
54+
if (names.length > 0) {
55+
beanName = names[0];
56+
}
57+
}
58+
beanNameCache.put(beanMethod, beanName);
4859
}
49-
5060
return beanName;
5161
}
5262

63+
public static boolean isScopedProxy(Method beanMethod) {
64+
Boolean scopedProxy = scopedProxyCache.get(beanMethod);
65+
if (scopedProxy == null) {
66+
AnnotationAttributes scope =
67+
AnnotatedElementUtils.findMergedAnnotationAttributes(beanMethod, Scope.class, false, false);
68+
scopedProxy = (scope != null && scope.getEnum("proxyMode") != ScopedProxyMode.NO);
69+
scopedProxyCache.put(beanMethod, scopedProxy);
70+
}
71+
return scopedProxy;
72+
}
73+
5374
}

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.springframework.cglib.proxy.NoOp;
4848
import org.springframework.cglib.transform.ClassEmitterTransformer;
4949
import org.springframework.cglib.transform.TransformingClassGenerator;
50-
import org.springframework.core.annotation.AnnotatedElementUtils;
5150
import org.springframework.lang.Nullable;
5251
import org.springframework.objenesis.ObjenesisException;
5352
import org.springframework.objenesis.SpringObjenesis;
@@ -317,8 +316,7 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object
317316
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
318317

319318
// Determine whether this bean is a scoped-proxy
320-
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
321-
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
319+
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
322320
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
323321
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
324322
beanName = scopedBeanName;

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -585,22 +585,20 @@ public static boolean hasAnnotation(AnnotatedElement element, Class<? extends An
585585
* attributes of the same name from higher levels, and
586586
* {@link AliasFor @AliasFor} semantics are fully supported, both
587587
* within a single annotation and within the annotation hierarchy.
588-
* <p>In contrast to {@link #getAllAnnotationAttributes}, the search
589-
* algorithm used by this method will stop searching the annotation
590-
* hierarchy once the first annotation of the specified
591-
* {@code annotationType} has been found. As a consequence, additional
592-
* annotations of the specified {@code annotationType} will be ignored.
588+
* <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm
589+
* used by this method will stop searching the annotation hierarchy once the
590+
* first annotation of the specified {@code annotationType} has been found.
591+
* As a consequence, additional annotations of the specified
592+
* {@code annotationType} will be ignored.
593593
* <p>This method follows <em>find semantics</em> as described in the
594594
* {@linkplain AnnotatedElementUtils class-level javadoc}.
595595
* @param element the annotated element
596596
* @param annotationType the annotation type to find
597597
* @param classValuesAsString whether to convert Class references into
598598
* Strings or to preserve them as Class references
599-
* @param nestedAnnotationsAsMap whether to convert nested Annotation
600-
* instances into {@code AnnotationAttributes} maps or to preserve them
601-
* as Annotation instances
602-
* @return the merged {@code AnnotationAttributes}, or {@code null} if
603-
* not found
599+
* @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
600+
* {@code AnnotationAttributes} maps or to preserve them as Annotation instances
601+
* @return the merged {@code AnnotationAttributes}, or {@code null} if not found
604602
* @since 4.2
605603
* @see #findMergedAnnotation(AnnotatedElement, Class)
606604
* @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)

spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,26 @@ public void visitEnd() {
7575
}
7676
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {
7777
Set<Annotation> visited = new LinkedHashSet<>();
78-
Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
79-
if (!ObjectUtils.isEmpty(metaAnnotations)) {
80-
for (Annotation metaAnnotation : metaAnnotations) {
81-
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
82-
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
78+
try {
79+
Annotation[] metaAnnotations = annotationClass.getAnnotations();
80+
if (!ObjectUtils.isEmpty(metaAnnotations)) {
81+
for (Annotation metaAnnotation : metaAnnotations) {
82+
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
83+
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
84+
}
8385
}
8486
}
87+
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
88+
for (Annotation ann : visited) {
89+
metaAnnotationTypeNames.add(ann.annotationType().getName());
90+
}
91+
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
8592
}
86-
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
87-
for (Annotation ann : visited) {
88-
metaAnnotationTypeNames.add(ann.annotationType().getName());
93+
catch (Throwable ex) {
94+
if (logger.isDebugEnabled()) {
95+
logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);
96+
}
8997
}
90-
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
9198
}
9299
}
93100
}
@@ -110,7 +117,7 @@ private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotati
110117
}
111118
catch (Throwable ex) {
112119
if (logger.isDebugEnabled()) {
113-
logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex);
120+
logger.debug("Failed to introspect meta-annotations on " + annotation + ": " + ex);
114121
}
115122
}
116123
}

spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
192192
}
193193

194194
private DestinationHelper getDestinationHelper(MessageHeaders headers, MethodParameter returnType) {
195-
196195
SendToUser m1 = AnnotatedElementUtils.findMergedAnnotation(returnType.getExecutable(), SendToUser.class);
197196
SendTo m2 = AnnotatedElementUtils.findMergedAnnotation(returnType.getExecutable(), SendTo.class);
198197
if ((m1 != null && !ObjectUtils.isEmpty(m1.value())) || (m2 != null && !ObjectUtils.isEmpty(m2.value()))) {
@@ -205,8 +204,8 @@ private DestinationHelper getDestinationHelper(MessageHeaders headers, MethodPar
205204
return new DestinationHelper(headers, c1, c2);
206205
}
207206

208-
return m1 != null || m2 != null ?
209-
new DestinationHelper(headers, m1, m2) : new DestinationHelper(headers, c1, c2);
207+
return (m1 != null || m2 != null ?
208+
new DestinationHelper(headers, m1, m2) : new DestinationHelper(headers, c1, c2));
210209
}
211210

212211
@Nullable
@@ -291,9 +290,9 @@ public SendToUser getSendToUser() {
291290
return this.sendToUser;
292291
}
293292

294-
295293
public String expandTemplateVars(String destination) {
296294
return placeholderHelper.replacePlaceholders(destination, this.placeholderResolver);
297295
}
298296
}
297+
299298
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
2222

2323
import org.springframework.core.MethodParameter;
2424
import org.springframework.core.ReactiveAdapterRegistry;
25-
import org.springframework.core.annotation.AnnotationUtils;
25+
import org.springframework.core.annotation.AnnotatedElementUtils;
2626
import org.springframework.http.codec.HttpMessageWriter;
2727
import org.springframework.web.bind.annotation.ResponseBody;
2828
import org.springframework.web.reactive.HandlerResult;
@@ -53,9 +53,7 @@ public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandle
5353
* @param writers writers for serializing to the response body
5454
* @param resolver to determine the requested content type
5555
*/
56-
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> writers,
57-
RequestedContentTypeResolver resolver) {
58-
56+
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver) {
5957
this(writers, resolver, ReactiveAdapterRegistry.getSharedInstance());
6058
}
6159

@@ -75,10 +73,10 @@ public ResponseBodyResultHandler(List<HttpMessageWriter<?>> writers,
7573

7674
@Override
7775
public boolean supports(HandlerResult result) {
78-
MethodParameter parameter = result.getReturnTypeSource();
79-
Class<?> containingClass = parameter.getContainingClass();
80-
return (AnnotationUtils.findAnnotation(containingClass, ResponseBody.class) != null ||
81-
parameter.getMethodAnnotation(ResponseBody.class) != null);
76+
MethodParameter returnType = result.getReturnTypeSource();
77+
Class<?> containingClass = returnType.getContainingClass();
78+
return (AnnotatedElementUtils.hasAnnotation(containingClass, ResponseBody.class) ||
79+
returnType.hasMethodAnnotation(ResponseBody.class));
8280
}
8381

8482
@Override

0 commit comments

Comments
 (0)