Web原生组件注入
ServletAPI
要想在SpringBoot中使用Servlet原生API【Servlet、Filter、Listener】,有两种方法,一种是使用Servlet3.0提供的注解,一种是使用RegistrationBean,通过配置类的方式添加ServletAPI。
使用Servlet提供的注解
使用这种注解方式时,要给Application类里面添加一个 @ServletComponentScan(basePackages = {扫描包路径})
注解,否则就不能扫描到添加的Servlet注解
Servlet
在类上面加一个 @WebServlet
注解,属性UrlPatterns是当前 Servlet
映射的请求路径。其他的和 Servlet 用法一样
/**
* @WebServlet 添加后能使用servlet技术栈
* 属性urlPatterns值是当前servlet映射路径
* @author xyk
*/
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("哈哈哈");
}
}
Filter
和Servlet差不多,添加一个@WebFilter注解,属性UrlPatterns值代表拦截路径
@WebFilter(urlPatterns = {"/my","/my01","/css/*"})
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter开始工作");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request,response);
System.out.println("MyFilter放行了");
}
@Override
public void destroy() {
System.out.println("MyFilter工作结束了");
}
}
Listener
添加一个@WebListener注解
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext对象生成了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext对象销毁了");
}
}
使用RegistrationBean
首先创建一个配置类,默认是单例模式
配置类里面使用不同的RegistrationBean,并在容器中注册组件。
最后也能达到效果。
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my01");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// 有参构造的写法保证servlet映射的路径也能被过滤器拦截
// return new FilterRegistrationBean(myFilter,myServlet());
//或者通过调用setUrlPattern方法添加拦截路径
FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean(myFilter);
filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/aaa"));
return filterFilterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MyListener myListener = new MyListener();
return new ServletListenerRegistrationBean(myListener);
}
}
上面这两种注入原生ServletAPI方式处理请求时,都不会经过拦截器拦截,即都不经过DispatcherServlet分发处理,就是都被tomcat直接处理。
DispatcherServlet与普通Servlet的不同
首先来到DispatcherServletAutoConfiguration类中,在DispatcherServlet自动配置类中。
//在容器中添加了一个组件并且前提是容器中要有名字叫 dispatcherServlet 的组件。这个在上面都注册了的
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
//这里面的操作和前面添加ServletAPI的操作一样
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
//从容器中拿dispatcherServlet用,然后dispatcherServlet的路径是由跟配置文件绑定的类的属性
//根据getPath方法观察内部,能知道获取的path="/"
//同时webMvcProperties是跟配置文件绑定的,就能通过 spring.mvc.servlet.path 修改默认配置
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
根据上面的配置方法,知道了在程序里面,会有一个映射路径为 "/" 的dispatcherServlet和一个 映射路径具体为 "/my" 的servlet,或者更多个映射路径为 "/my/" 的servlet。
当有一个请求为 "/my" ,就会被精确匹配给 映射路径为 "/my" 的servlet处理,如果路径为 "/aa" 就会被 dispatcherServlet 处理,如果路径为 "/my/3" 就会被映射路径为 "/my/" 的servlet处理。
结论:tomcatServlet处理原则,当多个Servlet处于同一层,遵循精确匹配原则。