001/* 002 * Copyright 2012-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.web; 018 019import java.util.Arrays; 020import java.util.List; 021 022import javax.servlet.MultipartConfigElement; 023import javax.servlet.ServletRegistration; 024 025import org.springframework.beans.factory.ObjectProvider; 026import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 027import org.springframework.boot.autoconfigure.AutoConfigureAfter; 028import org.springframework.boot.autoconfigure.AutoConfigureOrder; 029import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 030import org.springframework.boot.autoconfigure.condition.ConditionMessage; 031import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; 032import org.springframework.boot.autoconfigure.condition.ConditionOutcome; 033import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 034import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 037import org.springframework.boot.autoconfigure.condition.SpringBootCondition; 038import org.springframework.boot.context.properties.EnableConfigurationProperties; 039import org.springframework.boot.web.servlet.ServletRegistrationBean; 040import org.springframework.boot.web.support.SpringBootServletInitializer; 041import org.springframework.context.annotation.Bean; 042import org.springframework.context.annotation.ConditionContext; 043import org.springframework.context.annotation.Conditional; 044import org.springframework.context.annotation.Configuration; 045import org.springframework.context.annotation.Import; 046import org.springframework.core.Ordered; 047import org.springframework.core.annotation.Order; 048import org.springframework.core.type.AnnotatedTypeMetadata; 049import org.springframework.web.multipart.MultipartResolver; 050import org.springframework.web.servlet.DispatcherServlet; 051 052/** 053 * {@link EnableAutoConfiguration Auto-configuration} for the Spring 054 * {@link DispatcherServlet}. Should work for a standalone application where an embedded 055 * servlet container is already present and also for a deployable application using 056 * {@link SpringBootServletInitializer}. 057 * 058 * @author Phillip Webb 059 * @author Dave Syer 060 * @author Stephane Nicoll 061 * @author Brian Clozel 062 */ 063@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 064@Configuration 065@ConditionalOnWebApplication 066@ConditionalOnClass(DispatcherServlet.class) 067@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) 068public class DispatcherServletAutoConfiguration { 069 070 /* 071 * The bean name for a DispatcherServlet that will be mapped to the root URL "/" 072 */ 073 public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; 074 075 /* 076 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" 077 */ 078 public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration"; 079 080 @Configuration 081 @Conditional(DefaultDispatcherServletCondition.class) 082 @ConditionalOnClass(ServletRegistration.class) 083 @EnableConfigurationProperties(WebMvcProperties.class) 084 protected static class DispatcherServletConfiguration { 085 086 private final WebMvcProperties webMvcProperties; 087 088 public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) { 089 this.webMvcProperties = webMvcProperties; 090 } 091 092 @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) 093 public DispatcherServlet dispatcherServlet() { 094 DispatcherServlet dispatcherServlet = new DispatcherServlet(); 095 dispatcherServlet.setDispatchOptionsRequest( 096 this.webMvcProperties.isDispatchOptionsRequest()); 097 dispatcherServlet.setDispatchTraceRequest( 098 this.webMvcProperties.isDispatchTraceRequest()); 099 dispatcherServlet.setThrowExceptionIfNoHandlerFound( 100 this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); 101 return dispatcherServlet; 102 } 103 104 @Bean 105 @ConditionalOnBean(MultipartResolver.class) 106 @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) 107 public MultipartResolver multipartResolver(MultipartResolver resolver) { 108 // Detect if the user has created a MultipartResolver but named it incorrectly 109 return resolver; 110 } 111 112 } 113 114 @Configuration 115 @Conditional(DispatcherServletRegistrationCondition.class) 116 @ConditionalOnClass(ServletRegistration.class) 117 @EnableConfigurationProperties(WebMvcProperties.class) 118 @Import(DispatcherServletConfiguration.class) 119 protected static class DispatcherServletRegistrationConfiguration { 120 121 private final ServerProperties serverProperties; 122 123 private final WebMvcProperties webMvcProperties; 124 125 private final MultipartConfigElement multipartConfig; 126 127 public DispatcherServletRegistrationConfiguration( 128 ServerProperties serverProperties, WebMvcProperties webMvcProperties, 129 ObjectProvider<MultipartConfigElement> multipartConfigProvider) { 130 this.serverProperties = serverProperties; 131 this.webMvcProperties = webMvcProperties; 132 this.multipartConfig = multipartConfigProvider.getIfAvailable(); 133 } 134 135 @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) 136 @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) 137 public ServletRegistrationBean dispatcherServletRegistration( 138 DispatcherServlet dispatcherServlet) { 139 ServletRegistrationBean registration = new ServletRegistrationBean( 140 dispatcherServlet, this.serverProperties.getServletMapping()); 141 registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); 142 registration.setLoadOnStartup( 143 this.webMvcProperties.getServlet().getLoadOnStartup()); 144 if (this.multipartConfig != null) { 145 registration.setMultipartConfig(this.multipartConfig); 146 } 147 return registration; 148 } 149 150 } 151 152 @Order(Ordered.LOWEST_PRECEDENCE - 10) 153 private static class DefaultDispatcherServletCondition extends SpringBootCondition { 154 155 @Override 156 public ConditionOutcome getMatchOutcome(ConditionContext context, 157 AnnotatedTypeMetadata metadata) { 158 ConditionMessage.Builder message = ConditionMessage 159 .forCondition("Default DispatcherServlet"); 160 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 161 List<String> dispatchServletBeans = Arrays.asList(beanFactory 162 .getBeanNamesForType(DispatcherServlet.class, false, false)); 163 if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { 164 return ConditionOutcome.noMatch(message.found("dispatcher servlet bean") 165 .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); 166 } 167 if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { 168 return ConditionOutcome 169 .noMatch(message.found("non dispatcher servlet bean") 170 .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); 171 } 172 if (dispatchServletBeans.isEmpty()) { 173 return ConditionOutcome 174 .match(message.didNotFind("dispatcher servlet beans").atAll()); 175 } 176 return ConditionOutcome.match(message 177 .found("dispatcher servlet bean", "dispatcher servlet beans") 178 .items(Style.QUOTE, dispatchServletBeans) 179 .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); 180 } 181 182 } 183 184 @Order(Ordered.LOWEST_PRECEDENCE - 10) 185 private static class DispatcherServletRegistrationCondition 186 extends SpringBootCondition { 187 188 @Override 189 public ConditionOutcome getMatchOutcome(ConditionContext context, 190 AnnotatedTypeMetadata metadata) { 191 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 192 ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory); 193 if (!outcome.isMatch()) { 194 return outcome; 195 } 196 return checkServletRegistration(beanFactory); 197 } 198 199 private ConditionOutcome checkDefaultDispatcherName( 200 ConfigurableListableBeanFactory beanFactory) { 201 List<String> servlets = Arrays.asList(beanFactory 202 .getBeanNamesForType(DispatcherServlet.class, false, false)); 203 boolean containsDispatcherBean = beanFactory 204 .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); 205 if (containsDispatcherBean 206 && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { 207 return ConditionOutcome 208 .noMatch(startMessage().found("non dispatcher servlet") 209 .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); 210 } 211 return ConditionOutcome.match(); 212 } 213 214 private ConditionOutcome checkServletRegistration( 215 ConfigurableListableBeanFactory beanFactory) { 216 ConditionMessage.Builder message = startMessage(); 217 List<String> registrations = Arrays.asList(beanFactory 218 .getBeanNamesForType(ServletRegistrationBean.class, false, false)); 219 boolean containsDispatcherRegistrationBean = beanFactory 220 .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); 221 if (registrations.isEmpty()) { 222 if (containsDispatcherRegistrationBean) { 223 return ConditionOutcome 224 .noMatch(message.found("non servlet registration bean").items( 225 DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); 226 } 227 return ConditionOutcome 228 .match(message.didNotFind("servlet registration bean").atAll()); 229 } 230 if (registrations 231 .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) { 232 return ConditionOutcome.noMatch(message.found("servlet registration bean") 233 .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); 234 } 235 if (containsDispatcherRegistrationBean) { 236 return ConditionOutcome 237 .noMatch(message.found("non servlet registration bean").items( 238 DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); 239 } 240 return ConditionOutcome.match(message.found("servlet registration beans") 241 .items(Style.QUOTE, registrations).append("and none is named " 242 + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); 243 } 244 245 private ConditionMessage.Builder startMessage() { 246 return ConditionMessage.forCondition("DispatcherServlet Registration"); 247 } 248 249 } 250 251}