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
This commit is contained in:
Juergen Hoeller
2015-03-19 16:50:15 +01:00
parent 778a01943b
commit 192462902e
14 changed files with 605 additions and 240 deletions

View File

@@ -243,6 +243,26 @@ public class CommonAnnotationBeanPostProcessorTests {
assertEquals("testBean4", depBeans[0]);
}
@Test
public void testResourceInjectionWithDefaultMethod() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(DefaultMethodResourceInjectionBean.class));
TestBean tb2 = new TestBean();
bf.registerSingleton("testBean2", tb2);
NestedTestBean tb7 = new NestedTestBean();
bf.registerSingleton("testBean7", tb7);
DefaultMethodResourceInjectionBean bean = (DefaultMethodResourceInjectionBean) bf.getBean("annotatedBean");
assertSame(tb2, bean.getTestBean2());
assertSame(2, bean.counter);
bf.destroySingletons();
assertSame(3, bean.counter);
}
@Test
public void testResourceInjectionWithTwoProcessors() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -694,6 +714,42 @@ public class CommonAnnotationBeanPostProcessorTests {
}
public interface InterfaceWithDefaultMethod {
@Resource
void setTestBean2(TestBean testBean2);
@Resource
default void setTestBean7(INestedTestBean testBean7) {
increaseCounter();
}
@PostConstruct
default void initDefault() {
increaseCounter();
}
@PreDestroy
default void destroyDefault() {
increaseCounter();
}
void increaseCounter();
}
public static class DefaultMethodResourceInjectionBean extends ResourceInjectionBean
implements InterfaceWithDefaultMethod {
public int counter = 0;
@Override
public void increaseCounter() {
counter++;
}
}
public static class ExtendedEjbInjectionBean extends ResourceInjectionBean {
@EJB(name="testBean4", beanInterface=TestBean.class)

View File

@@ -470,6 +470,36 @@ public class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
public void testConfigWithDefaultMethods() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
public void testConfigWithDefaultMethodsUsingAsm() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName()));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName()));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
@@ -944,6 +974,37 @@ public class ConfigurationClassPostProcessorTests {
}
}
public interface DefaultMethodsConfig {
@Bean
default ServiceBean serviceBean() {
return provider().getServiceBean();
}
@Bean
default ServiceBeanProvider provider() {
return new ServiceBeanProvider();
}
}
@Configuration
public static class ConcreteConfigWithDefaultMethods implements DefaultMethodsConfig {
@Autowired
private ServiceBeanProvider provider;
@Bean
@Override
public ServiceBeanProvider provider() {
return provider;
}
@PostConstruct
public void validate() {
Assert.notNull(provider);
}
}
@Primary
public static class ServiceBeanProvider {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -77,6 +77,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -98,11 +99,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test
public void fixedRateTask() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.FixedRateTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -128,6 +129,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -148,19 +150,29 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test
public void severalFixedRatesWithRepeatedScheduledAnnotation() {
BeanDefinition processorDefinition = new
RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesWithSchedulesContainerAnnotation() {
BeanDefinition processorDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
SeveralFixedRatesWithSchedulesContainerAnnotationTestBean.class);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithSchedulesContainerAnnotationTestBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesOnBaseClass() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRatesSubBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesOnDefaultMethod() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRatesDefaultBean.class);
severalFixedRates(context, processorDefinition, targetDefinition);
}
@@ -170,6 +182,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -201,11 +214,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Assume.group(TestGroup.LONG_RUNNING);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.CronTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -229,11 +242,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Assume.group(TestGroup.LONG_RUNNING);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.CronWithTimezoneTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithTimezoneTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -274,8 +287,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Assume.group(TestGroup.LONG_RUNNING);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.CronWithInvalidTimezoneTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithInvalidTimezoneTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@@ -286,8 +298,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
public void cronTaskWithMethodValidation() throws InterruptedException {
BeanDefinition validationDefinition = new RootBeanDefinition(MethodValidationPostProcessor.class);
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.CronTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
context.registerBeanDefinition("methodValidation", validationDefinition);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
@@ -301,6 +312,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -321,11 +333,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test
public void metaAnnotationWithCronExpression() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.MetaAnnotationCronTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(MetaAnnotationCronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -356,6 +368,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -386,6 +399,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -417,6 +431,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -443,12 +458,12 @@ public class ScheduledAnnotationBeanPostProcessorTests {
Properties properties = new Properties();
properties.setProperty("schedules.businessHours", businessHoursCronExpression);
placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.PropertyPlaceholderMetaAnnotationTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderMetaAnnotationTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("placeholder", placeholderDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
Object postProcessor = context.getBean("postProcessor");
Object target = context.getBean("target");
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
@@ -469,8 +484,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test(expected = BeanCreationException.class)
public void emptyAnnotation() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.EmptyAnnotationTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(EmptyAnnotationTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@@ -479,8 +493,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test(expected = BeanCreationException.class)
public void invalidCron() throws Throwable {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.InvalidCronTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(InvalidCronTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@@ -489,8 +502,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test(expected = BeanCreationException.class)
public void nonVoidReturnType() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.NonVoidReturnTypeTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(NonVoidReturnTypeTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@@ -499,8 +511,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Test(expected = BeanCreationException.class)
public void nonEmptyParamList() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(
ScheduledAnnotationBeanPostProcessorTests.NonEmptyParamListTestBean.class);
BeanDefinition targetDefinition = new RootBeanDefinition(NonEmptyParamListTestBean.class);
context.registerBeanDefinition("postProcessor", processorDefinition);
context.registerBeanDefinition("target", targetDefinition);
context.refresh();
@@ -542,16 +553,39 @@ public class ScheduledAnnotationBeanPostProcessorTests {
static class SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean {
// can use Java 8 repeated @Scheduled once we have Eclipse IDE support for it
@Schedules({
@Scheduled(fixedRate=4000),
@Scheduled(fixedRate=4000, initialDelay=2000)
})
@Scheduled(fixedRate=4000)
@Scheduled(fixedRate=4000, initialDelay=2000)
public void fixedRate() {
}
}
static class FixedRatesBaseBean {
@Scheduled(fixedRate=4000)
@Scheduled(fixedRate=4000, initialDelay=2000)
public void fixedRate() {
}
}
static class FixedRatesSubBean extends FixedRatesBaseBean {
}
static interface FixedRatesDefaultMethod {
@Scheduled(fixedRate=4000)
@Scheduled(fixedRate=4000, initialDelay=2000)
default void fixedRate() {
}
}
static class FixedRatesDefaultBean implements FixedRatesDefaultMethod {
}
@Validated
static class CronTestBean {