SpringBoot 注册Servlet,Filter,Listener
我们现在使用工程大多是SpringBoot应用, 默认是以jar包的方式运行,使用嵌入式的Tomcat容器,而以前我们使用的Spring Web应用是以war包的方式,放在外部的Tomcat或者WebLogic容器中运行,如果是web应用,工程的src下会有一个webapp/WEB-INF/web.xml文件,我们可以把三大组件都注册在web.xml文件中,而 SpringBoot 工程没有 web.xml 文件怎么注册三大组件呢?SpringBoot为我们提供了注册方式:
• ServletRegistrationBean
@Bean
public ServletRegistrationBean myServlet(){
// 如果发送/myServlet请求,使用自定义的MyServlet处理
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
return servletRegistrationBean;
}
• FilterRegistrationBean
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet"));
return filterRegistrationBean;
}
• ServletListenerRegistrationBean
@Bean
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(new MyListener());
return servletListenerRegistrationBean;
}
// 我们的Listener有哪些接口可以继承在的静态代码块中有初始化
static {
Set<Class<?>> types = new HashSet<>();
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);
}
SpringBoot 配置 MVC 的时候帮我们自动配置的前端控制器 DispatcherServlet 也是通过这种方式: DispatcherServletAutoConfiguration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties,
ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
// 默认拦截 / 路径下的请求,可以通过spring.mvc.servlet.path 修改SpringMVC前端控制器默认请求
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
其实在SpringBoot应用中 Servlet 的这三大组件已经过时了,现在基本都不用,原来的 Servlet 现在被 Controller 代替了,Listener的作用也不明显,只有 Filter 还会有使用场景,有时候会使用它做统一的登录验证,比如登录之后把用户信息放在session,在 Filter 里判断 session 中是否存在用户信息,不存在则跳转到登陆页面。
那下面就来说下 Filter:
前端页面在发送请求到 Tomcat 容器之后,web容器会把 http 协议请求包装成一个 HttpRequest 对象,然后用这个 HttpRequest 对象贯穿整个请求的始终,请求会首先到达 Filter,然后调用 Servlet 里的service方法。所以他们共享同一个 HttpRequest 对象,其实 Filter 的作用类似 Spring AOP,如果这里不清楚,可以画个图看下:
每个 Filter 的 doFiltet 方法里都必须有 chain. doFilter(request, response) 方法,代表请求继续往下执行,那我们来看看这个方法干了什么:
// 方法最终会调到 ApplicationFilterChain 的 doFilter 方法
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 判断是否安全可用,这里是false
if( Globals.IS_SECURITY_ENABLED ) {
// 省略
} else {
// 调用这个方法
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 如果有下一个Filter,调用它的doFilter方法
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
// 还是一些异常
}
return;
}
// 如果没有下一个Filter,调用Servlet的service方法
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
// 调用servlet.service
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
//
}
}
所以我们看到这个chain. doFilter(request, response) 一共干了两件事:
1、如果有下一个Filter,调用它的doFilter方法
2、如果没有下一个Filter,调用Servlet的service方法
方法结束后会沿着链路回到 Filter 里的 doFilter 方法,最终返回到浏览器。
以上就是Servlet三大组件在SpringBoot中的注入方式