註冊Servlet、Filter和Listener(程式碼和註解兩種方式)
阿新 • • 發佈:2018-12-31
由於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方式一樣!
參考部落格如下:
ServletContainerInitializer並註冊三大元件:部落格地址
Servlet、Filter和Listener(註解註冊):部落格地址
【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上使用註解進行註冊!