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:
parent
778a01943b
commit
192462902e
|
@ -380,48 +380,58 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
|
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
Class<?> targetClass = clazz;
|
Class<?> targetClass = clazz;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
|
final LinkedList<InjectionMetadata.InjectedElement> currElements =
|
||||||
for (Field field : targetClass.getDeclaredFields()) {
|
new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
AnnotationAttributes ann = findAutowiredAnnotation(field);
|
|
||||||
if (ann != null) {
|
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
@Override
|
||||||
if (logger.isWarnEnabled()) {
|
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||||
logger.warn("Autowired annotation is not supported on static fields: " + field);
|
AnnotationAttributes ann = findAutowiredAnnotation(field);
|
||||||
|
if (ann != null) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn("Autowired annotation is not supported on static fields: " + field);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
boolean required = determineRequiredStatus(ann);
|
||||||
|
currElements.add(new AutowiredFieldElement(field, required));
|
||||||
}
|
}
|
||||||
boolean required = determineRequiredStatus(ann);
|
|
||||||
currElements.add(new AutowiredFieldElement(field, required));
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
for (Method method : targetClass.getDeclaredMethods()) {
|
|
||||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
@Override
|
||||||
continue;
|
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||||
}
|
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
|
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||||
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
return;
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
}
|
||||||
if (logger.isWarnEnabled()) {
|
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
|
||||||
logger.warn("Autowired annotation is not supported on static methods: " + method);
|
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn("Autowired annotation is not supported on static methods: " + method);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
if (method.getParameterTypes().length == 0) {
|
||||||
}
|
if (logger.isWarnEnabled()) {
|
||||||
if (method.getParameterTypes().length == 0) {
|
logger.warn("Autowired annotation should be used on methods with parameters: " + method);
|
||||||
if (logger.isWarnEnabled()) {
|
}
|
||||||
logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
|
|
||||||
}
|
}
|
||||||
|
boolean required = determineRequiredStatus(ann);
|
||||||
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||||
|
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||||
}
|
}
|
||||||
boolean required = determineRequiredStatus(ann);
|
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
|
||||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
elements.addAll(0, currElements);
|
elements.addAll(0, currElements);
|
||||||
targetClass = targetClass.getSuperclass();
|
targetClass = targetClass.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2012 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -187,34 +187,39 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
|
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
|
||||||
final boolean debug = logger.isDebugEnabled();
|
final boolean debug = logger.isDebugEnabled();
|
||||||
LinkedList<LifecycleElement> initMethods = new LinkedList<LifecycleElement>();
|
LinkedList<LifecycleElement> initMethods = new LinkedList<LifecycleElement>();
|
||||||
LinkedList<LifecycleElement> destroyMethods = new LinkedList<LifecycleElement>();
|
LinkedList<LifecycleElement> destroyMethods = new LinkedList<LifecycleElement>();
|
||||||
Class<?> targetClass = clazz;
|
Class<?> targetClass = clazz;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
|
final LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
|
||||||
LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
|
final LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
|
||||||
for (Method method : targetClass.getDeclaredMethods()) {
|
|
||||||
if (this.initAnnotationType != null) {
|
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||||
if (method.getAnnotation(this.initAnnotationType) != null) {
|
@Override
|
||||||
LifecycleElement element = new LifecycleElement(method);
|
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||||
currInitMethods.add(element);
|
if (initAnnotationType != null) {
|
||||||
if (debug) {
|
if (method.getAnnotation(initAnnotationType) != null) {
|
||||||
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
|
LifecycleElement element = new LifecycleElement(method);
|
||||||
|
currInitMethods.add(element);
|
||||||
|
if (debug) {
|
||||||
|
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (destroyAnnotationType != null) {
|
||||||
|
if (method.getAnnotation(destroyAnnotationType) != null) {
|
||||||
|
currDestroyMethods.add(new LifecycleElement(method));
|
||||||
|
if (debug) {
|
||||||
|
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.destroyAnnotationType != null) {
|
});
|
||||||
if (method.getAnnotation(this.destroyAnnotationType) != null) {
|
|
||||||
currDestroyMethods.add(new LifecycleElement(method));
|
|
||||||
if (debug) {
|
|
||||||
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initMethods.addAll(0, currInitMethods);
|
initMethods.addAll(0, currInitMethods);
|
||||||
destroyMethods.addAll(currDestroyMethods);
|
destroyMethods.addAll(currDestroyMethods);
|
||||||
targetClass = targetClass.getSuperclass();
|
targetClass = targetClass.getSuperclass();
|
||||||
|
|
|
@ -189,6 +189,32 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
bf.destroySingletons();
|
bf.destroySingletons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendedResourceInjectionWithDefaultMethod() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.registerResolvableDependency(BeanFactory.class, bf);
|
||||||
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
bpp.setBeanFactory(bf);
|
||||||
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
RootBeanDefinition annotatedBd = new RootBeanDefinition(DefaultMethodResourceInjectionBean.class);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", annotatedBd);
|
||||||
|
TestBean tb = new TestBean();
|
||||||
|
bf.registerSingleton("testBean", tb);
|
||||||
|
NestedTestBean ntb = new NestedTestBean();
|
||||||
|
bf.registerSingleton("nestedTestBean", ntb);
|
||||||
|
|
||||||
|
DefaultMethodResourceInjectionBean bean = (DefaultMethodResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertSame(tb, bean.getTestBean());
|
||||||
|
assertNull(bean.getTestBean2());
|
||||||
|
assertSame(tb, bean.getTestBean3());
|
||||||
|
assertSame(tb, bean.getTestBean4());
|
||||||
|
assertSame(ntb, bean.getNestedTestBean());
|
||||||
|
assertNull(bean.getBeanFactory());
|
||||||
|
assertTrue(bean.baseInjected);
|
||||||
|
assertTrue(bean.subInjected);
|
||||||
|
bf.destroySingletons();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExtendedResourceInjectionWithAtRequired() {
|
public void testExtendedResourceInjectionWithAtRequired() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
@ -1876,6 +1902,42 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface InterfaceWithDefaultMethod {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
void setTestBean2(TestBean testBean2);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
default void injectDefault(ITestBean testBean4) {
|
||||||
|
markSubInjected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void markSubInjected();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class DefaultMethodResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean>
|
||||||
|
implements InterfaceWithDefaultMethod {
|
||||||
|
|
||||||
|
public boolean subInjected = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTestBean2(TestBean testBean2) {
|
||||||
|
super.setTestBean2(testBean2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initBeanFactory(BeanFactory beanFactory) {
|
||||||
|
this.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markSubInjected() {
|
||||||
|
subInjected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class OptionalResourceInjectionBean extends ResourceInjectionBean {
|
public static class OptionalResourceInjectionBean extends ResourceInjectionBean {
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
|
|
|
@ -66,6 +66,7 @@ import org.springframework.core.Ordered;
|
||||||
import org.springframework.jndi.support.SimpleJndiBeanFactory;
|
import org.springframework.jndi.support.SimpleJndiBeanFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,75 +340,85 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
|
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
Class<?> targetClass = clazz;
|
Class<?> targetClass = clazz;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
|
final LinkedList<InjectionMetadata.InjectedElement> currElements =
|
||||||
for (Field field : targetClass.getDeclaredFields()) {
|
new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
|
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
|
||||||
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
|
@Override
|
||||||
|
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
|
||||||
|
}
|
||||||
|
currElements.add(new WebServiceRefElement(field, field, null));
|
||||||
}
|
}
|
||||||
currElements.add(new WebServiceRefElement(field, field, null));
|
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
|
||||||
}
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
|
throw new IllegalStateException("@EJB annotation is not supported on static fields");
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
}
|
||||||
throw new IllegalStateException("@EJB annotation is not supported on static fields");
|
currElements.add(new EjbRefElement(field, field, null));
|
||||||
}
|
}
|
||||||
currElements.add(new EjbRefElement(field, field, null));
|
else if (field.isAnnotationPresent(Resource.class)) {
|
||||||
}
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
else if (field.isAnnotationPresent(Resource.class)) {
|
throw new IllegalStateException("@Resource annotation is not supported on static fields");
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
}
|
||||||
throw new IllegalStateException("@Resource annotation is not supported on static fields");
|
if (!ignoredResourceTypes.contains(field.getType().getName())) {
|
||||||
}
|
currElements.add(new ResourceElement(field, field, null));
|
||||||
if (!ignoredResourceTypes.contains(field.getType().getName())) {
|
}
|
||||||
currElements.add(new ResourceElement(field, field, null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
for (Method method : targetClass.getDeclaredMethods()) {
|
|
||||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
@Override
|
||||||
continue;
|
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||||
}
|
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||||
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
|
return;
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
|
||||||
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
|
|
||||||
}
|
|
||||||
if (method.getParameterTypes().length != 1) {
|
|
||||||
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
|
|
||||||
}
|
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
|
||||||
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
|
|
||||||
}
|
}
|
||||||
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
|
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
|
||||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
}
|
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
|
||||||
if (method.getParameterTypes().length != 1) {
|
}
|
||||||
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
|
if (method.getParameterTypes().length != 1) {
|
||||||
}
|
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
}
|
||||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
|
||||||
}
|
|
||||||
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
|
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
|
||||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
|
||||||
}
|
|
||||||
Class<?>[] paramTypes = method.getParameterTypes();
|
|
||||||
if (paramTypes.length != 1) {
|
|
||||||
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
|
||||||
}
|
|
||||||
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||||
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
|
||||||
|
}
|
||||||
|
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||||
|
}
|
||||||
|
if (method.getParameterTypes().length != 1) {
|
||||||
|
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
|
||||||
|
}
|
||||||
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||||
|
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||||
|
}
|
||||||
|
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||||
|
}
|
||||||
|
Class<?>[] paramTypes = method.getParameterTypes();
|
||||||
|
if (paramTypes.length != 1) {
|
||||||
|
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
||||||
|
}
|
||||||
|
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
||||||
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||||
|
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
elements.addAll(0, currElements);
|
elements.addAll(0, currElements);
|
||||||
targetClass = targetClass.getSuperclass();
|
targetClass = targetClass.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -288,6 +288,17 @@ class ConfigurationClassParser {
|
||||||
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
|
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process default methods on interfaces
|
||||||
|
for (SourceClass ifc : sourceClass.getInterfaces()) {
|
||||||
|
beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
|
||||||
|
for (MethodMetadata methodMetadata : beanMethods) {
|
||||||
|
if (!methodMetadata.isAbstract()) {
|
||||||
|
// A default method or other concrete method on a Java 8+ interface...
|
||||||
|
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process superclass, if any
|
// Process superclass, if any
|
||||||
if (sourceClass.getMetadata().hasSuperClass()) {
|
if (sourceClass.getMetadata().hasSuperClass()) {
|
||||||
String superclass = sourceClass.getMetadata().getSuperClassName();
|
String superclass = sourceClass.getMetadata().getSuperClassName();
|
||||||
|
@ -474,7 +485,7 @@ class ConfigurationClassParser {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
|
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
|
||||||
// process it as a @Configuration class
|
// process it as an @Configuration class
|
||||||
this.importStack.registerImport(
|
this.importStack.registerImport(
|
||||||
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
|
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
|
||||||
processConfigurationClass(candidate.asConfigClass(configClass));
|
processConfigurationClass(candidate.asConfigClass(configClass));
|
||||||
|
@ -762,6 +773,22 @@ class ConfigurationClassParser {
|
||||||
return asSourceClass(((MetadataReader) this.source).getClassMetadata().getSuperClassName());
|
return asSourceClass(((MetadataReader) this.source).getClassMetadata().getSuperClassName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<SourceClass> getInterfaces() throws IOException {
|
||||||
|
Set<SourceClass> result = new LinkedHashSet<SourceClass>();
|
||||||
|
if (this.source instanceof Class<?>) {
|
||||||
|
Class<?> sourceClass = (Class<?>) this.source;
|
||||||
|
for (Class<?> ifcClass : sourceClass.getInterfaces()) {
|
||||||
|
result.add(asSourceClass(ifcClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (String className : this.metadata.getInterfaceNames()) {
|
||||||
|
result.add(asSourceClass(className));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<SourceClass> getAnnotations() throws IOException {
|
public Set<SourceClass> getAnnotations() throws IOException {
|
||||||
Set<SourceClass> result = new LinkedHashSet<SourceClass>();
|
Set<SourceClass> result = new LinkedHashSet<SourceClass>();
|
||||||
for (String className : this.metadata.getAnnotationTypes()) {
|
for (String className : this.metadata.getAnnotationTypes()) {
|
||||||
|
|
|
@ -243,6 +243,26 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||||
assertEquals("testBean4", depBeans[0]);
|
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
|
@Test
|
||||||
public void testResourceInjectionWithTwoProcessors() {
|
public void testResourceInjectionWithTwoProcessors() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
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 {
|
public static class ExtendedEjbInjectionBean extends ResourceInjectionBean {
|
||||||
|
|
||||||
@EJB(name="testBean4", beanInterface=TestBean.class)
|
@EJB(name="testBean4", beanInterface=TestBean.class)
|
||||||
|
|
|
@ -470,6 +470,36 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
|
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||||
beanFactory.preInstantiateSingletons();
|
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
|
@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
|
@Primary
|
||||||
public static class ServiceBeanProvider {
|
public static class ServiceBeanProvider {
|
||||||
|
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -98,11 +99,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void fixedRateTask() {
|
public void fixedRateTask() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(FixedRateTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.FixedRateTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -128,6 +129,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -148,19 +150,29 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalFixedRatesWithRepeatedScheduledAnnotation() {
|
public void severalFixedRatesWithRepeatedScheduledAnnotation() {
|
||||||
BeanDefinition processorDefinition = new
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
|
||||||
SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
|
|
||||||
severalFixedRates(context, processorDefinition, targetDefinition);
|
severalFixedRates(context, processorDefinition, targetDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void severalFixedRatesWithSchedulesContainerAnnotation() {
|
public void severalFixedRatesWithSchedulesContainerAnnotation() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithSchedulesContainerAnnotationTestBean.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
severalFixedRates(context, processorDefinition, targetDefinition);
|
||||||
SeveralFixedRatesWithSchedulesContainerAnnotationTestBean.class);
|
}
|
||||||
|
|
||||||
|
@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);
|
severalFixedRates(context, processorDefinition, targetDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +182,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -201,11 +214,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
Assume.group(TestGroup.LONG_RUNNING);
|
Assume.group(TestGroup.LONG_RUNNING);
|
||||||
|
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.CronTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -229,11 +242,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
Assume.group(TestGroup.LONG_RUNNING);
|
Assume.group(TestGroup.LONG_RUNNING);
|
||||||
|
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithTimezoneTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.CronWithTimezoneTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -274,8 +287,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
Assume.group(TestGroup.LONG_RUNNING);
|
Assume.group(TestGroup.LONG_RUNNING);
|
||||||
|
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(CronWithInvalidTimezoneTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.CronWithInvalidTimezoneTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
@ -286,8 +298,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
public void cronTaskWithMethodValidation() throws InterruptedException {
|
public void cronTaskWithMethodValidation() throws InterruptedException {
|
||||||
BeanDefinition validationDefinition = new RootBeanDefinition(MethodValidationPostProcessor.class);
|
BeanDefinition validationDefinition = new RootBeanDefinition(MethodValidationPostProcessor.class);
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(CronTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.CronTestBean.class);
|
|
||||||
context.registerBeanDefinition("methodValidation", validationDefinition);
|
context.registerBeanDefinition("methodValidation", validationDefinition);
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
|
@ -301,6 +312,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -321,11 +333,11 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test
|
@Test
|
||||||
public void metaAnnotationWithCronExpression() {
|
public void metaAnnotationWithCronExpression() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(MetaAnnotationCronTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.MetaAnnotationCronTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -356,6 +368,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -386,6 +399,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -417,6 +431,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -443,12 +458,12 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
properties.setProperty("schedules.businessHours", businessHoursCronExpression);
|
properties.setProperty("schedules.businessHours", businessHoursCronExpression);
|
||||||
placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties);
|
placeholderDefinition.getPropertyValues().addPropertyValue("properties", properties);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(PropertyPlaceholderMetaAnnotationTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.PropertyPlaceholderMetaAnnotationTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("placeholder", placeholderDefinition);
|
context.registerBeanDefinition("placeholder", placeholderDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
||||||
Object postProcessor = context.getBean("postProcessor");
|
Object postProcessor = context.getBean("postProcessor");
|
||||||
Object target = context.getBean("target");
|
Object target = context.getBean("target");
|
||||||
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)
|
||||||
|
@ -469,8 +484,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
public void emptyAnnotation() {
|
public void emptyAnnotation() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(EmptyAnnotationTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.EmptyAnnotationTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
@ -479,8 +493,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
public void invalidCron() throws Throwable {
|
public void invalidCron() throws Throwable {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(InvalidCronTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.InvalidCronTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
@ -489,8 +502,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
public void nonVoidReturnType() {
|
public void nonVoidReturnType() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(NonVoidReturnTypeTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.NonVoidReturnTypeTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
@ -499,8 +511,7 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
@Test(expected = BeanCreationException.class)
|
@Test(expected = BeanCreationException.class)
|
||||||
public void nonEmptyParamList() {
|
public void nonEmptyParamList() {
|
||||||
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
|
||||||
BeanDefinition targetDefinition = new RootBeanDefinition(
|
BeanDefinition targetDefinition = new RootBeanDefinition(NonEmptyParamListTestBean.class);
|
||||||
ScheduledAnnotationBeanPostProcessorTests.NonEmptyParamListTestBean.class);
|
|
||||||
context.registerBeanDefinition("postProcessor", processorDefinition);
|
context.registerBeanDefinition("postProcessor", processorDefinition);
|
||||||
context.registerBeanDefinition("target", targetDefinition);
|
context.registerBeanDefinition("target", targetDefinition);
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
@ -542,16 +553,39 @@ public class ScheduledAnnotationBeanPostProcessorTests {
|
||||||
|
|
||||||
static class SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean {
|
static class SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean {
|
||||||
|
|
||||||
// can use Java 8 repeated @Scheduled once we have Eclipse IDE support for it
|
@Scheduled(fixedRate=4000)
|
||||||
@Schedules({
|
@Scheduled(fixedRate=4000, initialDelay=2000)
|
||||||
@Scheduled(fixedRate=4000),
|
|
||||||
@Scheduled(fixedRate=4000, initialDelay=2000)
|
|
||||||
})
|
|
||||||
public void fixedRate() {
|
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
|
@Validated
|
||||||
static class CronTestBean {
|
static class CronTestBean {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -41,6 +41,14 @@ public interface MethodMetadata extends AnnotatedTypeMetadata {
|
||||||
*/
|
*/
|
||||||
public String getDeclaringClassName();
|
public String getDeclaringClassName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the underlying method is effectively abstract:
|
||||||
|
* i.e. marked as abstract on a class or declared as a regular,
|
||||||
|
* non-default method in an interface.
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
boolean isAbstract();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the underlying method is declared as 'static'.
|
* Return whether the underlying method is declared as 'static'.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -84,6 +84,11 @@ public class StandardMethodMetadata implements MethodMetadata {
|
||||||
return this.introspectedMethod.getDeclaringClass().getName();
|
return this.introspectedMethod.getDeclaringClass().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAbstract() {
|
||||||
|
return Modifier.isAbstract(this.introspectedMethod.getModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return Modifier.isStatic(this.introspectedMethod.getModifiers());
|
return Modifier.isStatic(this.introspectedMethod.getModifiers());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -82,6 +82,11 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAbstract() {
|
||||||
|
return ((this.access & Opcodes.ACC_ABSTRACT) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isStatic() {
|
public boolean isStatic() {
|
||||||
return ((this.access & Opcodes.ACC_STATIC) != 0);
|
return ((this.access & Opcodes.ACC_STATIC) != 0);
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -25,6 +25,7 @@ import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -51,11 +52,18 @@ public abstract class ReflectionUtils {
|
||||||
private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
|
private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache for {@link Class#getDeclaredMethods()}, allowing for fast resolution.
|
* Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods
|
||||||
|
* from Java 8 based interfaces, allowing for fast iteration.
|
||||||
*/
|
*/
|
||||||
private static final Map<Class<?>, Method[]> declaredMethodsCache =
|
private static final Map<Class<?>, Method[]> declaredMethodsCache =
|
||||||
new ConcurrentReferenceHashMap<Class<?>, Method[]>(256);
|
new ConcurrentReferenceHashMap<Class<?>, Method[]>(256);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for {@link Class#getDeclaredFields()}, allowing for fast iteration.
|
||||||
|
*/
|
||||||
|
private static final Map<Class<?>, Field[]> declaredFieldsCache =
|
||||||
|
new ConcurrentReferenceHashMap<Class<?>, Field[]>(256);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
|
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
|
||||||
|
@ -82,7 +90,7 @@ public abstract class ReflectionUtils {
|
||||||
Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
|
Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
|
||||||
Class<?> searchType = clazz;
|
Class<?> searchType = clazz;
|
||||||
while (!Object.class.equals(searchType) && searchType != null) {
|
while (!Object.class.equals(searchType) && searchType != null) {
|
||||||
Field[] fields = searchType.getDeclaredFields();
|
Field[] fields = getDeclaredFields(searchType);
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
|
if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
|
||||||
return field;
|
return field;
|
||||||
|
@ -440,8 +448,8 @@ public abstract class ReflectionUtils {
|
||||||
* @see java.lang.reflect.Method#setAccessible
|
* @see java.lang.reflect.Method#setAccessible
|
||||||
*/
|
*/
|
||||||
public static void makeAccessible(Method method) {
|
public static void makeAccessible(Method method) {
|
||||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) &&
|
if ((!Modifier.isPublic(method.getModifiers()) ||
|
||||||
!method.isAccessible()) {
|
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,22 +463,43 @@ public abstract class ReflectionUtils {
|
||||||
* @see java.lang.reflect.Constructor#setAccessible
|
* @see java.lang.reflect.Constructor#setAccessible
|
||||||
*/
|
*/
|
||||||
public static void makeAccessible(Constructor<?> ctor) {
|
public static void makeAccessible(Constructor<?> ctor) {
|
||||||
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) &&
|
if ((!Modifier.isPublic(ctor.getModifiers()) ||
|
||||||
!ctor.isAccessible()) {
|
!Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) {
|
||||||
ctor.setAccessible(true);
|
ctor.setAccessible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the given callback operation on all matching methods of the given
|
||||||
|
* class, as locally declared or equivalent thereof (such as default methods
|
||||||
|
* on Java 8 based interfaces that the given class implements).
|
||||||
|
* @param clazz the class to introspect
|
||||||
|
* @param mc the callback to invoke for each method
|
||||||
|
* @since 4.2
|
||||||
|
* @see #doWithMethods
|
||||||
|
*/
|
||||||
|
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
|
||||||
|
Method[] methods = getDeclaredMethods(clazz);
|
||||||
|
for (Method method : methods) {
|
||||||
|
try {
|
||||||
|
mc.doWith(method);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the given callback operation on all matching methods of the given
|
* Perform the given callback operation on all matching methods of the given
|
||||||
* class and superclasses.
|
* class and superclasses.
|
||||||
* <p>The same named method occurring on subclass and superclass will appear
|
* <p>The same named method occurring on subclass and superclass will appear
|
||||||
* twice, unless excluded by a {@link MethodFilter}.
|
* twice, unless excluded by a {@link MethodFilter}.
|
||||||
* @param clazz class to start looking at
|
* @param clazz the class to introspect
|
||||||
* @param mc the callback to invoke for each method
|
* @param mc the callback to invoke for each method
|
||||||
* @see #doWithMethods(Class, MethodCallback, MethodFilter)
|
* @see #doWithMethods(Class, MethodCallback, MethodFilter)
|
||||||
*/
|
*/
|
||||||
public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException {
|
public static void doWithMethods(Class<?> clazz, MethodCallback mc) {
|
||||||
doWithMethods(clazz, mc, null);
|
doWithMethods(clazz, mc, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,13 +508,11 @@ public abstract class ReflectionUtils {
|
||||||
* class and superclasses (or given interface and super-interfaces).
|
* class and superclasses (or given interface and super-interfaces).
|
||||||
* <p>The same named method occurring on subclass and superclass will appear
|
* <p>The same named method occurring on subclass and superclass will appear
|
||||||
* twice, unless excluded by the specified {@link MethodFilter}.
|
* twice, unless excluded by the specified {@link MethodFilter}.
|
||||||
* @param clazz class to start looking at
|
* @param clazz the class to introspect
|
||||||
* @param mc the callback to invoke for each method
|
* @param mc the callback to invoke for each method
|
||||||
* @param mf the filter that determines the methods to apply the callback to
|
* @param mf the filter that determines the methods to apply the callback to
|
||||||
*/
|
*/
|
||||||
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf)
|
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
|
||||||
throws IllegalArgumentException {
|
|
||||||
|
|
||||||
// Keep backing up the inheritance hierarchy.
|
// Keep backing up the inheritance hierarchy.
|
||||||
Method[] methods = getDeclaredMethods(clazz);
|
Method[] methods = getDeclaredMethods(clazz);
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
|
@ -496,7 +523,7 @@ public abstract class ReflectionUtils {
|
||||||
mc.doWith(method);
|
mc.doWith(method);
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException ex) {
|
catch (IllegalAccessException ex) {
|
||||||
throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex);
|
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clazz.getSuperclass() != null) {
|
if (clazz.getSuperclass() != null) {
|
||||||
|
@ -510,10 +537,11 @@ public abstract class ReflectionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all declared methods on the leaf class and all superclasses. Leaf
|
* Get all declared methods on the leaf class and all superclasses.
|
||||||
* class methods are included first.
|
* Leaf class methods are included first.
|
||||||
|
* @param leafClass the class to introspect
|
||||||
*/
|
*/
|
||||||
public static Method[] getAllDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException {
|
public static Method[] getAllDeclaredMethods(Class<?> leafClass) {
|
||||||
final List<Method> methods = new ArrayList<Method>(32);
|
final List<Method> methods = new ArrayList<Method>(32);
|
||||||
doWithMethods(leafClass, new MethodCallback() {
|
doWithMethods(leafClass, new MethodCallback() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -525,11 +553,12 @@ public abstract class ReflectionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the unique set of declared methods on the leaf class and all superclasses. Leaf
|
* Get the unique set of declared methods on the leaf class and all superclasses.
|
||||||
* class methods are included first and while traversing the superclass hierarchy any methods found
|
* Leaf class methods are included first and while traversing the superclass hierarchy
|
||||||
* with signatures matching a method already included are filtered out.
|
* any methods found with signatures matching a method already included are filtered out.
|
||||||
|
* @param leafClass the class to introspect
|
||||||
*/
|
*/
|
||||||
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException {
|
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
|
||||||
final List<Method> methods = new ArrayList<Method>(32);
|
final List<Method> methods = new ArrayList<Method>(32);
|
||||||
doWithMethods(leafClass, new MethodCallback() {
|
doWithMethods(leafClass, new MethodCallback() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -562,25 +591,77 @@ public abstract class ReflectionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method retrieves {@link Class#getDeclaredMethods()} from a local cache
|
* This variant retrieves {@link Class#getDeclaredMethods()} from a local cache
|
||||||
* in order to avoid the JVM's SecurityManager check and defensive array copying.
|
* in order to avoid the JVM's SecurityManager check and defensive array copying.
|
||||||
|
* In addition, it also includes Java 8 default methods from locally implemented
|
||||||
|
* interfaces, since those are effectively to be treated just like declared methods.
|
||||||
|
* @param clazz the class to introspect
|
||||||
|
* @return the cached array of methods
|
||||||
|
* @see Class#getDeclaredMethods()
|
||||||
*/
|
*/
|
||||||
private static Method[] getDeclaredMethods(Class<?> clazz) {
|
private static Method[] getDeclaredMethods(Class<?> clazz) {
|
||||||
Method[] result = declaredMethodsCache.get(clazz);
|
Method[] result = declaredMethodsCache.get(clazz);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = clazz.getDeclaredMethods();
|
Method[] declaredMethods = clazz.getDeclaredMethods();
|
||||||
|
List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
|
||||||
|
if (defaultMethods != null) {
|
||||||
|
result = new Method[declaredMethods.length + defaultMethods.size()];
|
||||||
|
System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
|
||||||
|
int index = declaredMethods.length;
|
||||||
|
for (Method defaultMethod : defaultMethods) {
|
||||||
|
result[index] = defaultMethod;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = declaredMethods;
|
||||||
|
}
|
||||||
declaredMethodsCache.put(clazz, result);
|
declaredMethodsCache.put(clazz, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
|
||||||
|
List<Method> result = null;
|
||||||
|
for (Class<?> ifc : clazz.getInterfaces()) {
|
||||||
|
for (Method ifcMethod : ifc.getMethods()) {
|
||||||
|
if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
|
||||||
|
if (result == null) {
|
||||||
|
result = new LinkedList<Method>();
|
||||||
|
}
|
||||||
|
result.add(ifcMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke the given callback on all fields in the target class, going up the
|
* Invoke the given callback on all fields in the target class, going up the
|
||||||
* class hierarchy to get all declared fields.
|
* class hierarchy to get all declared fields.
|
||||||
* @param clazz the target class to analyze
|
* @param clazz the target class to analyze
|
||||||
* @param fc the callback to invoke for each field
|
* @param fc the callback to invoke for each field
|
||||||
|
* @since 4.2
|
||||||
|
* @see #doWithFields
|
||||||
*/
|
*/
|
||||||
public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException {
|
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
|
||||||
|
for (Field field : getDeclaredFields(clazz)) {
|
||||||
|
try {
|
||||||
|
fc.doWith(field);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the given callback on all fields in the target class, going up the
|
||||||
|
* class hierarchy to get all declared fields.
|
||||||
|
* @param clazz the target class to analyze
|
||||||
|
* @param fc the callback to invoke for each field
|
||||||
|
*/
|
||||||
|
public static void doWithFields(Class<?> clazz, FieldCallback fc) {
|
||||||
doWithFields(clazz, fc, null);
|
doWithFields(clazz, fc, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,15 +672,12 @@ public abstract class ReflectionUtils {
|
||||||
* @param fc the callback to invoke for each field
|
* @param fc the callback to invoke for each field
|
||||||
* @param ff the filter that determines the fields to apply the callback to
|
* @param ff the filter that determines the fields to apply the callback to
|
||||||
*/
|
*/
|
||||||
public static void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff)
|
public static void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff) {
|
||||||
throws IllegalArgumentException {
|
|
||||||
|
|
||||||
// Keep backing up the inheritance hierarchy.
|
// Keep backing up the inheritance hierarchy.
|
||||||
Class<?> targetClass = clazz;
|
Class<?> targetClass = clazz;
|
||||||
do {
|
do {
|
||||||
Field[] fields = targetClass.getDeclaredFields();
|
Field[] fields = getDeclaredFields(targetClass);
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
// Skip static and final fields.
|
|
||||||
if (ff != null && !ff.matches(field)) {
|
if (ff != null && !ff.matches(field)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -607,7 +685,7 @@ public abstract class ReflectionUtils {
|
||||||
fc.doWith(field);
|
fc.doWith(field);
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException ex) {
|
catch (IllegalAccessException ex) {
|
||||||
throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
|
throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetClass = targetClass.getSuperclass();
|
targetClass = targetClass.getSuperclass();
|
||||||
|
@ -615,13 +693,28 @@ public abstract class ReflectionUtils {
|
||||||
while (targetClass != null && targetClass != Object.class);
|
while (targetClass != null && targetClass != Object.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This variant retrieves {@link Class#getDeclaredFields()} from a local cache
|
||||||
|
* in order to avoid the JVM's SecurityManager check and defensive array copying.
|
||||||
|
* @param clazz the class to introspect
|
||||||
|
* @return the cached array of fields
|
||||||
|
* @see Class#getDeclaredFields()
|
||||||
|
*/
|
||||||
|
private static Field[] getDeclaredFields(Class<?> clazz) {
|
||||||
|
Field[] result = declaredFieldsCache.get(clazz);
|
||||||
|
if (result == null) {
|
||||||
|
result = clazz.getDeclaredFields();
|
||||||
|
declaredFieldsCache.put(clazz, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the source object and the destination, which must be the same class
|
* Given the source object and the destination, which must be the same class
|
||||||
* or a subclass, copy all fields, including inherited fields. Designed to
|
* or a subclass, copy all fields, including inherited fields. Designed to
|
||||||
* work on objects with public no-arg constructors.
|
* work on objects with public no-arg constructors.
|
||||||
* @throws IllegalArgumentException if the arguments are incompatible
|
|
||||||
*/
|
*/
|
||||||
public static void shallowCopyFieldState(final Object src, final Object dest) throws IllegalArgumentException {
|
public static void shallowCopyFieldState(final Object src, final Object dest) {
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
throw new IllegalArgumentException("Source for field copy cannot be null");
|
throw new IllegalArgumentException("Source for field copy cannot be null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2015 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -66,8 +66,8 @@ public class ReflectionUtilsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setField() {
|
public void setField() {
|
||||||
final TestObjectSubclassWithNewField testBean = new TestObjectSubclassWithNewField();
|
TestObjectSubclassWithNewField testBean = new TestObjectSubclassWithNewField();
|
||||||
final Field field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class);
|
Field field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class);
|
||||||
|
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
|
|
||||||
|
@ -79,13 +79,6 @@ public class ReflectionUtilsTests {
|
||||||
assertNull(testBean.getName());
|
assertNull(testBean.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
|
||||||
public void setFieldIllegal() {
|
|
||||||
final TestObjectSubclassWithNewField testBean = new TestObjectSubclassWithNewField();
|
|
||||||
final Field field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class);
|
|
||||||
ReflectionUtils.setField(field, testBean, "FooBar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invokeMethod() throws Exception {
|
public void invokeMethod() throws Exception {
|
||||||
String rob = "Rob Harrop";
|
String rob = "Rob Harrop";
|
||||||
|
@ -94,65 +87,50 @@ public class ReflectionUtilsTests {
|
||||||
bean.setName(rob);
|
bean.setName(rob);
|
||||||
|
|
||||||
Method getName = TestObject.class.getMethod("getName", (Class[]) null);
|
Method getName = TestObject.class.getMethod("getName", (Class[]) null);
|
||||||
Method setName = TestObject.class.getMethod("setName", new Class[] { String.class });
|
Method setName = TestObject.class.getMethod("setName", String.class);
|
||||||
|
|
||||||
Object name = ReflectionUtils.invokeMethod(getName, bean);
|
Object name = ReflectionUtils.invokeMethod(getName, bean);
|
||||||
assertEquals("Incorrect name returned", rob, name);
|
assertEquals("Incorrect name returned", rob, name);
|
||||||
|
|
||||||
String juergen = "Juergen Hoeller";
|
String juergen = "Juergen Hoeller";
|
||||||
ReflectionUtils.invokeMethod(setName, bean, new Object[] { juergen });
|
ReflectionUtils.invokeMethod(setName, bean, juergen);
|
||||||
assertEquals("Incorrect name set", juergen, bean.getName());
|
assertEquals("Incorrect name set", juergen, bean.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void declaresException() throws Exception {
|
public void declaresException() throws Exception {
|
||||||
Method remoteExMethod = A.class.getDeclaredMethod("foo", new Class[] { Integer.class });
|
Method remoteExMethod = A.class.getDeclaredMethod("foo", Integer.class);
|
||||||
assertTrue(ReflectionUtils.declaresException(remoteExMethod, RemoteException.class));
|
assertTrue(ReflectionUtils.declaresException(remoteExMethod, RemoteException.class));
|
||||||
assertTrue(ReflectionUtils.declaresException(remoteExMethod, ConnectException.class));
|
assertTrue(ReflectionUtils.declaresException(remoteExMethod, ConnectException.class));
|
||||||
assertFalse(ReflectionUtils.declaresException(remoteExMethod, NoSuchMethodException.class));
|
assertFalse(ReflectionUtils.declaresException(remoteExMethod, NoSuchMethodException.class));
|
||||||
assertFalse(ReflectionUtils.declaresException(remoteExMethod, Exception.class));
|
assertFalse(ReflectionUtils.declaresException(remoteExMethod, Exception.class));
|
||||||
|
|
||||||
Method illegalExMethod = B.class.getDeclaredMethod("bar", new Class[] { String.class });
|
Method illegalExMethod = B.class.getDeclaredMethod("bar", String.class);
|
||||||
assertTrue(ReflectionUtils.declaresException(illegalExMethod, IllegalArgumentException.class));
|
assertTrue(ReflectionUtils.declaresException(illegalExMethod, IllegalArgumentException.class));
|
||||||
assertTrue(ReflectionUtils.declaresException(illegalExMethod, NumberFormatException.class));
|
assertTrue(ReflectionUtils.declaresException(illegalExMethod, NumberFormatException.class));
|
||||||
assertFalse(ReflectionUtils.declaresException(illegalExMethod, IllegalStateException.class));
|
assertFalse(ReflectionUtils.declaresException(illegalExMethod, IllegalStateException.class));
|
||||||
assertFalse(ReflectionUtils.declaresException(illegalExMethod, Exception.class));
|
assertFalse(ReflectionUtils.declaresException(illegalExMethod, Exception.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void copySrcToDestinationOfIncorrectClass() {
|
public void copySrcToDestinationOfIncorrectClass() {
|
||||||
TestObject src = new TestObject();
|
TestObject src = new TestObject();
|
||||||
String dest = new String();
|
String dest = new String();
|
||||||
try {
|
ReflectionUtils.shallowCopyFieldState(src, dest);
|
||||||
ReflectionUtils.shallowCopyFieldState(src, dest);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
// Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void rejectsNullSrc() {
|
public void rejectsNullSrc() {
|
||||||
TestObject src = null;
|
TestObject src = null;
|
||||||
String dest = new String();
|
String dest = new String();
|
||||||
try {
|
ReflectionUtils.shallowCopyFieldState(src, dest);
|
||||||
ReflectionUtils.shallowCopyFieldState(src, dest);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
// Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void rejectsNullDest() {
|
public void rejectsNullDest() {
|
||||||
TestObject src = new TestObject();
|
TestObject src = new TestObject();
|
||||||
String dest = null;
|
String dest = null;
|
||||||
try {
|
ReflectionUtils.shallowCopyFieldState(src, dest);
|
||||||
ReflectionUtils.shallowCopyFieldState(src, dest);
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
// Ok
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -402,39 +402,49 @@ public class PersistenceAnnotationBeanPostProcessor
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private InjectionMetadata buildPersistenceMetadata(Class<?> clazz) {
|
private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
Class<?> targetClass = clazz;
|
Class<?> targetClass = clazz;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
|
final LinkedList<InjectionMetadata.InjectedElement> currElements =
|
||||||
for (Field field : targetClass.getDeclaredFields()) {
|
new LinkedList<InjectionMetadata.InjectedElement>();
|
||||||
if (field.isAnnotationPresent(PersistenceContext.class) ||
|
|
||||||
field.isAnnotationPresent(PersistenceUnit.class)) {
|
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
|
||||||
if (Modifier.isStatic(field.getModifiers())) {
|
@Override
|
||||||
throw new IllegalStateException("Persistence annotations are not supported on static fields");
|
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
if (field.isAnnotationPresent(PersistenceContext.class) ||
|
||||||
|
field.isAnnotationPresent(PersistenceUnit.class)) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
throw new IllegalStateException("Persistence annotations are not supported on static fields");
|
||||||
|
}
|
||||||
|
currElements.add(new PersistenceElement(field, field, null));
|
||||||
}
|
}
|
||||||
currElements.add(new PersistenceElement(field, field, null));
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
for (Method method : targetClass.getDeclaredMethods()) {
|
|
||||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
@Override
|
||||||
continue;
|
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||||
}
|
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) ||
|
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||||
bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) &&
|
return;
|
||||||
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
|
||||||
if (Modifier.isStatic(method.getModifiers())) {
|
|
||||||
throw new IllegalStateException("Persistence annotations are not supported on static methods");
|
|
||||||
}
|
}
|
||||||
if (method.getParameterTypes().length != 1) {
|
if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) ||
|
||||||
throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
|
bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) &&
|
||||||
|
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
throw new IllegalStateException("Persistence annotations are not supported on static methods");
|
||||||
|
}
|
||||||
|
if (method.getParameterTypes().length != 1) {
|
||||||
|
throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
|
||||||
|
}
|
||||||
|
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||||
|
currElements.add(new PersistenceElement(method, bridgedMethod, pd));
|
||||||
}
|
}
|
||||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
|
||||||
currElements.add(new PersistenceElement(method, bridgedMethod, pd));
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
elements.addAll(0, currElements);
|
elements.addAll(0, currElements);
|
||||||
targetClass = targetClass.getSuperclass();
|
targetClass = targetClass.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue