Refactor BeanInfoFactory
This commit refactors the BeanInfoFactory so that: - supports() and getBeanInfo() are folded into one, so that getBeanInfo() returns null if a given class is not supported. - CachedIntrospectionResults now uses SpringFactoriesLoader
This commit is contained in:
parent
aeff91c1da
commit
82739dd4ac
|
@ -23,17 +23,18 @@ import java.beans.IntrospectionException;
|
||||||
* Strategy for creating {@link BeanInfo} instances.
|
* Strategy for creating {@link BeanInfo} instances.
|
||||||
*
|
*
|
||||||
* <p>BeanInfoFactories are are instantiated by the {@link CachedIntrospectionResults},
|
* <p>BeanInfoFactories are are instantiated by the {@link CachedIntrospectionResults},
|
||||||
* which looks for {@code META-INF/spring.beanInfoFactories} files on the class path.
|
* by using the {@link org.springframework.core.io.support.SpringFactoriesLoader} utility
|
||||||
* These files contain one or more {@code BeanInfoFactory} class names, each of a single
|
* class.
|
||||||
* line. When a {@link BeanInfo} is to be created, the {@code CachedIntrospectionResults}
|
|
||||||
* will iterate through the discovered factories, asking each one if it {@linkplain
|
|
||||||
* #supports(Class) supports} the given bean class. If it does, {@link
|
|
||||||
* #getBeanInfo(Class)} will be called; if not, the next factory will be queried. If none
|
|
||||||
* of the factories support the class, an standard {@link BeanInfo} is created as a
|
|
||||||
* default.
|
|
||||||
*
|
*
|
||||||
* <p>Note that the {@link CachedIntrospectionResults} sorts the {@code BeanInfoFactory}
|
* When a {@link BeanInfo} is to be created, the {@code CachedIntrospectionResults}
|
||||||
* instances by {@link org.springframework.core.annotation.Order Order}, so that ones with
|
* will iterate through the discovered factories, calling {@link
|
||||||
|
* #getBeanInfo(Class)} on each one. If {@code null} is returned, the next factory will
|
||||||
|
* be queried. If none of the factories support the class, an standard {@link BeanInfo}
|
||||||
|
* is created as a default.
|
||||||
|
*
|
||||||
|
* <p>Note that the {@link org.springframework.core.io.support.SpringFactoriesLoader}
|
||||||
|
* sorts the {@code BeanInfoFactory} instances by
|
||||||
|
* {@link org.springframework.core.annotation.Order @Order}, so that ones with
|
||||||
* a higher precedence come first.
|
* a higher precedence come first.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
@ -42,18 +43,10 @@ import java.beans.IntrospectionException;
|
||||||
public interface BeanInfoFactory {
|
public interface BeanInfoFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether a bean with the given class is supported by this factory.
|
* Returns the bean info for the given class, if supported.
|
||||||
*
|
*
|
||||||
* @param beanClass the bean class
|
* @param beanClass the bean class
|
||||||
* @return {@code true} if supported; {@code false} otherwise
|
* @return the bean info, or {@code null} if not the given class is not supported
|
||||||
*/
|
|
||||||
boolean supports(Class<?> beanClass);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the bean info for the given class.
|
|
||||||
*
|
|
||||||
* @param beanClass the bean class
|
|
||||||
* @return the bean info
|
|
||||||
* @throws IntrospectionException in case of exceptions
|
* @throws IntrospectionException in case of exceptions
|
||||||
*/
|
*/
|
||||||
BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException;
|
BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException;
|
||||||
|
|
|
@ -20,25 +20,21 @@ import java.beans.BeanInfo;
|
||||||
import java.beans.IntrospectionException;
|
import java.beans.IntrospectionException;
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@ -64,12 +60,6 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class CachedIntrospectionResults {
|
public class CachedIntrospectionResults {
|
||||||
|
|
||||||
/**
|
|
||||||
* The location to look for the bean info mapping files. Can be present in multiple JAR files.
|
|
||||||
*/
|
|
||||||
public static final String BEAN_INFO_FACTORIES_LOCATION = "META-INF/spring.beanInfoFactories";
|
|
||||||
|
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class);
|
private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,8 +232,8 @@ public class CachedIntrospectionResults {
|
||||||
BeanInfo beanInfo = null;
|
BeanInfo beanInfo = null;
|
||||||
List<BeanInfoFactory> beanInfoFactories = getBeanInfoFactories(beanClass.getClassLoader());
|
List<BeanInfoFactory> beanInfoFactories = getBeanInfoFactories(beanClass.getClassLoader());
|
||||||
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
|
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
|
||||||
if (beanInfoFactory.supports(beanClass)) {
|
|
||||||
beanInfo = beanInfoFactory.getBeanInfo(beanClass);
|
beanInfo = beanInfoFactory.getBeanInfo(beanClass);
|
||||||
|
if (beanInfo != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,57 +329,12 @@ public class CachedIntrospectionResults {
|
||||||
if (beanInfoFactories == null) {
|
if (beanInfoFactories == null) {
|
||||||
synchronized (beanInfoFactoriesMutex) {
|
synchronized (beanInfoFactoriesMutex) {
|
||||||
if (beanInfoFactories == null) {
|
if (beanInfoFactories == null) {
|
||||||
try {
|
beanInfoFactories = Collections.synchronizedList(SpringFactoriesLoader
|
||||||
Properties properties =
|
.loadFactories(BeanInfoFactory.class, classLoader));
|
||||||
PropertiesLoaderUtils.loadAllProperties(
|
|
||||||
BEAN_INFO_FACTORIES_LOCATION, classLoader);
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Loaded BeanInfoFactories: " + properties.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<BeanInfoFactory> factories = new ArrayList<BeanInfoFactory>(properties.size());
|
|
||||||
|
|
||||||
for (Object key : properties.keySet()) {
|
|
||||||
if (key instanceof String) {
|
|
||||||
String className = (String) key;
|
|
||||||
BeanInfoFactory factory = instantiateBeanInfoFactory(className, classLoader);
|
|
||||||
factories.add(factory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(factories, new AnnotationAwareOrderComparator());
|
|
||||||
|
|
||||||
beanInfoFactories = Collections.synchronizedList(factories);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unable to load BeanInfoFactories from location [" + BEAN_INFO_FACTORIES_LOCATION + "]", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return beanInfoFactories;
|
return beanInfoFactories;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BeanInfoFactory instantiateBeanInfoFactory(String className,
|
|
||||||
ClassLoader classLoader) {
|
|
||||||
try {
|
|
||||||
Class<?> factoryClass = ClassUtils.forName(className, classLoader);
|
|
||||||
if (!BeanInfoFactory.class.isAssignableFrom(factoryClass)) {
|
|
||||||
throw new FatalBeanException(
|
|
||||||
"Class [" + className + "] does not implement the [" +
|
|
||||||
BeanInfoFactory.class.getName() + "] interface");
|
|
||||||
}
|
|
||||||
return (BeanInfoFactory) BeanUtils.instantiate(factoryClass);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
throw new FatalBeanException(
|
|
||||||
"BeanInfoFactory class [" + className + "] not found", ex);
|
|
||||||
}
|
|
||||||
catch (LinkageError err) {
|
|
||||||
throw new FatalBeanException("Invalid BeanInfoFactory class [" + className +
|
|
||||||
"]: problem with handler class file or dependent class", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.beans;
|
||||||
import java.beans.BeanInfo;
|
import java.beans.BeanInfo;
|
||||||
import java.beans.IntrospectionException;
|
import java.beans.IntrospectionException;
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
@ -37,13 +36,21 @@ import org.springframework.core.Ordered;
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
* @see BeanInfoFactory
|
* @see BeanInfoFactory
|
||||||
*/
|
*/
|
||||||
class ExtendedBeanInfoFactory implements Ordered, BeanInfoFactory {
|
public class ExtendedBeanInfoFactory implements Ordered, BeanInfoFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link ExtendedBeanInfo} for the given bean class.
|
||||||
|
*/
|
||||||
|
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
|
||||||
|
return supports(beanClass) ?
|
||||||
|
new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the given bean class declares or inherits any non-void returning
|
* Return whether the given bean class declares or inherits any non-void returning
|
||||||
* JavaBeans or <em>indexed property</em> setter methods.
|
* JavaBeans or <em>indexed property</em> setter methods.
|
||||||
*/
|
*/
|
||||||
public boolean supports(Class<?> beanClass) {
|
private boolean supports(Class<?> beanClass) {
|
||||||
for (Method method : beanClass.getMethods()) {
|
for (Method method : beanClass.getMethods()) {
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
|
@ -59,13 +66,6 @@ class ExtendedBeanInfoFactory implements Ordered, BeanInfoFactory {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a new {@link ExtendedBeanInfo} for the given bean class.
|
|
||||||
*/
|
|
||||||
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
|
|
||||||
return new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return Ordered.LOWEST_PRECEDENCE;
|
return Ordered.LOWEST_PRECEDENCE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
org.springframework.beans.ExtendedBeanInfoFactory
|
|
|
@ -0,0 +1 @@
|
||||||
|
org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory
|
|
@ -18,11 +18,11 @@ package org.springframework.beans;
|
||||||
|
|
||||||
import java.beans.IntrospectionException;
|
import java.beans.IntrospectionException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link ExtendedBeanInfoTests}.
|
* Unit tests for {@link ExtendedBeanInfoTests}.
|
||||||
*
|
*
|
||||||
|
@ -38,7 +38,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
public void setFoo(String s) { }
|
public void setFoo(String s) { }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(false));
|
assertThat(factory.getBeanInfo(C.class), nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -47,7 +47,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
public C setFoo(String s) { return this; }
|
public C setFoo(String s) { return this; }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(true));
|
assertThat(factory.getBeanInfo(C.class), notNullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -56,7 +56,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
public C setFoo(int i, String s) { return this; }
|
public C setFoo(int i, String s) { return this; }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(true));
|
assertThat(factory.getBeanInfo(C.class), notNullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -65,7 +65,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
void setBar(String s) { }
|
void setBar(String s) { }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(false));
|
assertThat(factory.getBeanInfo(C.class), nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -74,7 +74,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
C setBar() { return this; }
|
C setBar() { return this; }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(false));
|
assertThat(factory.getBeanInfo(C.class), nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -83,7 +83,7 @@ public class ExtendedBeanInfoFactoryTests {
|
||||||
class C {
|
class C {
|
||||||
C set(String s) { return this; }
|
C set(String s) { return this; }
|
||||||
}
|
}
|
||||||
assertThat(factory.supports(C.class), is(false));
|
assertThat(factory.getBeanInfo(C.class), nullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue