Springboot注入Web原生三大组件

163 阅读2分钟

Web原生组件的注入(servlet,filter,lister)

@ServletComponentScan: 指定原生servlet组件放在哪里

1.注解实现

@WebServlet(urlPatterns = "/my"):效果:直接响应,没有通过springboot设置的拦截器

@WebServlet(urlPatterns = "/my") // 加入 @WebServlet 注释
public class MyServlet extends HttpServlet { // 注意要继承 HttpServlet 类
    @Override // 重写 DoGet 方法
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("haha");
    }
}

启动类

@ServletComponentScan(basePackages = "com.wanqing.admin") //扫描那个包中有servlet
@SpringBootApplication
public class DemoAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoAdminApplication.class, args);
    }
}

@WebFilter(urlPatterns = "/css/*")

@Slf4j
@WebFilter(urlPatterns = {"/css/*", "/images/*"}) // 拦截静态资源
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("MyFilter初始化完成");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter工作");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
        log.info("MyFilter销毁");
    }
}

@WebListener

@WebListener
@Slf4j	
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("MyServletContextListener 监听到项目初始化完成");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener 监听到项目销毁");
    }
}

2.使用RegisterBean注入原生组件

区别:

  • 不用使用注解指定该类是一个filter或者lister或者servlet

注意点:要记得使用 @Bean 注释将 ServletRegistrationBean 注册到容器中。

自定义 MyRegistConfig 配置类,注册 myServlet 组件,返回 ServletRegistrationBean 对象 (对象参数为自定义的 myServlet 对象实例) myFilter 及myListener 的实现方式同理

@Configuration
public class MyRegistConfig {
    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();
        return new ServletRegistrationBean(myServlet, "/my","/my02");
    }
    @Bean
    public FilterRegistrationBean myFilter(){
        MyFilter filter = new MyFilter();
        //return new FilterRegistrationBean(filter, myServlet()); // 拦截myServlet()的路径
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter);
        filterRegistrationBean.addUrlPatterns("/my","/css/*");
        return filterRegistrationBean;
    }
    @Bean
    public ServletListenerRegistrationBean myListener(){
        MyServletContextListener myServletContextListener = new MyServletContextListener();
        return new ServletListenerRegistrationBean(myServletContextListener);

    }
}

3.为什么原生组件:直接响应,没有通过springboot设置的拦截器

分析 DispatcherServlet 如何注册进入容器中,从 DispatcherServletAutoConfiguration 类开始 容器中自动配置了 DispatcherServlet 组件,其属性绑定到 WebMvcProperties 中,对应的配置文件是 spring.mvc

		public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
			return dispatcherServlet;
		}

通过 ServletRegistrationBean < DispatcherServlet > 机制(DispatcherServletRegistrationBean.class)将 DispatcherServlet 原生的 Servlet 组件配置进来

		@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 multipartConfig) {
   		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;
   	}

拿到默认映射路径 /

WebMvcProperties.class 中配置

image.png

使用 Tomcat 做原生 Servlet 开发,如果多个 Servlet 都能处理到同一层路径,是精确优先原则,例如:

A:/A/

B: /A/1

发送 /A/1 请求 1处理,而发送 /A/2 请求 2处理

结论 : 来到 /A 不经过 A/1 —— 精确匹配 /A 直接经 Tomcat 写出响应,不经过 SpringMVC 的一系列流程,因此不被拦截器拦截,如下图所示