mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Ensure ErrorControllers work when using AOP
Add a BeanFactoryPostProcessor to set PRESERVE_TARGET_CLASS_ATTRIBUTE to true on all ErrorController bean definitions. Without this attribute AOP advice on @Controllers causes ErrorController beans to be created as JDK proxies (since they implement a single valid looking interface) and therefore not get found by Spring MVC. Fixes gh-4236
This commit is contained in:
parent
ee93307402
commit
cfbac20807
@ -24,8 +24,12 @@ import javax.servlet.Servlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
@ -102,6 +106,11 @@ public class ErrorMvcAutoConfiguration
|
||||
new ErrorPage(this.properties.getServletPrefix() + this.errorPath));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PreserveErrorControllerTargetClassPostProcessor preserveErrorControllerTargetClassPostProcessor() {
|
||||
return new PreserveErrorControllerTargetClassPostProcessor();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "error.whitelabel", name = "enabled", matchIfMissing = true)
|
||||
@Conditional(ErrorTemplateMissingCondition.class)
|
||||
@ -225,4 +234,29 @@ public class ErrorMvcAutoConfiguration
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} to ensure that the target class of ErrorController
|
||||
* MVC beans are preserved when using AOP.
|
||||
*/
|
||||
static class PreserveErrorControllerTargetClassPostProcessor
|
||||
implements BeanFactoryPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
String[] errorControllerBeans = beanFactory
|
||||
.getBeanNamesForType(ErrorController.class, false, false);
|
||||
for (String errorControllerBean : errorControllerBeans) {
|
||||
try {
|
||||
beanFactory.getBeanDefinition(errorControllerBean).setAttribute(
|
||||
AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ import java.lang.annotation.Target;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -34,6 +38,7 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.test.ApplicationContextTestUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@ -101,11 +106,21 @@ public class BasicErrorControllerDirectMockMvcTests {
|
||||
setup((ConfigurableWebApplicationContext) new SpringApplication(
|
||||
WebMvcIncludedConfiguration.class).run("--server.port=0",
|
||||
"--error.whitelabel.enabled=false"));
|
||||
|
||||
thrown.expect(ServletException.class);
|
||||
this.thrown.expect(ServletException.class);
|
||||
this.mockMvc.perform(get("/error").accept(MediaType.TEXT_HTML));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorControllerWithAop() throws Exception {
|
||||
setup((ConfigurableWebApplicationContext) new SpringApplication(
|
||||
WithAopConfiguration.class).run("--server.port=0"));
|
||||
MvcResult response = this.mockMvc
|
||||
.perform(get("/error").accept(MediaType.TEXT_HTML))
|
||||
.andExpect(status().isOk()).andReturn();
|
||||
String content = response.getResponse().getContentAsString();
|
||||
assertTrue("Wrong content: " + content, content.contains("status=999"));
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@ -115,6 +130,7 @@ public class BasicErrorControllerDirectMockMvcTests {
|
||||
HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class })
|
||||
protected static @interface MinimalWebConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ -127,6 +143,7 @@ public class BasicErrorControllerDirectMockMvcTests {
|
||||
@MinimalWebConfiguration
|
||||
@EnableWebMvc
|
||||
protected static class WebMvcIncludedConfiguration {
|
||||
|
||||
// For manual testing
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WebMvcIncludedConfiguration.class, args);
|
||||
@ -137,6 +154,7 @@ public class BasicErrorControllerDirectMockMvcTests {
|
||||
@Configuration
|
||||
@MinimalWebConfiguration
|
||||
protected static class VanillaConfiguration {
|
||||
|
||||
// For manual testing
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(VanillaConfiguration.class, args);
|
||||
@ -147,11 +165,30 @@ public class BasicErrorControllerDirectMockMvcTests {
|
||||
@Configuration
|
||||
@MinimalWebConfiguration
|
||||
protected static class ChildConfiguration {
|
||||
|
||||
// For manual testing
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(ParentConfiguration.class)
|
||||
.child(ChildConfiguration.class).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = false)
|
||||
@MinimalWebConfiguration
|
||||
@Aspect
|
||||
protected static class WithAopConfiguration {
|
||||
|
||||
@Pointcut("within(@org.springframework.stereotype.Controller *)")
|
||||
private void controllerPointCut() {
|
||||
};
|
||||
|
||||
@Around("controllerPointCut()")
|
||||
public Object mvcAdvice(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user