如何在Spring MVC使用原生Servlet?
- @ServletComponentScan(basePackages = “com.xxx.xxx”)
- @WebServlet(urlPatterns = "/my/* ") 不经过Spring MVC的拦截器 需要注意的是跟SpringMVC不同,原生Filter 使用一个型号进行路径匹配 如 /my/*
- @WebFilter(urlPatterns={"/css/*","/images/*"})
- @WebListener
@ServletComponentScan(basePackages = {"com.corn.turorial.spring.servlet"})
@SpringBootApplication
public class SpringMain {
public static void main(String[] args) {
SpringApplication.run(SpringMain.class, args);
}
}
@WebServlet(urlPatterns = {"/my/Servlet"})
public class MyCustomerServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType(MediaType.APPLICATION_JSON_VALUE);
resp.getWriter().write("from my servlet!");
}
}
@Slf4j
@WebFilter(urlPatterns = "/my/*")
public class MyCustomerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("自定义filter 的 init方法");
}
@Override
public void destroy() {
log.info(" filter 销毁 ");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
log.info("进入 filter 内部");
filterChain.doFilter(request, response);
}
}
@Slf4j
@WebListener
public class MyCustomerListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info(" servlet 上下文 start");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info(" servlet 上下文 destroy");
}
}
方式二使用RegistrationBean的方式注册原生Se rvlet组件
ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean
@Configuration
public class MyServletConfiguration {
@Bean
public ServletRegistrationBean<Servlet> myServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setServlet(new MyCustomerServlet());
bean.setUrlMappings(Arrays.asList("/my/Servlet"));
return bean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filter = new FilterRegistrationBean();
filter.setFilter(new MyCustomerFilter());
filter.setUrlPatterns(Arrays.asList("/my/*"));
return filter;
}
@Bean
public ServletListenerRegistrationBean myListener() {
ServletListenerRegistrationBean<ServletContextListener> listener = new ServletListenerRegistrationBean<>();
listener.setListener(new MyCustomerListener());
return listener;
}
}
DispatcherServlet的分析
Spring MVC的核心DispatcherServlet 的自动配置在 DispatcherServletAutoConfiguration类中
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
// new了一个DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// 从webMvcProperties 中拿到各种配置设置属性
return dispatcherServlet;
}}
// .. 省略
进入DispatcherServlet内部查看细节 核心有一个onRefresh方法
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
// 各种初始化 如HandlerMapping HandlerAdpater
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
onRefresh被上层的FrameworkServlet调用
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
onApplicationEvent方法被FrameworkServlet 的内部类ContextRefreshListener调用而这个是实现了Spring的ApplicationListener接口 传入了ContextRefreshedEvent事件 在Spring启动过程中会触发这个事件,然后回调这个onRefresh方法从而初始化DispatcherServlet的各种属性
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
而DispatherServlet中的initStrategies 方法里面各种init方法如何实现
以initHandlerMappings 为例 BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false)使用这个方法获取容器中所有的HandlerMapping组件 但是我们从来没有注册过HandlerMapping组件. 这个组件的注册又得追溯到WebMvcConfigurationSupport这个类又是WebMVC自动配置的底层核心
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// 省略
WebMvcConfigurationSupport默认注册了大量我们在MVC中会只用到的Bean
如RequestMappingHandlerMapping
如RouterFunctionMapping
如BeanNameUrlHandlerMapping
如内容协商管理器
在WebMvcAutoConfiguration中 引入了EnableWebMvcConfiguration
而 EnableWebMvcConfiguration 继承DelegatingWebMvcConfiguration DelegatingWebMvcConfiguration又继承了WebMvcConfigurationSupport 此时跟底层的Support联系了起来
需要注意的是如果标注了@EnableWebMVC注解 表示我们将全面接管 SpringMVC WebMvcAutoConfiguration所有的配置项将不在生效,只会提供最底层了WebMvcConfigurationSupport 的一些基本功能 开发不建议使用@EnableWebMVC注解 而是推荐标注@Configuration 继承WebMvcConfigurer 来覆盖我们需要拓展的逻辑