Skip to content

Commit 1924629

Browse files
committed
Consistent support for Java 8 default methods (in interfaces implemented by user classes)
Covers ReflectionUtils.doWithMethods as well as affected annotation post-processors. Includes an extension of MethodMetadata for the detection of @bean default methods. Issue: SPR-12822 Issue: SPR-10919
1 parent 778a019 commit 1924629

File tree

14 files changed

+601
-236
lines changed

14 files changed

+601
-236
lines changed

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

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -380,48 +380,58 @@ private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz
380380
return metadata;
381381
}
382382

383-
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
383+
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
384384
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
385385
Class<?> targetClass = clazz;
386386

387387
do {
388-
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
389-
for (Field field : targetClass.getDeclaredFields()) {
390-
AnnotationAttributes ann = findAutowiredAnnotation(field);
391-
if (ann != null) {
392-
if (Modifier.isStatic(field.getModifiers())) {
393-
if (logger.isWarnEnabled()) {
394-
logger.warn("Autowired annotation is not supported on static fields: " + field);
388+
final LinkedList<InjectionMetadata.InjectedElement> currElements =
389+
new LinkedList<InjectionMetadata.InjectedElement>();
390+
391+
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
392+
@Override
393+
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
394+
AnnotationAttributes ann = findAutowiredAnnotation(field);
395+
if (ann != null) {
396+
if (Modifier.isStatic(field.getModifiers())) {
397+
if (logger.isWarnEnabled()) {
398+
logger.warn("Autowired annotation is not supported on static fields: " + field);
399+
}
400+
return;
395401
}
396-
continue;
402+
boolean required = determineRequiredStatus(ann);
403+
currElements.add(new AutowiredFieldElement(field, required));
397404
}
398-
boolean required = determineRequiredStatus(ann);
399-
currElements.add(new AutowiredFieldElement(field, required));
400-
}
401-
}
402-
for (Method method : targetClass.getDeclaredMethods()) {
403-
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
404-
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
405-
continue;
406405
}
407-
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
408-
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
409-
if (Modifier.isStatic(method.getModifiers())) {
410-
if (logger.isWarnEnabled()) {
411-
logger.warn("Autowired annotation is not supported on static methods: " + method);
412-
}
413-
continue;
406+
});
407+
408+
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
409+
@Override
410+
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
411+
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
412+
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
413+
return;
414414
}
415-
if (method.getParameterTypes().length == 0) {
416-
if (logger.isWarnEnabled()) {
417-
logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
415+
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
416+
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
417+
if (Modifier.isStatic(method.getModifiers())) {
418+
if (logger.isWarnEnabled()) {
419+
logger.warn("Autowired annotation is not supported on static methods: " + method);
420+
}
421+
return;
422+
}
423+
if (method.getParameterTypes().length == 0) {
424+
if (logger.isWarnEnabled()) {
425+
logger.warn("Autowired annotation should be used on methods with parameters: " + method);
426+
}
418427
}
428+
boolean required = determineRequiredStatus(ann);
429+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
430+
currElements.add(new AutowiredMethodElement(method, required, pd));
419431
}
420-
boolean required = determineRequiredStatus(ann);
421-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
422-
currElements.add(new AutowiredMethodElement(method, required, pd));
423432
}
424-
}
433+
});
434+
425435
elements.addAll(0, currElements);
426436
targetClass = targetClass.getSuperclass();
427437
}

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

Lines changed: 23 additions & 18 deletions
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-2015 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.
@@ -187,34 +187,39 @@ private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
187187
return metadata;
188188
}
189189

190-
private LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
190+
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
191191
final boolean debug = logger.isDebugEnabled();
192192
LinkedList<LifecycleElement> initMethods = new LinkedList<LifecycleElement>();
193193
LinkedList<LifecycleElement> destroyMethods = new LinkedList<LifecycleElement>();
194194
Class<?> targetClass = clazz;
195195

196196
do {
197-
LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
198-
LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
199-
for (Method method : targetClass.getDeclaredMethods()) {
200-
if (this.initAnnotationType != null) {
201-
if (method.getAnnotation(this.initAnnotationType) != null) {
202-
LifecycleElement element = new LifecycleElement(method);
203-
currInitMethods.add(element);
204-
if (debug) {
205-
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
197+
final LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
198+
final LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
199+
200+
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
201+
@Override
202+
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
203+
if (initAnnotationType != null) {
204+
if (method.getAnnotation(initAnnotationType) != null) {
205+
LifecycleElement element = new LifecycleElement(method);
206+
currInitMethods.add(element);
207+
if (debug) {
208+
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
209+
}
206210
}
207211
}
208-
}
209-
if (this.destroyAnnotationType != null) {
210-
if (method.getAnnotation(this.destroyAnnotationType) != null) {
211-
currDestroyMethods.add(new LifecycleElement(method));
212-
if (debug) {
213-
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
212+
if (destroyAnnotationType != null) {
213+
if (method.getAnnotation(destroyAnnotationType) != null) {
214+
currDestroyMethods.add(new LifecycleElement(method));
215+
if (debug) {
216+
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
217+
}
214218
}
215219
}
216220
}
217-
}
221+
});
222+
218223
initMethods.addAll(0, currInitMethods);
219224
destroyMethods.addAll(currDestroyMethods);
220225
targetClass = targetClass.getSuperclass();

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,32 @@ public void testExtendedResourceInjectionWithSkippedOverriddenMethods() {
189189
bf.destroySingletons();
190190
}
191191

192+
@Test
193+
public void testExtendedResourceInjectionWithDefaultMethod() {
194+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
195+
bf.registerResolvableDependency(BeanFactory.class, bf);
196+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
197+
bpp.setBeanFactory(bf);
198+
bf.addBeanPostProcessor(bpp);
199+
RootBeanDefinition annotatedBd = new RootBeanDefinition(DefaultMethodResourceInjectionBean.class);
200+
bf.registerBeanDefinition("annotatedBean", annotatedBd);
201+
TestBean tb = new TestBean();
202+
bf.registerSingleton("testBean", tb);
203+
NestedTestBean ntb = new NestedTestBean();
204+
bf.registerSingleton("nestedTestBean", ntb);
205+
206+
DefaultMethodResourceInjectionBean bean = (DefaultMethodResourceInjectionBean) bf.getBean("annotatedBean");
207+
assertSame(tb, bean.getTestBean());
208+
assertNull(bean.getTestBean2());
209+
assertSame(tb, bean.getTestBean3());
210+
assertSame(tb, bean.getTestBean4());
211+
assertSame(ntb, bean.getNestedTestBean());
212+
assertNull(bean.getBeanFactory());
213+
assertTrue(bean.baseInjected);
214+
assertTrue(bean.subInjected);
215+
bf.destroySingletons();
216+
}
217+
192218
@Test
193219
public void testExtendedResourceInjectionWithAtRequired() {
194220
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1876,6 +1902,42 @@ private void inject(ITestBean testBean4) {
18761902
}
18771903

18781904

1905+
public interface InterfaceWithDefaultMethod {
1906+
1907+
@Autowired
1908+
void setTestBean2(TestBean testBean2);
1909+
1910+
@Autowired
1911+
default void injectDefault(ITestBean testBean4) {
1912+
markSubInjected();
1913+
}
1914+
1915+
void markSubInjected();
1916+
}
1917+
1918+
1919+
public static class DefaultMethodResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean>
1920+
implements InterfaceWithDefaultMethod {
1921+
1922+
public boolean subInjected = false;
1923+
1924+
@Override
1925+
public void setTestBean2(TestBean testBean2) {
1926+
super.setTestBean2(testBean2);
1927+
}
1928+
1929+
@Override
1930+
protected void initBeanFactory(BeanFactory beanFactory) {
1931+
this.beanFactory = beanFactory;
1932+
}
1933+
1934+
@Override
1935+
public void markSubInjected() {
1936+
subInjected = true;
1937+
}
1938+
}
1939+
1940+
18791941
public static class OptionalResourceInjectionBean extends ResourceInjectionBean {
18801942

18811943
@Autowired(required = false)

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

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.springframework.jndi.support.SimpleJndiBeanFactory;
6767
import org.springframework.util.Assert;
6868
import org.springframework.util.ClassUtils;
69+
import org.springframework.util.ReflectionUtils;
6970
import org.springframework.util.StringUtils;
7071

7172
/**
@@ -339,75 +340,85 @@ private InjectionMetadata findResourceMetadata(String beanName, final Class<?> c
339340
return metadata;
340341
}
341342

342-
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
343+
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
343344
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
344345
Class<?> targetClass = clazz;
345346

346347
do {
347-
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
348-
for (Field field : targetClass.getDeclaredFields()) {
349-
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
350-
if (Modifier.isStatic(field.getModifiers())) {
351-
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
352-
}
353-
currElements.add(new WebServiceRefElement(field, field, null));
354-
}
355-
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
356-
if (Modifier.isStatic(field.getModifiers())) {
357-
throw new IllegalStateException("@EJB annotation is not supported on static fields");
358-
}
359-
currElements.add(new EjbRefElement(field, field, null));
360-
}
361-
else if (field.isAnnotationPresent(Resource.class)) {
362-
if (Modifier.isStatic(field.getModifiers())) {
363-
throw new IllegalStateException("@Resource annotation is not supported on static fields");
364-
}
365-
if (!ignoredResourceTypes.contains(field.getType().getName())) {
366-
currElements.add(new ResourceElement(field, field, null));
367-
}
368-
}
369-
}
370-
for (Method method : targetClass.getDeclaredMethods()) {
371-
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
372-
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
373-
continue;
374-
}
375-
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
376-
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
377-
if (Modifier.isStatic(method.getModifiers())) {
378-
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
348+
final LinkedList<InjectionMetadata.InjectedElement> currElements =
349+
new LinkedList<InjectionMetadata.InjectedElement>();
350+
351+
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
352+
@Override
353+
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
354+
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
355+
if (Modifier.isStatic(field.getModifiers())) {
356+
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
379357
}
380-
if (method.getParameterTypes().length != 1) {
381-
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
358+
currElements.add(new WebServiceRefElement(field, field, null));
359+
}
360+
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
361+
if (Modifier.isStatic(field.getModifiers())) {
362+
throw new IllegalStateException("@EJB annotation is not supported on static fields");
382363
}
383-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
384-
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
364+
currElements.add(new EjbRefElement(field, field, null));
385365
}
386-
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
387-
if (Modifier.isStatic(method.getModifiers())) {
388-
throw new IllegalStateException("@EJB annotation is not supported on static methods");
366+
else if (field.isAnnotationPresent(Resource.class)) {
367+
if (Modifier.isStatic(field.getModifiers())) {
368+
throw new IllegalStateException("@Resource annotation is not supported on static fields");
389369
}
390-
if (method.getParameterTypes().length != 1) {
391-
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
370+
if (!ignoredResourceTypes.contains(field.getType().getName())) {
371+
currElements.add(new ResourceElement(field, field, null));
392372
}
393-
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
394-
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
395373
}
396-
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
397-
if (Modifier.isStatic(method.getModifiers())) {
398-
throw new IllegalStateException("@Resource annotation is not supported on static methods");
399-
}
400-
Class<?>[] paramTypes = method.getParameterTypes();
401-
if (paramTypes.length != 1) {
402-
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
374+
}
375+
});
376+
377+
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
378+
@Override
379+
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
380+
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
381+
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
382+
return;
383+
}
384+
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
385+
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
386+
if (Modifier.isStatic(method.getModifiers())) {
387+
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
388+
}
389+
if (method.getParameterTypes().length != 1) {
390+
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
391+
}
392+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
393+
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
403394
}
404-
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
395+
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
396+
if (Modifier.isStatic(method.getModifiers())) {
397+
throw new IllegalStateException("@EJB annotation is not supported on static methods");
398+
}
399+
if (method.getParameterTypes().length != 1) {
400+
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
401+
}
405402
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
406-
currElements.add(new ResourceElement(method, bridgedMethod, pd));
403+
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
404+
}
405+
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
406+
if (Modifier.isStatic(method.getModifiers())) {
407+
throw new IllegalStateException("@Resource annotation is not supported on static methods");
408+
}
409+
Class<?>[] paramTypes = method.getParameterTypes();
410+
if (paramTypes.length != 1) {
411+
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
412+
}
413+
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
414+
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
415+
currElements.add(new ResourceElement(method, bridgedMethod, pd));
416+
}
407417
}
408418
}
409419
}
410-
}
420+
});
421+
411422
elements.addAll(0, currElements);
412423
targetClass = targetClass.getSuperclass();
413424
}

0 commit comments

Comments
 (0)