Add new features on @ControllerAdvice
Prior to this commit, @ControllerAdvice annotated beans would assist all known Controllers, by applying @ExceptionHandler, @InitBinder, and @ModelAttribute. This commit updates the @ControllerAdvice annotation, which accepts now base package names, assignableTypes, annotations and basePackageClasses. If attributes are set, only Controllers that match those selectors will be assisted by the annotated class. This commit does not change the default behavior when no value is set, i.e. @ControllerAdvice(). Issue: SPR-10222
This commit is contained in:
parent
be4e5d2841
commit
c4a8bf9c4d
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -34,7 +34,21 @@ import org.springframework.stereotype.Component;
|
|||
* {@link InitBinder @InitBinder}, and {@link ModelAttribute @ModelAttribute}
|
||||
* methods that apply to all {@link RequestMapping @RequestMapping} methods.
|
||||
*
|
||||
* <p>One of {@link #annotations()}, {@link #basePackageClasses()},
|
||||
* {@link #basePackages()} or its alias {@link #value()}
|
||||
* may be specified to define specific subsets of Controllers
|
||||
* to assist. When multiple selectors are applied, OR logic is applied -
|
||||
* meaning selected Controllers should match at least one selector.
|
||||
*
|
||||
* <p>The default behavior (i.e. if used without any selector),
|
||||
* the {@code @ControllerAdvice} annotated class will
|
||||
* assist all known Controllers.
|
||||
*
|
||||
* <p>Note that those checks are done at runtime, so adding many attributes and using
|
||||
* multiple strategies may have negative impacts (complexity, performance).
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 3.2
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
|
@ -43,4 +57,56 @@ import org.springframework.stereotype.Component;
|
|||
@Component
|
||||
public @interface ControllerAdvice {
|
||||
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute.
|
||||
* Allows for more concise annotation declarations e.g.:
|
||||
* {@code @ControllerAdvice("org.my.pkg")} instead of
|
||||
* {@code @ControllerAdvice(basePackages="org.my.pkg")}.
|
||||
* @since 4.0
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Array of base packages.
|
||||
* Controllers that belong to those base packages will be selected
|
||||
* to be assisted by the annotated class, e.g.:
|
||||
* {@code @ControllerAdvice(basePackages="org.my.pkg")}
|
||||
* {@code @ControllerAdvice(basePackages={"org.my.pkg","org.my.other.pkg"})}
|
||||
*
|
||||
* <p>{@link #value()} is an alias for this attribute.
|
||||
* <p>Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
|
||||
* @since 4.0
|
||||
*/
|
||||
String[] basePackages() default {};
|
||||
|
||||
/**
|
||||
* Array of classes.
|
||||
* Controllers that are assignable to at least one of the given types
|
||||
* will be assisted by the {@code @ControllerAdvice} annotated class.
|
||||
* @since 4.0
|
||||
*/
|
||||
Class<?>[] assignableTypes() default {};
|
||||
|
||||
|
||||
/**
|
||||
* Array of annotations.
|
||||
* Controllers that are annotated with this/one of those annotation(s)
|
||||
* will be assisted by the {@code @ControllerAdvice} annotated class.
|
||||
*
|
||||
* <p>Consider creating a special annotation or use a predefined one,
|
||||
* like {@link RestController @RestController}.
|
||||
* @since 4.0
|
||||
*/
|
||||
Class<?>[] annotations() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #value()} for specifying the packages
|
||||
* to select Controllers to be assisted by the {@code @ControllerAdvice}
|
||||
* annotated class.
|
||||
*
|
||||
* <p>Consider creating a special no-op marker class or interface in each package
|
||||
* that serves no purpose other than being referenced by this attribute.
|
||||
* @since 4.0
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,9 +15,13 @@
|
|||
*/
|
||||
package org.springframework.web.method;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
|
@ -25,6 +29,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
|
||||
/**
|
||||
|
@ -36,6 +41,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
|||
* any object, including ones without an {@code @ControllerAdvice}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ControllerAdviceBean implements Ordered {
|
||||
|
@ -46,6 +52,13 @@ public class ControllerAdviceBean implements Ordered {
|
|||
|
||||
private final BeanFactory beanFactory;
|
||||
|
||||
private final List<Package> basePackages = new ArrayList<Package>();
|
||||
|
||||
private final List<Class<Annotation>> annotations = new ArrayList<Class<Annotation>>();
|
||||
|
||||
private final List<Class<?>> assignableTypes = new ArrayList<Class<?>>();
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ControllerAdviceBean.class);
|
||||
|
||||
/**
|
||||
* Create an instance using the given bean name.
|
||||
|
@ -59,7 +72,11 @@ public class ControllerAdviceBean implements Ordered {
|
|||
"Bean factory [" + beanFactory + "] does not contain bean " + "with name [" + beanName + "]");
|
||||
this.bean = beanName;
|
||||
this.beanFactory = beanFactory;
|
||||
this.order = initOrderFromBeanType(this.beanFactory.getType(beanName));
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
this.order = initOrderFromBeanType(beanType);
|
||||
this.basePackages.addAll(initBasePackagesFromBeanType(beanType));
|
||||
this.annotations.addAll(initAnnotationsFromBeanType(beanType));
|
||||
this.assignableTypes.addAll(initAssignableTypesFromBeanType(beanType));
|
||||
}
|
||||
|
||||
private static int initOrderFromBeanType(Class<?> beanType) {
|
||||
|
@ -67,6 +84,56 @@ public class ControllerAdviceBean implements Ordered {
|
|||
return (annot != null) ? annot.value() : Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
private static List<Package> initBasePackagesFromBeanType(Class<?> beanType) {
|
||||
List<Package> basePackages = new ArrayList<Package>();
|
||||
ControllerAdvice annotation = AnnotationUtils.findAnnotation(beanType,ControllerAdvice.class);
|
||||
Assert.notNull(annotation,"BeanType ["+beanType.getName()+"] is not annotated @ControllerAdvice");
|
||||
for (String pkgName : (String[])AnnotationUtils.getValue(annotation)) {
|
||||
if (StringUtils.hasText(pkgName)) {
|
||||
Package pack = Package.getPackage(pkgName);
|
||||
if(pack != null) {
|
||||
basePackages.add(pack);
|
||||
} else {
|
||||
logger.warn("Package [" + pkgName + "] was not found, see ["
|
||||
+ beanType.getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String pkgName : (String[])AnnotationUtils.getValue(annotation,"basePackages")) {
|
||||
if (StringUtils.hasText(pkgName)) {
|
||||
Package pack = Package.getPackage(pkgName);
|
||||
if(pack != null) {
|
||||
basePackages.add(pack);
|
||||
} else {
|
||||
logger.warn("Package [" + pkgName + "] was not found, see ["
|
||||
+ beanType.getName() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Class<?> markerClass : (Class<?>[])AnnotationUtils.getValue(annotation,"basePackageClasses")) {
|
||||
Package pack = markerClass.getPackage();
|
||||
if(pack != null) {
|
||||
basePackages.add(pack);
|
||||
} else {
|
||||
logger.warn("Package was not found for class [" + markerClass.getName()
|
||||
+ "], see [" + beanType.getName() + "]");
|
||||
}
|
||||
}
|
||||
return basePackages;
|
||||
}
|
||||
|
||||
private static List<Class<Annotation>> initAnnotationsFromBeanType(Class<?> beanType) {
|
||||
ControllerAdvice annotation = AnnotationUtils.findAnnotation(beanType,ControllerAdvice.class);
|
||||
Class<Annotation>[] annotations = (Class<Annotation>[])AnnotationUtils.getValue(annotation,"annotations");
|
||||
return Arrays.asList(annotations);
|
||||
}
|
||||
|
||||
private static List<Class<?>> initAssignableTypesFromBeanType(Class<?> beanType) {
|
||||
ControllerAdvice annotation = AnnotationUtils.findAnnotation(beanType,ControllerAdvice.class);
|
||||
Class<?>[] assignableTypes = (Class<?>[])AnnotationUtils.getValue(annotation,"assignableTypes");
|
||||
return Arrays.asList(assignableTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance using the given bean instance.
|
||||
* @param bean the bean
|
||||
|
@ -75,6 +142,9 @@ public class ControllerAdviceBean implements Ordered {
|
|||
Assert.notNull(bean, "'bean' must not be null");
|
||||
this.bean = bean;
|
||||
this.order = initOrderFromBean(bean);
|
||||
this.basePackages.addAll(initBasePackagesFromBeanType(bean.getClass()));
|
||||
this.annotations.addAll(initAnnotationsFromBeanType(bean.getClass()));
|
||||
this.assignableTypes.addAll(initAssignableTypesFromBeanType(bean.getClass()));
|
||||
this.beanFactory = null;
|
||||
}
|
||||
|
||||
|
@ -124,6 +194,45 @@ public class ControllerAdviceBean implements Ordered {
|
|||
return (this.bean instanceof String) ? this.beanFactory.getBean((String) this.bean) : this.bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bean type given as a parameter should be assisted by
|
||||
* the current {@code @ControllerAdvice} annotated bean.
|
||||
*
|
||||
* @param beanType the type of the bean
|
||||
* @see org.springframework.web.bind.annotation.ControllerAdvice
|
||||
* @since 4.0
|
||||
*/
|
||||
public boolean isApplicableToBeanType(Class<?> beanType) {
|
||||
if(hasNoSelector()) {
|
||||
return true;
|
||||
}
|
||||
else if(beanType != null) {
|
||||
String packageName = beanType.getPackage().getName();
|
||||
for(Package basePackage : this.basePackages) {
|
||||
if(packageName.startsWith(basePackage.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Class<Annotation> annotationClass : this.annotations) {
|
||||
if(AnnotationUtils.findAnnotation(beanType, annotationClass) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(Class<?> clazz : this.assignableTypes) {
|
||||
if(ClassUtils.isAssignable(clazz, beanType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasNoSelector() {
|
||||
return this.basePackages.isEmpty() && this.annotations.isEmpty()
|
||||
&& this.assignableTypes.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
package org.springframework.web.method;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ControllerAdviceBeanTests {
|
||||
|
||||
@Test
|
||||
public void shouldMatchAll() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new SimpleControllerAdvice());
|
||||
assertApplicable("should match all", bean, AnnotatedController.class);
|
||||
assertApplicable("should match all", bean, ImplementationController.class);
|
||||
assertApplicable("should match all", bean, InheritanceController.class);
|
||||
assertApplicable("should match all", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basePackageSupport() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new BasePackageSupport());
|
||||
assertApplicable("base package support", bean, AnnotatedController.class);
|
||||
assertApplicable("base package support", bean, ImplementationController.class);
|
||||
assertApplicable("base package support", bean, InheritanceController.class);
|
||||
assertNotApplicable("bean not in package", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basePackageValueSupport() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new BasePackageValueSupport());
|
||||
assertApplicable("base package support", bean, AnnotatedController.class);
|
||||
assertApplicable("base package support", bean, ImplementationController.class);
|
||||
assertApplicable("base package support", bean, InheritanceController.class);
|
||||
assertNotApplicable("bean not in package", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationSupport() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new AnnotationSupport());
|
||||
assertApplicable("annotation support", bean, AnnotatedController.class);
|
||||
assertNotApplicable("this bean is not annotated", bean, InheritanceController.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void markerClassSupport() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new MarkerClassSupport());
|
||||
assertApplicable("base package class support", bean, AnnotatedController.class);
|
||||
assertApplicable("base package class support", bean, ImplementationController.class);
|
||||
assertApplicable("base package class support", bean, InheritanceController.class);
|
||||
assertNotApplicable("bean not in package", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotMatch() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new ShouldNotMatch());
|
||||
assertNotApplicable("should not match", bean, AnnotatedController.class);
|
||||
assertNotApplicable("should not match", bean, ImplementationController.class);
|
||||
assertNotApplicable("should not match", bean, InheritanceController.class);
|
||||
assertNotApplicable("should not match", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignableTypesSupport() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new AssignableTypesSupport());
|
||||
assertApplicable("controller implements assignable", bean, ImplementationController.class);
|
||||
assertApplicable("controller inherits assignable", bean, InheritanceController.class);
|
||||
assertNotApplicable("not assignable", bean, AnnotatedController.class);
|
||||
assertNotApplicable("not assignable", bean, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleMatch() {
|
||||
ControllerAdviceBean bean = new ControllerAdviceBean(new MultipleSelectorsSupport());
|
||||
assertApplicable("controller implements assignable", bean, ImplementationController.class);
|
||||
assertApplicable("controller is annotated", bean, AnnotatedController.class);
|
||||
assertNotApplicable("should not match", bean, InheritanceController.class);
|
||||
}
|
||||
|
||||
private void assertApplicable(String message, ControllerAdviceBean controllerAdvice,
|
||||
Class<?> controllerBeanType) {
|
||||
assertNotNull(controllerAdvice);
|
||||
assertTrue(message,controllerAdvice.isApplicableToBeanType(controllerBeanType));
|
||||
}
|
||||
|
||||
private void assertNotApplicable(String message, ControllerAdviceBean controllerAdvice,
|
||||
Class<?> controllerBeanType) {
|
||||
assertNotNull(controllerAdvice);
|
||||
assertFalse(message,controllerAdvice.isApplicableToBeanType(controllerBeanType));
|
||||
}
|
||||
|
||||
// ControllerAdvice classes
|
||||
|
||||
@ControllerAdvice
|
||||
static class SimpleControllerAdvice {}
|
||||
|
||||
@ControllerAdvice(annotations = ControllerAnnotation.class)
|
||||
static class AnnotationSupport {}
|
||||
|
||||
@ControllerAdvice(basePackageClasses = MarkerClass.class)
|
||||
static class MarkerClassSupport {}
|
||||
|
||||
@ControllerAdvice(assignableTypes = {ControllerInterface.class,
|
||||
AbstractController.class})
|
||||
static class AssignableTypesSupport {}
|
||||
|
||||
@ControllerAdvice(basePackages = "org.springframework.web.method")
|
||||
static class BasePackageSupport {}
|
||||
|
||||
@ControllerAdvice("org.springframework.web.method")
|
||||
static class BasePackageValueSupport {}
|
||||
|
||||
@ControllerAdvice(annotations = ControllerAnnotation.class,
|
||||
assignableTypes = ControllerInterface.class)
|
||||
static class MultipleSelectorsSupport {}
|
||||
|
||||
@ControllerAdvice(basePackages = "java.util",
|
||||
annotations = RestController.class)
|
||||
static class ShouldNotMatch {}
|
||||
|
||||
// Support classes
|
||||
|
||||
static class MarkerClass {}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface ControllerAnnotation {}
|
||||
|
||||
@ControllerAnnotation
|
||||
public static class AnnotatedController {}
|
||||
|
||||
static interface ControllerInterface {}
|
||||
|
||||
static class ImplementationController implements ControllerInterface {}
|
||||
|
||||
static abstract class AbstractController {}
|
||||
|
||||
static class InheritanceController extends AbstractController {}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -351,8 +351,8 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
* @return a method to handle the exception, or {@code null}
|
||||
*/
|
||||
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
|
||||
Class<?> handlerType = (handlerMethod != null) ? handlerMethod.getBeanType() : null;
|
||||
if (handlerMethod != null) {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
|
||||
if (resolver == null) {
|
||||
resolver = new ExceptionHandlerMethodResolver(handlerType);
|
||||
|
@ -364,9 +364,11 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce
|
|||
}
|
||||
}
|
||||
for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
|
||||
Method method = entry.getValue().resolveMethod(exception);
|
||||
if (method != null) {
|
||||
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
|
||||
if(entry.getKey().isApplicableToBeanType(handlerType)) {
|
||||
Method method = entry.getValue().resolveMethod(exception);
|
||||
if (method != null) {
|
||||
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -776,9 +776,11 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
// Global methods first
|
||||
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
|
||||
Object bean = entry.getKey().resolveBean();
|
||||
for (Method method : entry.getValue()) {
|
||||
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
|
||||
if(entry.getKey().isApplicableToBeanType(handlerType)) {
|
||||
Object bean = entry.getKey().resolveBean();
|
||||
for (Method method : entry.getValue()) {
|
||||
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Method method : methods) {
|
||||
|
@ -806,9 +808,11 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
// Global methods first
|
||||
for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache .entrySet()) {
|
||||
Object bean = entry.getKey().resolveBean();
|
||||
for (Method method : entry.getValue()) {
|
||||
initBinderMethods.add(createInitBinderMethod(bean, method));
|
||||
if(entry.getKey().isApplicableToBeanType(handlerType)) {
|
||||
Object bean = entry.getKey().resolveBean();
|
||||
for (Method method : entry.getValue()) {
|
||||
initBinderMethods.add(createInitBinderMethod(bean, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Method method : methods) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -204,6 +204,35 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
assertEquals("TestExceptionResolver: IllegalStateException", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveExceptionControllerAdviceHandler() throws Exception {
|
||||
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
|
||||
this.resolver.setApplicationContext(cxt);
|
||||
this.resolver.afterPropertiesSet();
|
||||
|
||||
IllegalStateException ex = new IllegalStateException();
|
||||
HandlerMethod handlerMethod = new HandlerMethod(new ResponseBodyController(), "handle");
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, handlerMethod, ex);
|
||||
|
||||
assertNotNull("Exception was not handled", mav);
|
||||
assertTrue(mav.isEmpty());
|
||||
assertEquals("BasePackageTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveExceptionControllerAdviceNoHandler() throws Exception {
|
||||
AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class);
|
||||
this.resolver.setApplicationContext(cxt);
|
||||
this.resolver.afterPropertiesSet();
|
||||
|
||||
IllegalStateException ex = new IllegalStateException();
|
||||
ModelAndView mav = this.resolver.resolveException(this.request, this.response, null, ex);
|
||||
|
||||
assertNotNull("Exception was not handled", mav);
|
||||
assertTrue(mav.isEmpty());
|
||||
assertEquals("DefaultTestExceptionResolver: IllegalStateException", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
|
||||
private void assertMethodProcessorCount(int resolverCount, int handlerCount) {
|
||||
assertEquals(resolverCount, this.resolver.getArgumentResolvers().getResolvers().size());
|
||||
|
@ -288,4 +317,51 @@ public class ExceptionHandlerExceptionResolverTests {
|
|||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice("java.lang")
|
||||
@Order(1)
|
||||
static class NotCalledTestExceptionResolver {
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public String handleException(IllegalStateException ex) {
|
||||
return "NotCalledTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice("org.springframework.web.servlet.mvc.method.annotation")
|
||||
@Order(2)
|
||||
static class BasePackageTestExceptionResolver {
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public String handleException(IllegalStateException ex) {
|
||||
return "BasePackageTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
@Order(3)
|
||||
static class DefaultTestExceptionResolver {
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public String handleException(IllegalStateException ex) {
|
||||
return "DefaultTestExceptionResolver: " + ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MyControllerAdviceConfig {
|
||||
@Bean public NotCalledTestExceptionResolver notCalledTestExceptionResolver() {
|
||||
return new NotCalledTestExceptionResolver();
|
||||
}
|
||||
|
||||
@Bean public BasePackageTestExceptionResolver basePackageTestExceptionResolver() {
|
||||
return new BasePackageTestExceptionResolver();
|
||||
}
|
||||
|
||||
@Bean public DefaultTestExceptionResolver defaultTestExceptionResolver() {
|
||||
return new DefaultTestExceptionResolver();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,9 @@ import org.junit.Test;
|
|||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
@ -184,6 +186,21 @@ public class RequestMappingHandlerAdapterTests {
|
|||
assertEquals("gAttr2", mav.getModel().get("attr2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelAttributePackageNameAdvice() throws Exception {
|
||||
this.webAppContext.registerSingleton("mapa", ModelAttributePackageAdvice.class);
|
||||
this.webAppContext.registerSingleton("manupa", ModelAttributeNotUsedPackageAdvice.class);
|
||||
this.webAppContext.refresh();
|
||||
|
||||
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
|
||||
this.handlerAdapter.afterPropertiesSet();
|
||||
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
|
||||
|
||||
assertEquals("lAttr1", mav.getModel().get("attr1"));
|
||||
assertEquals("gAttr2", mav.getModel().get("attr2"));
|
||||
assertEquals(null,mav.getModel().get("attr3"));
|
||||
}
|
||||
|
||||
|
||||
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
|
||||
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
|
||||
|
@ -237,4 +254,21 @@ public class RequestMappingHandlerAdapterTests {
|
|||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice({"org.springframework.web.servlet.mvc.method.annotation","java.lang"})
|
||||
private static class ModelAttributePackageAdvice {
|
||||
|
||||
@ModelAttribute
|
||||
public void addAttributes(Model model) {
|
||||
model.addAttribute("attr2", "gAttr2");
|
||||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice("java.lang")
|
||||
private static class ModelAttributeNotUsedPackageAdvice {
|
||||
|
||||
@ModelAttribute
|
||||
public void addAttributes(Model model) {
|
||||
model.addAttribute("attr3", "gAttr3");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue