AbstractHandlerMethodMapping allows for customized bean retrieval
Issue: SPR-15535
This commit is contained in:
@@ -119,6 +119,8 @@ public abstract class BeanFactoryUtils {
|
||||
}
|
||||
|
||||
|
||||
// Retrieval of bean names
|
||||
|
||||
/**
|
||||
* Count all beans in any hierarchy in which this factory participates.
|
||||
* Includes counts of ancestor bean factories.
|
||||
@@ -126,6 +128,7 @@ public abstract class BeanFactoryUtils {
|
||||
* with the same name) are only counted once.
|
||||
* @param lbf the bean factory
|
||||
* @return count of beans including those defined in ancestor factories
|
||||
* @see #beanNamesIncludingAncestors
|
||||
*/
|
||||
public static int countBeansIncludingAncestors(ListableBeanFactory lbf) {
|
||||
return beanNamesIncludingAncestors(lbf).length;
|
||||
@@ -153,6 +156,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @param type the type that beans must match (as a {@code ResolvableType})
|
||||
* @return the array of matching bean names, or an empty array if none
|
||||
* @since 4.2
|
||||
* @see ListableBeanFactory#getBeanNamesForType(ResolvableType)
|
||||
*/
|
||||
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) {
|
||||
Assert.notNull(lbf, "ListableBeanFactory must not be null");
|
||||
@@ -179,6 +183,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @param lbf the bean factory
|
||||
* @param type the type that beans must match (as a {@code Class})
|
||||
* @return the array of matching bean names, or an empty array if none
|
||||
* @see ListableBeanFactory#getBeanNamesForType(Class)
|
||||
*/
|
||||
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type) {
|
||||
Assert.notNull(lbf, "ListableBeanFactory must not be null");
|
||||
@@ -213,6 +218,7 @@ public abstract class BeanFactoryUtils {
|
||||
* for this flag will initialize FactoryBeans and "factory-bean" references.
|
||||
* @param type the type that beans must match
|
||||
* @return the array of matching bean names, or an empty array if none
|
||||
* @see ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)
|
||||
*/
|
||||
public static String[] beanNamesForTypeIncludingAncestors(
|
||||
ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
@@ -230,6 +236,35 @@ public abstract class BeanFactoryUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bean names whose {@code Class} has the supplied {@link Annotation}
|
||||
* type, including those defined in ancestor factories, without creating any bean
|
||||
* instances yet. Will return unique names in case of overridden bean definitions.
|
||||
* @param lbf the bean factory
|
||||
* @param annotationType the type of annotation to look for
|
||||
* @return the array of matching bean names, or an empty array if none
|
||||
* @since 5.0
|
||||
* @see ListableBeanFactory#getBeanNamesForAnnotation(Class)
|
||||
*/
|
||||
public static String[] beanNamesForAnnotationIncludingAncestors(
|
||||
ListableBeanFactory lbf, Class<? extends Annotation> annotationType) {
|
||||
|
||||
Assert.notNull(lbf, "ListableBeanFactory must not be null");
|
||||
String[] result = lbf.getBeanNamesForAnnotation(annotationType);
|
||||
if (lbf instanceof HierarchicalBeanFactory) {
|
||||
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
|
||||
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
|
||||
String[] parentResult = beanNamesForAnnotationIncludingAncestors(
|
||||
(ListableBeanFactory) hbf.getParentBeanFactory(), annotationType);
|
||||
result = mergeNamesWithParent(result, parentResult, hbf);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Retrieval of bean instances
|
||||
|
||||
/**
|
||||
* Return all beans of the given type or subtypes, also picking up beans defined in
|
||||
* ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
|
||||
@@ -246,6 +281,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @param type type of bean to match
|
||||
* @return the Map of matching bean instances, or an empty Map if none
|
||||
* @throws BeansException if a bean could not be created
|
||||
* @see ListableBeanFactory#getBeansOfType(Class)
|
||||
*/
|
||||
public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
|
||||
throws BeansException {
|
||||
@@ -293,6 +329,7 @@ public abstract class BeanFactoryUtils {
|
||||
* for this flag will initialize FactoryBeans and "factory-bean" references.
|
||||
* @return the Map of matching bean instances, or an empty Map if none
|
||||
* @throws BeansException if a bean could not be created
|
||||
* @see ListableBeanFactory#getBeansOfType(Class, boolean, boolean)
|
||||
*/
|
||||
public static <T> Map<String, T> beansOfTypeIncludingAncestors(
|
||||
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
|
||||
@@ -316,7 +353,6 @@ public abstract class BeanFactoryUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a single bean of the given type or subtypes, also picking up beans
|
||||
* defined in ancestor bean factories if the current bean factory is a
|
||||
@@ -338,6 +374,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
|
||||
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
|
||||
* @throws BeansException if the bean could not be created
|
||||
* @see #beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
|
||||
*/
|
||||
public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
|
||||
throws BeansException {
|
||||
@@ -346,31 +383,6 @@ public abstract class BeanFactoryUtils {
|
||||
return uniqueBean(type, beansOfType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bean names whose {@code Class} has the supplied {@link Annotation}
|
||||
* type, including those defined in ancestor factories, without creating any bean
|
||||
* instances yet. Will return unique names in case of overridden bean definitions.
|
||||
* @param lbf the bean factory
|
||||
* @param annotationType the type of annotation to look for
|
||||
* @return the array of matching bean names, or an empty array if none
|
||||
* @since 5.0
|
||||
*/
|
||||
public static String[] beanNamesForAnnotationIncludingAncestors(
|
||||
ListableBeanFactory lbf, Class<? extends Annotation> annotationType) {
|
||||
|
||||
Assert.notNull(lbf, "ListableBeanFactory must not be null");
|
||||
String[] result = lbf.getBeanNamesForAnnotation(annotationType);
|
||||
if (lbf instanceof HierarchicalBeanFactory) {
|
||||
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
|
||||
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
|
||||
String[] parentResult = beanNamesForAnnotationIncludingAncestors(
|
||||
(ListableBeanFactory) hbf.getParentBeanFactory(), annotationType);
|
||||
result = mergeNamesWithParent(result, parentResult, hbf);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a single bean of the given type or subtypes, also picking up beans
|
||||
* defined in ancestor bean factories if the current bean factory is a
|
||||
@@ -399,6 +411,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
|
||||
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
|
||||
* @throws BeansException if the bean could not be created
|
||||
* @see #beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
|
||||
*/
|
||||
public static <T> T beanOfTypeIncludingAncestors(
|
||||
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
|
||||
@@ -423,6 +436,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
|
||||
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
|
||||
* @throws BeansException if the bean could not be created
|
||||
* @see ListableBeanFactory#getBeansOfType(Class)
|
||||
*/
|
||||
public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws BeansException {
|
||||
Assert.notNull(lbf, "ListableBeanFactory must not be null");
|
||||
@@ -453,6 +467,7 @@ public abstract class BeanFactoryUtils {
|
||||
* @throws NoSuchBeanDefinitionException if no bean of the given type was found
|
||||
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
|
||||
* @throws BeansException if the bean could not be created
|
||||
* @see ListableBeanFactory#getBeansOfType(Class, boolean, boolean)
|
||||
*/
|
||||
public static <T> T beanOfType(
|
||||
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
|
||||
|
||||
@@ -101,6 +101,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* is defined in (typically the current DispatcherServlet's context).
|
||||
* <p>Switch this flag on to detect handler beans in ancestor contexts
|
||||
* (typically the Spring root WebApplicationContext) as well.
|
||||
* @see #getCandidateBeanNames()
|
||||
*/
|
||||
public void setDetectHandlerMethodsInAncestorContexts(boolean detectHandlerMethodsInAncestorContexts) {
|
||||
this.detectHandlerMethodsInAncestorContexts = detectHandlerMethodsInAncestorContexts;
|
||||
@@ -178,7 +179,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
*/
|
||||
public void unregisterMapping(T mapping) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Unregister mapping \"" + mapping);
|
||||
logger.trace("Unregister mapping \"" + mapping + "\"");
|
||||
}
|
||||
this.mappingRegistry.unregister(mapping);
|
||||
}
|
||||
@@ -188,62 +189,78 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
/**
|
||||
* Detects handler methods at initialization.
|
||||
* @see #initHandlerMethods
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
initHandlerMethods();
|
||||
|
||||
// Total includes detected mappings + explicit registrations via registerMapping..
|
||||
int total = this.getHandlerMethods().size();
|
||||
|
||||
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
|
||||
logger.debug(total + " mappings in " + formatMappingName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan beans in the ApplicationContext, detect and register handler methods.
|
||||
* @see #isHandler(Class)
|
||||
* @see #getMappingForMethod(Method, Class)
|
||||
* @see #handlerMethodsInitialized(Map)
|
||||
* @see #getCandidateBeanNames()
|
||||
* @see #processCandidateBean
|
||||
* @see #handlerMethodsInitialized
|
||||
*/
|
||||
protected void initHandlerMethods() {
|
||||
|
||||
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
|
||||
obtainApplicationContext().getBeanNamesForType(Object.class));
|
||||
|
||||
for (String beanName : beanNames) {
|
||||
for (String beanName : getCandidateBeanNames()) {
|
||||
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
|
||||
Class<?> beanType = null;
|
||||
try {
|
||||
beanType = obtainApplicationContext().getType(beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
if (beanType != null && isHandler(beanType)) {
|
||||
detectHandlerMethods(beanName);
|
||||
}
|
||||
processCandidateBean(beanName);
|
||||
}
|
||||
}
|
||||
handlerMethodsInitialized(getHandlerMethods());
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for handler methods in a handler.
|
||||
* @param handler the bean name of a handler or a handler instance
|
||||
* Determine the names of candidate beans in the application context.
|
||||
* @since 5.1
|
||||
* @see #setDetectHandlerMethodsInAncestorContexts
|
||||
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
|
||||
*/
|
||||
protected void detectHandlerMethods(final Object handler) {
|
||||
protected String[] getCandidateBeanNames() {
|
||||
return (this.detectHandlerMethodsInAncestorContexts ?
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
|
||||
obtainApplicationContext().getBeanNamesForType(Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of the specified candidate bean and call
|
||||
* {@link #detectHandlerMethods} if identified as a handler type.
|
||||
* <p>This implementation avoids bean creation through checking
|
||||
* {@link org.springframework.beans.factory.BeanFactory#getType}
|
||||
* and calling {@link #detectHandlerMethods} with the bean name.
|
||||
* @param beanName the name of the candidate bean
|
||||
* @since 5.1
|
||||
* @see #isHandler
|
||||
* @see #detectHandlerMethods
|
||||
*/
|
||||
protected void processCandidateBean(String beanName) {
|
||||
Class<?> beanType = null;
|
||||
try {
|
||||
beanType = obtainApplicationContext().getType(beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
if (beanType != null && isHandler(beanType)) {
|
||||
detectHandlerMethods(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for handler methods in the specified handler bean.
|
||||
* @param handler either a bean name or an actual handler instance
|
||||
* @see #getMappingForMethod
|
||||
*/
|
||||
protected void detectHandlerMethods(Object handler) {
|
||||
Class<?> handlerType = (handler instanceof String ?
|
||||
obtainApplicationContext().getType((String) handler) : handler.getClass());
|
||||
|
||||
if (handlerType != null) {
|
||||
final Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
|
||||
(MethodIntrospector.MetadataLookup<T>) method -> {
|
||||
try {
|
||||
@@ -309,6 +326,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* @param handlerMethods a read-only map with handler methods and mappings.
|
||||
*/
|
||||
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
|
||||
// Total includes detected mappings + explicit registrations via registerMapping
|
||||
int total = handlerMethods.size();
|
||||
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
|
||||
logger.debug(total + " mappings in " + formatMappingName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -476,7 +498,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
/**
|
||||
* A registry that maintains all mappings to handler methods, exposing methods
|
||||
* to perform lookups and providing concurrent access.
|
||||
*
|
||||
* <p>Package-private for testing purposes.
|
||||
*/
|
||||
class MappingRegistry {
|
||||
@@ -544,7 +565,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
try {
|
||||
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
||||
assertUniqueMethodMapping(handlerMethod, mapping);
|
||||
|
||||
this.mappingLookup.put(mapping, handlerMethod);
|
||||
|
||||
List<String> directUrls = getDirectUrls(mapping);
|
||||
|
||||
Reference in New Issue
Block a user