皇冠体育app:【Spring】内嵌Tomcat&去Xml&调试Mvc

admin 3个月前 (06-29) 科技 46 0

菜瓜:今天听到个名词“父子容器”,百度了一下,感受观点有点朴陋,这是什么核武器?

水稻:你说的是SpringMvc和Spring吧,实在只是一个观点而已,用来将两个容器做隔离,起到解耦的作用,其中子容器可以拿到父容器的bean,父容器拿不到子容器的。然则SpringBoot出来之后这个观点基本就被淡化掉,没有太大意义,SpringBoot中只有一个容器了。

菜瓜:能不能给个demo?

水稻:可以。由于现在SpringBoot已经大行其道,Mvc你可能接触的少,甚至没接触过。

  • 早些年启动一个Mvc项目费老鼻子劲了,要设置种种Xml文件(Web.xml,spring.xml,spring-dispather.xml),然后开发完的项目要打成War包发到Tomcat容器中
  • 现在可以直接引入Tomcat包,用main方式直接调起。为了调试利便,我就演示一个Pom引入Tomcat的例子
  • ①启动类
  • package com.vip.qc.mvc;
    
    import org.apache.catalina.Context;
    import org.apache.catalina.LifecycleException;
    import org.apache.catalina.LifecycleListener;
    import org.apache.catalina.startup.Tomcat;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    /**
     * 参考: * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
     * <p>
     * 嵌入tomcat,由Tomcat提议对Spring容器的初始化挪用历程
     * <p>
     * - 启动历程
     * * - Servlet规范,Servlet容器在启动之后会SPI加载META-INF/services目录下的实现类并挪用其onStartup方式
     * * - Spring遵照规范实现了ServletContainerInitializer接口。该接口在执行时会网络WebApplicationInitializer接口实现类并循环挪用其onStartup方式
     * * - 其中AbstractDispatcherServletInitializer
     * * * - 将spring上下文放入ContextLoaderListener监听器,该监听会提议对refresh方式的挪用
     * * * - 注册dispatcherServlet,后续会由tomcat挪用HttpServletBean的init方式,完成子容器的refresh挪用
     * *
     *
     * @author QuCheng on 2020/6/28.
     */
    public class SpringWebStart {
    
        public static void main(String[] args) {
            Tomcat tomcat = new Tomcat();
            try {
    // 此处需要取一个目录 Context context
    = tomcat.addContext("/", System.getProperty("java.io.tmp")); context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance()); tomcat.setPort(8081); tomcat.start(); tomcat.getServer().await(); } catch (LifecycleException | ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } } static class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { private final static String PACKAGE_PATH = "com.vip.qc.mvc.controller"; private final static String PACKAGE_PATH_CHILD = "com.vip.qc.mvc.service"; @Override protected String[] getServletMappings() { return new String[]{"/"}; } @Override protected Class<?>[] getRootConfigClasses() { // spring 父容器 return new Class[]{AppConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { // servlet 子容器 return new Class[]{ServletConfig.class}; } @Configuration @ComponentScan(value = PACKAGE_PATH_CHILD, excludeFilters = @ComponentScan.Filter(classes = Controller.class)) static class AppConfig { } @Configuration @ComponentScan(value = PACKAGE_PATH, includeFilters = @ComponentScan.Filter(classes = Controller.class)) static class ServletConfig { } } } 
  • ②Controller&Service
  • package com.vip.qc.mvc.controller;
    
    import com.vip.qc.mvc.service.ServiceChild;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Controller
    public class ControllerT implements ApplicationContextAware {
    
        @Resource
        private ServiceChild child;
    
        @RequestMapping("/hello")
        @ResponseBody
        public String containter() {
            child.getParent();
            System.out.println("parentContainer");
            return "containter";
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("子容器" + applicationContext);
            System.out.println("子容器中获取父容器bean" + applicationContext.getBean(ServiceChild.class));
        }
    }
    
    
    package com.vip.qc.mvc.service;
    
    import com.vip.qc.mvc.controller.ControllerT;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Service;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Service
    public class ServiceChild implements ApplicationContextAware {
    
        //    @Resource
        private ControllerT controllerT;
    
        public void getParent() {
    
            System.out.println(controllerT);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            System.out.println("父容器" + applicationContext);
            try {
                System.out.println("父容器中获取子容器bean" + applicationContext.getBean(ControllerT.class));
            } catch (NoSuchBeanDefinitionException e) {
                System.out.println("找不到子容器的bean");
            }
        }
    }

    // 挪用
    SpringWebStart的main方式启动-会有如下打印
    父容器Root WebApplicationContext, started on Sun Jun 28 22:03:52 CST 2020
    找不到子容器的bean
    子容器WebApplicationContext for namespace 'dispatcher-servlet', started on Sun Jun 28 22:03:58 CST 2020, parent: Root WebApplicationContext
    子容器中获取父容器beancom.vip.qc.mvc.service.ServiceChild@4acfc43a
      
  • Demo比较简单,不外也能反映父子容器的关系

菜瓜:嗯,效果看到了,能不能讲一下启动历程

水稻:稍等,我去下载源码。上面代码演示中已经提前说明晰,父子容器的加载是Tomcat依据Servlet规范提议挪用完成的

  • spring-web源码包的/META-INF中能找到SPI的现实加载类SpringServletContainerInitializer#onStartup()方式会搜集实现WebApplicationInitializer接口的类,并挪用其onStartup方式
  • 上面MyWebApplicationInitializer启动类是WebApplicationInitializer的子类,未实现onStartup,现实挪用的是其抽象父类AbstractDispatcherServletInitializer的方式。跟进去 
  • @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
       //① 建立Spring父容器上下文-工具放入ContextLoadListener,后续调起完成初始化,
       super.onStartup(servletContext);
       //② 建立DispatcherServlet工具,后续会由tomcat挪用其init方式,完成子容器的初始化事情
       registerDispatcherServlet(servletContext);
    }
    
    // ①进来
    protected void registerContextLoaderListener(ServletContext servletContext) {
        // 此处会回调我们启动类的getRootConfigClasses()方式 - 父容器设置
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            istener.setContextInitializers(getRootApplicationContextInitializers());
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                  "createRootApplicationContext() did not return an application context");
        }
    }
    
    // ②进来
    protected void registerDispatcherServlet(ServletContext servletContext) {
            。。。
        // 此处会回调我们启动类的getServletConfigClasses()方式 - 子容器设置
        WebApplicationContext servletAppContext = createServletApplicationContext();
            。。。
        // 初始化的dispatcherServlet,会加入Tomcat容器中-后续挪用
        // FrameworkServlet#initServletBean()会完成上下文初始化事情
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
            。。。
    }

菜瓜:这样容器就可以用了吗?

水稻:是的,这样就可以直接在浏览器上面接见http://localhost:8081/hello,不外这是一个最简陋的web项目

菜瓜:懂了,最简陋是什么意思

水稻:若是我们想加一些常见的Web功效,譬如说拦截器,过滤器啥的。可以通过@EnableWebMvc注解自定义一些功效

  • package com.vip.qc.mvc;
    
    import com.vip.qc.mvc.interceptor.MyInterceptor1;
    import com.vip.qc.mvc.interceptor.MyInterceptor2;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import javax.annotation.Resource;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    @EnableWebMvc
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Resource
        private MyInterceptor1 interceptor1;
        @Resource
        private MyInterceptor2 interceptor2;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(interceptor1).addPathPatterns("/interceptor/**");
            registry.addInterceptor(interceptor2).addPathPatterns("/interceptor/**");
        }
    }
    
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor1 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是拦截器1 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是拦截器1 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是拦截器1 after");
        }
    }
    
    
    package com.vip.qc.mvc.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author QuCheng on 2020/6/28.
     */
    @Configuration
    public class MyInterceptor2 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("嘻嘻 我是拦截器2 pre");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("嘻嘻 我是拦截器2 post");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("嘻嘻 我是拦截器2 after");
        }
    
    } 

菜瓜:我知道,这里另有个Mvc请求挪用流程和这个拦截器有关。而且这个拦截器不是MethodInterceptor(切面)

水稻:没错,说到这里顺便温习一下Mvc的请求历程

  • 请求最最先都是通过Tomcat容器转发过来的,挪用链:HttpServlet#service() -> FrameworkServlet#processRequest() -> DispatcherServlet#doDispather()
  •  1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     2     。。。
     3   processedRequest = checkMultipart(request);
     4   multipartRequestParsed = (processedRequest != request);
     5   // 1.返回一个持有methodHandler(根据URL匹配得出的被挪用bean工具以及目的方式)挪用链(拦截器链)工具
     6   mappedHandler = getHandler(processedRequest);
     7   。。。
     8   // 2.根据我们现在写代码的方式,只会用到HandlerMethod,其他三种基本不会用
     9   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    10   。。。
    11   // 3.前置过滤器 - 顺序挪用
    12   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    13       return;
    14   }
    15   // 4.Actually invoke the handler.
    16   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    17   。。。
    18     applyDefaultViewName(processedRequest, mv);
    19   // 5.后置过滤器 - 逆序挪用
    20   mappedHandler.applyPostHandle(processedRequest, response, mv);
    21   。。。
    22   // 6.处置试图 - 内部render
    23   processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    24   }
    25   catch (Exception ex) {
    26      // 异常处置
    27      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    28   }
    29       // 异常处置
    30   catch (Throwable err) {
    31      triggerAfterCompletion(processedRequest, response, mappedHandler,
    32                     new NestedServletException("Handler processing failed", err));
    33   }
    34     。。。

     

 菜瓜:这个之前看过不少,百度一大堆,不外照样源码亲热

 

总结:

  • 现在基本互联网项目都是SpringBoot起手了,再难遇到SpringMvc的项目,不外熟悉该流程有利于我们加倍深刻的明白Ioc容器
  • Mvc拦截器链也是一样平常开发中会用到的功效,顺便熟悉一下请求的执行历程

 

,

ALLBET官网娱乐平台开户

欢迎进入ALLBET官网娱乐平台开户:www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:皇冠体育app:【Spring】内嵌Tomcat&去Xml&调试Mvc

标签列表

    文章归档

      站点信息

      • 文章总数:465
      • 页面总数:0
      • 分类总数:8
      • 标签总数:804
      • 评论总数:0
      • 浏览总数:13467