Skip to content

Commit d97add0

Browse files
committed
Consistent bridge method handling in annotation post-processors
Issue: SPR-12495 (cherry picked from commit 03d4e1b)
1 parent c8ff562 commit d97add0

File tree

8 files changed

+97
-61
lines changed

8 files changed

+97
-61
lines changed

spring-beans/src/main/java/org/springframework/beans/BeanUtils.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -379,13 +379,28 @@ public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String pr
379379
* Find a JavaBeans {@code PropertyDescriptor} for the given method,
380380
* with the method either being the read method or the write method for
381381
* that bean property.
382-
* @param method the method to find a corresponding PropertyDescriptor for
382+
* @param method the method to find a corresponding PropertyDescriptor for,
383+
* introspecting its declaring class
383384
* @return the corresponding PropertyDescriptor, or {@code null} if none
384385
* @throws BeansException if PropertyDescriptor lookup fails
385386
*/
386387
public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException {
388+
return findPropertyForMethod(method, method.getDeclaringClass());
389+
}
390+
391+
/**
392+
* Find a JavaBeans {@code PropertyDescriptor} for the given method,
393+
* with the method either being the read method or the write method for
394+
* that bean property.
395+
* @param method the method to find a corresponding PropertyDescriptor for
396+
* @param clazz the (most specific) class to introspect for descriptors
397+
* @return the corresponding PropertyDescriptor, or {@code null} if none
398+
* @throws BeansException if PropertyDescriptor lookup fails
399+
* @since 4.0.9
400+
*/
401+
public static PropertyDescriptor findPropertyForMethod(Method method, Class<?> clazz) throws BeansException {
387402
Assert.notNull(method, "Method must not be null");
388-
PropertyDescriptor[] pds = getPropertyDescriptors(method.getDeclaringClass());
403+
PropertyDescriptor[] pds = getPropertyDescriptors(clazz);
389404
for (PropertyDescriptor pd : pds) {
390405
if (method.equals(pd.getReadMethod()) || method.equals(pd.getWriteMethod())) {
391406
return pd;
@@ -591,11 +606,11 @@ private static void copyProperties(Object source, Object target, Class<?> editab
591606
actualEditable = editable;
592607
}
593608
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
594-
List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
609+
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
595610

596611
for (PropertyDescriptor targetPd : targetPds) {
597612
Method writeMethod = targetPd.getWriteMethod();
598-
if (writeMethod != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
613+
if (writeMethod != null && (ignoreList == null || (!ignoreList.contains(targetPd.getName())))) {
599614
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
600615
if (sourcePd != null) {
601616
Method readMethod = sourcePd.getReadMethod();

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,6 @@ private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
363363
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
364364
ann = findAutowiredAnnotation(bridgedMethod);
365365
}
366-
else if (!method.isBridge()) {
367-
ann = findAutowiredAnnotation(method);
368-
}
369366
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
370367
if (Modifier.isStatic(method.getModifiers())) {
371368
if (logger.isWarnEnabled()) {
@@ -379,7 +376,7 @@ else if (!method.isBridge()) {
379376
}
380377
}
381378
boolean required = determineRequiredStatus(ann);
382-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
379+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
383380
currElements.add(new AutowiredMethodElement(method, required, pd));
384381
}
385382
}

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,7 @@ public TestBean getTestBean2() {
17731773
}
17741774

17751775

1776-
public static class ExtendedResourceInjectionBean<T> extends ResourceInjectionBean {
1776+
static class NonPublicResourceInjectionBean<T> extends ResourceInjectionBean {
17771777

17781778
@Autowired
17791779
public final ITestBean testBean3 = null;
@@ -1786,7 +1786,7 @@ public static class ExtendedResourceInjectionBean<T> extends ResourceInjectionBe
17861786

17871787
public boolean baseInjected = false;
17881788

1789-
public ExtendedResourceInjectionBean() {
1789+
public NonPublicResourceInjectionBean() {
17901790
}
17911791

17921792
@Override
@@ -1829,12 +1829,11 @@ public BeanFactory getBeanFactory() {
18291829
}
18301830

18311831

1832-
public static class TypedExtendedResourceInjectionBean extends ExtendedResourceInjectionBean<NestedTestBean> {
1833-
1832+
public static class TypedExtendedResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean> {
18341833
}
18351834

18361835

1837-
public static class OverriddenExtendedResourceInjectionBean extends ExtendedResourceInjectionBean<NestedTestBean> {
1836+
public static class OverriddenExtendedResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean> {
18381837

18391838
public boolean subInjected = false;
18401839

@@ -2229,7 +2228,6 @@ public static class CustomAnnotationOptionalMethodResourceInjectionBean extends
22292228

22302229
private TestBean testBean3;
22312230

2232-
22332231
@MyAutowired(optional = true)
22342232
protected void setTestBean3(TestBean testBean3) {
22352233
this.testBean3 = testBean3;

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,9 @@ public PropertyValues postProcessPropertyValues(
312312

313313

314314
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz) {
315-
// Quick check on the concurrent map first, with minimal locking.
316315
// Fall back to class name as cache key, for backwards compatibility with custom callers.
317316
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
317+
// Quick check on the concurrent map first, with minimal locking.
318318
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
319319
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
320320
synchronized (this.injectionMetadataCache) {
@@ -358,7 +358,7 @@ else if (field.isAnnotationPresent(Resource.class)) {
358358
if (method.getParameterTypes().length != 1) {
359359
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
360360
}
361-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
361+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
362362
currElements.add(new WebServiceRefElement(method, pd));
363363
}
364364
else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass)) {
@@ -368,7 +368,7 @@ else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass)) {
368368
if (method.getParameterTypes().length != 1) {
369369
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
370370
}
371-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
371+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
372372
currElements.add(new EjbRefElement(method, pd));
373373
}
374374
else if (method.isAnnotationPresent(Resource.class)) {
@@ -380,7 +380,7 @@ else if (method.isAnnotationPresent(Resource.class)) {
380380
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
381381
}
382382
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
383-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
383+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
384384
currElements.add(new ResourceElement(method, pd));
385385
}
386386
}
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-2014 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,47 +16,51 @@
1616

1717
package org.springframework.beans.factory.annotation;
1818

19-
import static org.junit.Assert.assertNotNull;
20-
2119
import javax.inject.Inject;
2220
import javax.inject.Named;
2321

2422
import org.junit.Test;
23+
2524
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2625
import org.springframework.stereotype.Component;
2726

27+
import static org.junit.Assert.*;
28+
2829
public class BridgeMethodAutowiringTests {
2930

3031
@Test
31-
public void SPR_8434() {
32+
public void SPR8434() {
3233
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
3334
ctx.register(UserServiceImpl.class, Foo.class);
3435
ctx.refresh();
3536
assertNotNull(ctx.getBean(UserServiceImpl.class).object);
3637
}
3738

38-
}
3939

40+
static abstract class GenericServiceImpl<D> {
4041

41-
abstract class GenericServiceImpl<D extends Object> {
42+
public abstract void setObject(D object);
43+
}
4244

43-
public abstract void setObject(D object);
4445

45-
}
46+
public static class UserServiceImpl extends GenericServiceImpl<Foo> {
4647

48+
protected Foo object;
4749

48-
class UserServiceImpl extends GenericServiceImpl<Foo> {
50+
@Override
51+
@Inject
52+
@Named("userObject")
53+
public void setObject(Foo object) {
54+
if (this.object != null) {
55+
throw new IllegalStateException("Already called");
56+
}
57+
this.object = object;
58+
}
59+
}
4960

50-
protected Foo object;
5161

52-
@Override
53-
@Inject
54-
@Named("userObject")
55-
public void setObject(Foo object) {
56-
this.object = object;
62+
@Component("userObject")
63+
public static class Foo {
5764
}
58-
}
59-
60-
@Component("userObject")
61-
class Foo { }
6265

66+
}

spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java

+29-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -25,11 +25,6 @@
2525
import org.junit.Test;
2626

2727
import org.springframework.beans.BeansException;
28-
import org.springframework.tests.mock.jndi.ExpectedLookupTemplate;
29-
import org.springframework.tests.sample.beans.INestedTestBean;
30-
import org.springframework.tests.sample.beans.ITestBean;
31-
import org.springframework.tests.sample.beans.NestedTestBean;
32-
import org.springframework.tests.sample.beans.TestBean;
3328
import org.springframework.beans.factory.BeanCreationException;
3429
import org.springframework.beans.factory.BeanFactory;
3530
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -42,6 +37,11 @@
4237
import org.springframework.beans.factory.support.RootBeanDefinition;
4338
import org.springframework.context.support.GenericApplicationContext;
4439
import org.springframework.jndi.support.SimpleJndiBeanFactory;
40+
import org.springframework.tests.mock.jndi.ExpectedLookupTemplate;
41+
import org.springframework.tests.sample.beans.INestedTestBean;
42+
import org.springframework.tests.sample.beans.ITestBean;
43+
import org.springframework.tests.sample.beans.NestedTestBean;
44+
import org.springframework.tests.sample.beans.TestBean;
4545
import org.springframework.util.SerializationTestUtils;
4646

4747
import static org.junit.Assert.*;
@@ -566,20 +566,20 @@ public TestBean getTestBean2() {
566566
}
567567

568568

569-
public static class ExtendedResourceInjectionBean extends ResourceInjectionBean {
569+
static class NonPublicResourceInjectionBean<B> extends ResourceInjectionBean {
570570

571571
@Resource(name="testBean4", type=TestBean.class)
572572
protected ITestBean testBean3;
573573

574-
private ITestBean testBean4;
574+
private B testBean4;
575575

576576
@Resource
577-
private INestedTestBean testBean5;
577+
INestedTestBean testBean5;
578578

579-
private INestedTestBean testBean6;
579+
INestedTestBean testBean6;
580580

581581
@Resource
582-
private BeanFactory beanFactory;
582+
BeanFactory beanFactory;
583583

584584
@Override
585585
@Resource
@@ -588,20 +588,26 @@ public void setTestBean2(TestBean testBean2) {
588588
}
589589

590590
@Resource(name="${tb}", type=ITestBean.class)
591-
private void setTestBean4(ITestBean testBean4) {
591+
private void setTestBean4(B testBean4) {
592+
if (this.testBean4 != null) {
593+
throw new IllegalStateException("Already called");
594+
}
592595
this.testBean4 = testBean4;
593596
}
594597

595598
@Resource
596599
public void setTestBean6(INestedTestBean testBean6) {
600+
if (this.testBean6 != null) {
601+
throw new IllegalStateException("Already called");
602+
}
597603
this.testBean6 = testBean6;
598604
}
599605

600606
public ITestBean getTestBean3() {
601607
return testBean3;
602608
}
603609

604-
public ITestBean getTestBean4() {
610+
public B getTestBean4() {
605611
return testBean4;
606612
}
607613

@@ -630,6 +636,10 @@ protected void destroy2() {
630636
}
631637

632638

639+
public static class ExtendedResourceInjectionBean extends NonPublicResourceInjectionBean<ITestBean> {
640+
}
641+
642+
633643
public static class ExtendedEjbInjectionBean extends ResourceInjectionBean {
634644

635645
@EJB(name="testBean4", beanInterface=TestBean.class)
@@ -653,11 +663,17 @@ public void setTestBean2(TestBean testBean2) {
653663

654664
@EJB(beanName="testBean3", beanInterface=ITestBean.class)
655665
private void setTestBean4(ITestBean testBean4) {
666+
if (this.testBean4 != null) {
667+
throw new IllegalStateException("Already called");
668+
}
656669
this.testBean4 = testBean4;
657670
}
658671

659672
@EJB
660673
public void setTestBean6(INestedTestBean testBean6) {
674+
if (this.testBean6 != null) {
675+
throw new IllegalStateException("Already called");
676+
}
661677
this.testBean6 = testBean6;
662678
}
663679

spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
5151
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
5252
import org.springframework.beans.factory.support.RootBeanDefinition;
53+
import org.springframework.core.BridgeMethodResolver;
5354
import org.springframework.core.Ordered;
5455
import org.springframework.core.PriorityOrdered;
5556
import org.springframework.jndi.JndiLocatorDelegate;
@@ -376,9 +377,9 @@ public void postProcessBeforeDestruction(Object bean, String beanName) throws Be
376377

377378

378379
private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz) {
379-
// Quick check on the concurrent map first, with minimal locking.
380380
// Fall back to class name as cache key, for backwards compatibility with custom callers.
381381
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
382+
// Quick check on the concurrent map first, with minimal locking.
382383
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
383384
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
384385
synchronized (this.injectionMetadataCache) {
@@ -390,27 +391,27 @@ private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?
390391
do {
391392
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
392393
for (Field field : targetClass.getDeclaredFields()) {
393-
PersistenceContext pc = field.getAnnotation(PersistenceContext.class);
394-
PersistenceUnit pu = field.getAnnotation(PersistenceUnit.class);
395-
if (pc != null || pu != null) {
394+
if (field.isAnnotationPresent(PersistenceContext.class) ||
395+
field.isAnnotationPresent(PersistenceUnit.class)) {
396396
if (Modifier.isStatic(field.getModifiers())) {
397397
throw new IllegalStateException("Persistence annotations are not supported on static fields");
398398
}
399399
currElements.add(new PersistenceElement(field, null));
400400
}
401401
}
402402
for (Method method : targetClass.getDeclaredMethods()) {
403-
PersistenceContext pc = method.getAnnotation(PersistenceContext.class);
404-
PersistenceUnit pu = method.getAnnotation(PersistenceUnit.class);
405-
if ((pc != null || pu != null) && !method.isBridge() &&
406-
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
403+
method = BridgeMethodResolver.findBridgedMethod(method);
404+
Method mostSpecificMethod = BridgeMethodResolver.findBridgedMethod(ClassUtils.getMostSpecificMethod(method, clazz));
405+
if ((method.isAnnotationPresent(PersistenceContext.class) ||
406+
method.isAnnotationPresent(PersistenceUnit.class)) &&
407+
method.equals(mostSpecificMethod)) {
407408
if (Modifier.isStatic(method.getModifiers())) {
408409
throw new IllegalStateException("Persistence annotations are not supported on static methods");
409410
}
410411
if (method.getParameterTypes().length != 1) {
411412
throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
412413
}
413-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
414+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
414415
currElements.add(new PersistenceElement(method, pd));
415416
}
416417
}

0 commit comments

Comments
 (0)