Skip to content

Commit 4c5d771

Browse files
committed
Merge pull request #247 from sbrannen/SPR-5613
* SPR-5613: Provide support for context hierarchies in the TCF
2 parents 7bc5353 + 98074e7 commit 4c5d771

File tree

55 files changed

+3908
-540
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3908
-540
lines changed

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

+54-9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.lang.reflect.AnnotatedElement;
2121
import java.lang.reflect.Method;
2222

23+
import java.util.List;
2324
import java.util.Map;
2425
import java.util.WeakHashMap;
2526

@@ -240,14 +241,58 @@ public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> a
240241
* if not found
241242
* @see Class#isAnnotationPresent(Class)
242243
* @see Class#getDeclaredAnnotations()
244+
* @see #findAnnotationDeclaringClassForTypes(List, Class)
245+
* @see #isAnnotationDeclaredLocally(Class, Class)
243246
*/
244247
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
245248
Assert.notNull(annotationType, "Annotation type must not be null");
246249
if (clazz == null || clazz.equals(Object.class)) {
247250
return null;
248251
}
249-
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz :
250-
findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
252+
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass(
253+
annotationType, clazz.getSuperclass());
254+
}
255+
256+
/**
257+
* Find the first {@link Class} in the inheritance hierarchy of the specified
258+
* {@code clazz} (including the specified {@code clazz} itself) which declares
259+
* at least one of the specified {@code annotationTypes}, or {@code null} if
260+
* none of the specified annotation types could be found.
261+
* <p>If the supplied {@code clazz} is {@code null}, {@code null} will be
262+
* returned.
263+
* <p>If the supplied {@code clazz} is an interface, only the interface itself
264+
* will be checked; the inheritance hierarchy for interfaces will not be traversed.
265+
* <p>The standard {@link Class} API does not provide a mechanism for determining
266+
* which class in an inheritance hierarchy actually declares one of several
267+
* candidate {@linkplain Annotation annotations}, so we need to handle this
268+
* explicitly.
269+
* @param annotationTypes the list of Class objects corresponding to the
270+
* annotation types
271+
* @param clazz the Class object corresponding to the class on which to check
272+
* for the annotations, or {@code null}
273+
* @return the first {@link Class} in the inheritance hierarchy of the specified
274+
* {@code clazz} which declares an annotation of at least one of the specified
275+
* {@code annotationTypes}, or {@code null} if not found
276+
* @see Class#isAnnotationPresent(Class)
277+
* @see Class#getDeclaredAnnotations()
278+
* @see #findAnnotationDeclaringClass(Class, Class)
279+
* @see #isAnnotationDeclaredLocally(Class, Class)
280+
* @since 3.2.2
281+
*/
282+
public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes,
283+
Class<?> clazz) {
284+
Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");
285+
if (clazz == null || clazz.equals(Object.class)) {
286+
return null;
287+
}
288+
289+
for (Class<? extends Annotation> annotationType : annotationTypes) {
290+
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
291+
return clazz;
292+
}
293+
}
294+
295+
return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
251296
}
252297

253298
/**
@@ -348,8 +393,8 @@ public static Map<String, Object> getAnnotationAttributes(Annotation annotation,
348393
* and corresponding attribute values as values
349394
* @since 3.1.1
350395
*/
351-
public static AnnotationAttributes getAnnotationAttributes(
352-
Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
396+
public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
397+
boolean nestedAnnotationsAsMap) {
353398

354399
AnnotationAttributes attrs = new AnnotationAttributes();
355400
Method[] methods = annotation.annotationType().getDeclaredMethods();
@@ -371,15 +416,15 @@ else if (value instanceof Class[]) {
371416
}
372417
}
373418
if (nestedAnnotationsAsMap && value instanceof Annotation) {
374-
attrs.put(method.getName(), getAnnotationAttributes(
375-
(Annotation)value, classValuesAsString, nestedAnnotationsAsMap));
419+
attrs.put(method.getName(),
420+
getAnnotationAttributes((Annotation) value, classValuesAsString, nestedAnnotationsAsMap));
376421
}
377422
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
378-
Annotation[] realAnnotations = (Annotation[])value;
423+
Annotation[] realAnnotations = (Annotation[]) value;
379424
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
380425
for (int i = 0; i < realAnnotations.length; i++) {
381-
mappedAnnotations[i] = getAnnotationAttributes(
382-
realAnnotations[i], classValuesAsString, nestedAnnotationsAsMap);
426+
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString,
427+
nestedAnnotationsAsMap);
383428
}
384429
attrs.put(method.getName(), mappedAnnotations);
385430
}

spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

+108-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -16,21 +16,26 @@
1616

1717
package org.springframework.core.annotation;
1818

19+
import static org.junit.Assert.*;
20+
import static org.springframework.core.annotation.AnnotationUtils.*;
21+
22+
import java.lang.annotation.Annotation;
1923
import java.lang.annotation.Inherited;
2024
import java.lang.annotation.Retention;
2125
import java.lang.annotation.RetentionPolicy;
2226
import java.lang.reflect.Method;
2327

28+
import java.util.Arrays;
29+
import java.util.List;
30+
2431
import org.junit.Test;
2532

2633
import org.springframework.core.Ordered;
2734
import org.springframework.stereotype.Component;
2835

29-
import static org.junit.Assert.*;
30-
31-
import static org.springframework.core.annotation.AnnotationUtils.*;
32-
3336
/**
37+
* Unit tests for {@link AnnotationUtils}.
38+
*
3439
* @author Rod Johnson
3540
* @author Juergen Hoeller
3641
* @author Sam Brannen
@@ -102,23 +107,91 @@ public void testFindAnnotationDeclaringClass() throws Exception {
102107
assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class));
103108

104109
// inherited class-level annotation; note: @Transactional is inherited
105-
assertEquals(InheritedAnnotationInterface.class, findAnnotationDeclaringClass(Transactional.class,
106-
InheritedAnnotationInterface.class));
110+
assertEquals(InheritedAnnotationInterface.class,
111+
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationInterface.class));
107112
assertNull(findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationInterface.class));
108-
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
109-
InheritedAnnotationClass.class));
110-
assertEquals(InheritedAnnotationClass.class, findAnnotationDeclaringClass(Transactional.class,
111-
SubInheritedAnnotationClass.class));
113+
assertEquals(InheritedAnnotationClass.class,
114+
findAnnotationDeclaringClass(Transactional.class, InheritedAnnotationClass.class));
115+
assertEquals(InheritedAnnotationClass.class,
116+
findAnnotationDeclaringClass(Transactional.class, SubInheritedAnnotationClass.class));
112117

113118
// non-inherited class-level annotation; note: @Order is not inherited,
114-
// but findAnnotationDeclaringClass() should still find it.
115-
assertEquals(NonInheritedAnnotationInterface.class, findAnnotationDeclaringClass(Order.class,
116-
NonInheritedAnnotationInterface.class));
119+
// but findAnnotationDeclaringClass() should still find it on classes.
120+
assertEquals(NonInheritedAnnotationInterface.class,
121+
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationInterface.class));
117122
assertNull(findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationInterface.class));
118-
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
119-
NonInheritedAnnotationClass.class));
120-
assertEquals(NonInheritedAnnotationClass.class, findAnnotationDeclaringClass(Order.class,
121-
SubNonInheritedAnnotationClass.class));
123+
assertEquals(NonInheritedAnnotationClass.class,
124+
findAnnotationDeclaringClass(Order.class, NonInheritedAnnotationClass.class));
125+
assertEquals(NonInheritedAnnotationClass.class,
126+
findAnnotationDeclaringClass(Order.class, SubNonInheritedAnnotationClass.class));
127+
}
128+
129+
@Test
130+
public void findAnnotationDeclaringClassForTypesWithSingleCandidateType() {
131+
132+
// no class-level annotation
133+
List<Class<? extends Annotation>> transactionalCandidateList = Arrays.<Class<? extends Annotation>> asList(Transactional.class);
134+
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedInterface.class));
135+
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList, NonAnnotatedClass.class));
136+
137+
// inherited class-level annotation; note: @Transactional is inherited
138+
assertEquals(InheritedAnnotationInterface.class,
139+
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationInterface.class));
140+
assertNull(findAnnotationDeclaringClassForTypes(transactionalCandidateList,
141+
SubInheritedAnnotationInterface.class));
142+
assertEquals(InheritedAnnotationClass.class,
143+
findAnnotationDeclaringClassForTypes(transactionalCandidateList, InheritedAnnotationClass.class));
144+
assertEquals(InheritedAnnotationClass.class,
145+
findAnnotationDeclaringClassForTypes(transactionalCandidateList, SubInheritedAnnotationClass.class));
146+
147+
// non-inherited class-level annotation; note: @Order is not inherited,
148+
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
149+
List<Class<? extends Annotation>> orderCandidateList = Arrays.<Class<? extends Annotation>> asList(Order.class);
150+
assertEquals(NonInheritedAnnotationInterface.class,
151+
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationInterface.class));
152+
assertNull(findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationInterface.class));
153+
assertEquals(NonInheritedAnnotationClass.class,
154+
findAnnotationDeclaringClassForTypes(orderCandidateList, NonInheritedAnnotationClass.class));
155+
assertEquals(NonInheritedAnnotationClass.class,
156+
findAnnotationDeclaringClassForTypes(orderCandidateList, SubNonInheritedAnnotationClass.class));
157+
}
158+
159+
@Test
160+
public void findAnnotationDeclaringClassForTypesWithMultipleCandidateTypes() {
161+
162+
List<Class<? extends Annotation>> candidates = Arrays.<Class<? extends Annotation>> asList(Transactional.class,
163+
Order.class);
164+
165+
// no class-level annotation
166+
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedInterface.class));
167+
assertNull(findAnnotationDeclaringClassForTypes(candidates, NonAnnotatedClass.class));
168+
169+
// inherited class-level annotation; note: @Transactional is inherited
170+
assertEquals(InheritedAnnotationInterface.class,
171+
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationInterface.class));
172+
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationInterface.class));
173+
assertEquals(InheritedAnnotationClass.class,
174+
findAnnotationDeclaringClassForTypes(candidates, InheritedAnnotationClass.class));
175+
assertEquals(InheritedAnnotationClass.class,
176+
findAnnotationDeclaringClassForTypes(candidates, SubInheritedAnnotationClass.class));
177+
178+
// non-inherited class-level annotation; note: @Order is not inherited,
179+
// but findAnnotationDeclaringClassForTypes() should still find it on classes.
180+
assertEquals(NonInheritedAnnotationInterface.class,
181+
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationInterface.class));
182+
assertNull(findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationInterface.class));
183+
assertEquals(NonInheritedAnnotationClass.class,
184+
findAnnotationDeclaringClassForTypes(candidates, NonInheritedAnnotationClass.class));
185+
assertEquals(NonInheritedAnnotationClass.class,
186+
findAnnotationDeclaringClassForTypes(candidates, SubNonInheritedAnnotationClass.class));
187+
188+
// class hierarchy mixed with @Transactional and @Order declarations
189+
assertEquals(TransactionalClass.class,
190+
findAnnotationDeclaringClassForTypes(candidates, TransactionalClass.class));
191+
assertEquals(TransactionalAndOrderedClass.class,
192+
findAnnotationDeclaringClassForTypes(candidates, TransactionalAndOrderedClass.class));
193+
assertEquals(TransactionalAndOrderedClass.class,
194+
findAnnotationDeclaringClassForTypes(candidates, SubTransactionalAndOrderedClass.class));
122195
}
123196

124197
@Test
@@ -216,18 +289,18 @@ public void testFindAnnotationFromInterfaceWhenSuperDoesNotImplementMethod() thr
216289
}
217290

218291

219-
@Component(value="meta1")
292+
@Component(value = "meta1")
220293
@Retention(RetentionPolicy.RUNTIME)
221294
@interface Meta1 {
222295
}
223296

224-
@Component(value="meta2")
297+
@Component(value = "meta2")
225298
@Retention(RetentionPolicy.RUNTIME)
226299
@interface Meta2 {
227300
}
228301

229302
@Meta1
230-
@Component(value="local")
303+
@Component(value = "local")
231304
@Meta2
232305
static class HasLocalAndMetaComponentAnnotation {
233306
}
@@ -332,6 +405,16 @@ public static class NonInheritedAnnotationClass {
332405
public static class SubNonInheritedAnnotationClass extends NonInheritedAnnotationClass {
333406
}
334407

408+
@Transactional
409+
public static class TransactionalClass {
410+
}
411+
412+
@Order
413+
public static class TransactionalAndOrderedClass {
414+
}
415+
416+
public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass {
417+
}
335418

336419
public static interface InterfaceWithAnnotatedMethod {
337420

@@ -353,10 +436,12 @@ public void foo() {
353436
}
354437
}
355438

356-
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements InterfaceWithAnnotatedMethod {
439+
public abstract static class AbstractDoesNotImplementInterfaceWithAnnotatedMethod implements
440+
InterfaceWithAnnotatedMethod {
357441
}
358442

359-
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
443+
public static class SubOfAbstractImplementsInterfaceWithAnnotatedMethod extends
444+
AbstractDoesNotImplementInterfaceWithAnnotatedMethod {
360445

361446
@Override
362447
public void foo() {

spring-test/.springBeans

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<config>src/test/java/org/springframework/test/context/junit4/profile/xml/DefaultProfileXmlConfigTests-context.xml</config>
1111
<config>src/test/java/org/springframework/test/context/junit4/aci/xml/MultipleInitializersXmlConfigTests-context.xml</config>
1212
<config>src/test/resources/org/springframework/test/context/web/RequestAndSessionScopedBeansWacTests-context.xml</config>
13+
<config>src/test/resources/org/springframework/test/context/hierarchies/web/DispatcherWacRootWacEarTests-context.xml</config>
1314
</configs>
1415
<configSets>
1516
</configSets>

spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
*
4141
* <p>There are various choices for DataSource implementations:
4242
* <ul>
43-
* <li>SingleConnectionDataSource (using the same Connection for all getConnection calls);
44-
* <li>DriverManagerDataSource (creating a new Connection on each getConnection call);
45-
* <li>Apache's Jakarta Commons DBCP offers BasicDataSource (a real pool).
43+
* <li>{@code SingleConnectionDataSource} (using the same Connection for all getConnection calls)
44+
* <li>{@code DriverManagerDataSource} (creating a new Connection on each getConnection call)
45+
* <li>Apache's Jakarta Commons DBCP offers {@code org.apache.commons.dbcp.BasicDataSource} (a real pool)
4646
* </ul>
4747
*
4848
* <p>Typical usage in bootstrap code:
@@ -77,7 +77,6 @@
7777
* @see SimpleNamingContext
7878
* @see org.springframework.jdbc.datasource.SingleConnectionDataSource
7979
* @see org.springframework.jdbc.datasource.DriverManagerDataSource
80-
* @see org.apache.commons.dbcp.BasicDataSource
8180
*/
8281
public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder {
8382

spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java

+2
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ public MockServletContext(ResourceLoader resourceLoader) {
163163
* @param resourceLoader the ResourceLoader to use (or null for the default)
164164
* @see #registerNamedDispatcher
165165
*/
166+
@SuppressWarnings("javadoc")
166167
public MockServletContext(String resourceBasePath, ResourceLoader resourceLoader) {
167168
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
168169
this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
@@ -344,6 +345,7 @@ public void unregisterNamedDispatcher(String name) {
344345
* <p>Defaults to {@linkplain #COMMON_DEFAULT_SERVLET_NAME "default"}.
345346
* @see #setDefaultServletName
346347
*/
348+
@SuppressWarnings("javadoc")
347349
public String getDefaultServletName() {
348350
return this.defaultServletName;
349351
}

spring-test/src/main/java/org/springframework/test/AbstractDependencyInjectionSpringContextTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ protected void prepareTestInstance() throws Exception {
197197
* test instance has not been configured
198198
* @see #populateProtectedVariables()
199199
*/
200+
@SuppressWarnings("javadoc")
200201
protected void injectDependencies() throws Exception {
201202
Assert.state(getApplicationContext() != null,
202203
"injectDependencies() called without first configuring an ApplicationContext");

0 commit comments

Comments
 (0)