由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。
故而采用其他方式来注册三大组件:代码注册和注解注册。
【1】ServletRegistrationBean注册Servlet
查看ServletRegistrationBean源码如下:
/** * A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+ * container. Similar to the {@link ServletContext#addServlet(String, Servlet) * registration} features provided by {@link ServletContext} but with a Spring Bean * friendly design. * <p> * The {@link #setServlet(Servlet) servlet} must be specified before calling * {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or * omitted when mapping to '/*' (unless * {@link #ServletRegistrationBean(Servlet, boolean, String...) alwaysMapUrl} is set to * {@code false}). The servlet name will be deduced if not specified. * * @author Phillip Webb * @since 1.4.0 * @see ServletContextInitializer * @see ServletContext#addServlet(String, Servlet) */ public class ServletRegistrationBean extends RegistrationBean { private static final Log logger = LogFactory.getLog(ServletRegistrationBean.class); private static final String[] DEFAULT_MAPPINGS = { "/*" }; private Servlet servlet; private Set<String> urlMappings = new LinkedHashSet<String>(); private boolean alwaysMapUrl = true; private int loadOnStartup = -1; private MultipartConfigElement multipartConfig; //... }
注册示例如下:
@Bean public ServletRegistrationBean myServlet(){ ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet"); registrationBean.setLoadOnStartup(1); return registrationBean; }
【2】FilterRegistrationBean注册Filter
查看FilterRegistrationBean源码如下:
/** * A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+ * container. Similar to the {@link ServletContext#addFilter(String, Filter) registration} * features provided by {@link ServletContext} but with a Spring Bean friendly design. * <p> * The {@link #setFilter(Filter) Filter} must be specified before calling * {@link #onStartup(ServletContext)}. Registrations can be associated with * {@link #setUrlPatterns URL patterns} and/or servlets (either by {@link #setServletNames * name} or via a {@link #setServletRegistrationBeans ServletRegistrationBean}s. When no * URL pattern or servlets are specified the filter will be associated to '/*'. The filter * name will be deduced if not specified. * * @author Phillip Webb * @since 1.4.0 * @see ServletContextInitializer * @see ServletContext#addFilter(String, Filter) * @see DelegatingFilterProxyRegistrationBean */ public class FilterRegistrationBean extends AbstractFilterRegistrationBean { /** * Filters that wrap the servlet request should be ordered less than or equal to this. */ public static final int REQUEST_WRAPPER_FILTER_MAX_ORDER = AbstractFilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER; private Filter filter; //... }
注册示例如下:
@Bean public FilterRegistrationBean myFilter(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new MyFilter()); registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet")); return registrationBean; }
【3】ServletListenerRegistrationBean注册Listener
查看ServletListenerRegistrationBean源码如下:
/** * A {@link ServletContextInitializer} to register {@link EventListener}s in a Servlet * 3.0+ container. Similar to the {@link ServletContext#addListener(EventListener) * registration} features provided by {@link ServletContext} but with a Spring Bean * friendly design. * * This bean can be used to register the following types of listener: * <ul> * <li>{@link ServletContextAttributeListener}</li> * <li>{@link ServletRequestListener}</li> * <li>{@link ServletRequestAttributeListener}</li> * <li>{@link HttpSessionAttributeListener}</li> * <li>{@link HttpSessionListener}</li> * <li>{@link ServletContextListener}</li> * </ul> * * @param <T> the type of listener * @author Dave Syer * @author Phillip Webb * @since 1.4.0 */ public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean { private static final Log logger = LogFactory .getLog(ServletListenerRegistrationBean.class); private static final Set<Class<?>> SUPPORTED_TYPES; static { Set<Class<?>> types = new HashSet<Class<?>>(); types.add(ServletContextAttributeListener.class); types.add(ServletRequestListener.class); types.add(ServletRequestAttributeListener.class); types.add(HttpSessionAttributeListener.class); types.add(HttpSessionListener.class); types.add(ServletContextListener.class); SUPPORTED_TYPES = Collections.unmodifiableSet(types); } private T listener; //... }
注册Listener示例如下:
@Bean public ServletListenerRegistrationBean myListener(){ ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener()); return registrationBean; }
【4】SpringMVC的DispatcherServlet默认注册机制
SpringBoot帮我们自动SpringMVC的时候,自动的注册SpringMVC的前端控制器:DIspatcherServlet。
查看DispatcherServletAutoConfiguration源码如下:
/** * {@link EnableAutoConfiguration Auto-configuration} for the Spring * {@link DispatcherServlet}. Should work for a standalone application where an embedded * servlet container is already present and also for a deployable application using * {@link SpringBootServletInitializer}. * * @author Phillip Webb * @author Dave Syer * @author Stephane Nicoll * @author Brian Clozel */ @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) public class DispatcherServletAutoConfiguration { /* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; /* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration"; @Configuration @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { private final WebMvcProperties webMvcProperties; public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) { this.webMvcProperties = webMvcProperties; } @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest( this.webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound( this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } } @Configuration @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { private final ServerProperties serverProperties; private final WebMvcProperties webMvcProperties; private final MultipartConfigElement multipartConfig; public DispatcherServletRegistrationConfiguration( ServerProperties serverProperties, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfigProvider) { this.serverProperties = serverProperties; this.webMvcProperties = webMvcProperties; this.multipartConfig = multipartConfigProvider.getIfAvailable(); } // 这里注册DispatcherServlet @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public ServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet, this.serverProperties.getServletMapping()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; } } @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DefaultDispatcherServletCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage .forCondition("Default DispatcherServlet"); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); List<String> dispatchServletBeans = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(message.found("non dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (dispatchServletBeans.isEmpty()) { return ConditionOutcome .match(message.didNotFind("dispatcher servlet beans").atAll()); } return ConditionOutcome.match(message .found("dispatcher servlet bean", "dispatcher servlet beans") .items(Style.QUOTE, dispatchServletBeans) .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } } @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DispatcherServletRegistrationCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory); if (!outcome.isMatch()) { return outcome; } return checkServletRegistration(beanFactory); } private ConditionOutcome checkDefaultDispatcherName( ConfigurableListableBeanFactory beanFactory) { List<String> servlets = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); boolean containsDispatcherBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(startMessage().found("non dispatcher servlet") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } return ConditionOutcome.match(); } private ConditionOutcome checkServletRegistration( ConfigurableListableBeanFactory beanFactory) { ConditionMessage.Builder message = startMessage(); List<String> registrations = Arrays.asList(beanFactory .getBeanNamesForType(ServletRegistrationBean.class, false, false)); boolean containsDispatcherRegistrationBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); if (registrations.isEmpty()) { if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome .match(message.didNotFind("servlet registration bean").atAll()); } if (registrations .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("servlet registration bean") .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome.match(message.found("servlet registration beans") .items(Style.QUOTE, registrations).append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } private ConditionMessage.Builder startMessage() { return ConditionMessage.forCondition("DispatcherServlet Registration"); } } }
【5】Servlet3.0下注册三大组件的方式
在上面的源码注释中,频繁见到如下字样:
A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+ * container. Similar to the {@link ServletContext#addFilter(String, Filter) registration} * features provided by {@link ServletContext} but with a Spring Bean friendly design.
即,SpringBoot提供的三大组件添加方式只是一种更为友好的设计方式,和Servlet3.0中使用ServletContext#addFilter方式一样!
【6】SpringBoot下使用注册注册三大组件
该种方式与【5】中Servlet、Filter和Listener(注解注册)是一致的,只是需要在主配置类上添加@ServletComponentScan注册。
示例如下:
@SpringBootApplication @ServletComponentScan public class SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringBootApplication.class, args); } @Bean public ViewResolver myViewReolver(){ return new MyViewResolver(); } public static class MyViewResolver implements ViewResolver{ @Override public View resolveViewName(String viewName, Locale locale) throws Exception { return null; } } }
然后就可以在Servlet、Filter和Listener上使用注解进行注册!