zoukankan      html  css  js  c++  java
  • DispatcherServlet和ContextLoaderListener,还有spring+servlet3.0 无web.xml启动问题

    上篇提到:

    关于spring +springmvc中两个spring应用上下文(DispatcherServlet和ContextLoaderListener)的问题,挺让人迷糊的。

    他们都是加载Bean。简单粗暴的理解就是spring的bean 用ContextLoaderListener加载,springmvc的用DispatcherServlet 加载。

    《spring in action》一书中给了点解释,【我们希望DispatcherServlet 加载包含Web组件的bean,如控制器,视图解析器及处理映射,而ContextLoaderListener需要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件】。

    按道理说,反正都是bean的配置,所有的配置都 配置到一起也是可以的? 其实不然。

    参看了网上几篇博客,解释的有的还算正常,有的就是可能是猜测的,当然也可能是spring版本不同导致的。

    【Spring启动过程分析】(1)启动流程简介  http://blog.csdn.net/moshenglv/article/details/53517343

    DispatcherServlet与ContextLoaderListener的对比 http://blog.csdn.net/sadfishsc/article/details/51027873

    SpringMVC DispatcherServlet 初始化过程 http://blog.csdn.net/sadfishsc/article/details/51027809

    SpringWeb ContextLoaderListener 初始化过程 http://blog.csdn.net/sadfishsc/article/details/51027840

    Servlet 3 + Spring MVC零配置:去除所有xml http://blog.csdn.net/xiao__gui/article/details/46803193

    如果想真正深入了解,估计得看源码了。

    个人觉得,还是分开好。否则可能引起不必要的错误。 那如果两个都配置了,都加载了所有的bean呢,会不会有bean重复加载的问题?

    个人亲自写代码实验了一下,

    1. 配置web.xml方式 启动

    为方便 ,先把普通的bean配置(service层,domain层 ,dao层,数据库等)成为application.xml, springmvc相关的bean配置(controller层,视图解析器等),称为springmvc.xml,

    springmvc.xml中配置是:

      <mvc:annotation-driven /> 
       <mvc:default-servlet-handler/>
       <mvc:resources location="/WEB-INF/pages/" mapping="/pages/**"/>  
       <!-- 扫描controller(controller层注入) -->
       <context:component-scan base-package="com.zj.controller"/> 
       <!-- 对模型视图添加前后缀 -->
       <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/pages/" p:suffix=".jsp"/>
        

    如果正常配置,应该是:

    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml</param-value>
        </context-param>
    <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    <!-- springMVC核心配置 -->
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc/springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
       
        <!-- 拦截设置 -->
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>

    UserServiceImpl-------------------------------  和 UserController==================== 是我写在构造函数中的System.out.println 输出的。

    如果配置成这样呢:把正常的springmvc.xml 和 application.xml 放一起,让ContextLoaderListener 加载。DispatcherServlet 加载一个空的springmvc配置springmvc-empty.xml,

     <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml,classpath:springmvc/springmvc.xml</param-value>
        </context-param>
       <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc/springmvc-empty.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>

    结果发现,还是可以正常加载controller的bean以及 视图解析等,只是controller Bean加载的顺序变提前了。。不过 应用访问 和正常情况没啥两样。

    所以,不像网上有些人说的controller 、视图解析器 一定要DispatcherServlet加载。 不过拦截器等东西,我没试验。

    ================

    如果 下面这样配置呢:

        <!-- 读取spring配置文件 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml,classpath:springmvc/springmvc.xml</param-value>
        </context-param>
    
     <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc/springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet

    可以发现 controller的bean确实加载了两遍。

    不过web应用可以正常使用。

    同理,如果把application.xml 也在DispatcherServlet里在配置一遍,那service等bean也是加载两遍。

    ===========

    结论:配置web.xml 方式启动,最好还是最好把application.xml 配置 和 springmvc.xml 配置分开,分别用ContextLoadListener加载  和 DispatcherServlet加载。

    2. spring +servlet3.0 无web.xml方式启动

    我用的是继承WebApplicationInitializer ,通过OnStartup 启动的方式。

    我用的Tomact 7.0环境,发现如果web.xml 中存在Servlet的那些配置,就不会按照WebApplicationInitializer 启动,除非web.xml 中不要Servlet的那些配置,几乎一个空的web.xml。

    这个没有深究,估计和 Servlet3.0启动方式 顺序有关吧。

    第一种写法:

    public class MyWebAppInitializer implements WebApplicationInitializer {
    
        public void onStartup(ServletContext servletContext) throws ServletException {
            System.out.println("startUP=++++=+++++++++++++++++++++++++++++");
             servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml,classpath:springmvc/springmvc.xml");  
            // servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml"); 
            ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet());  
                
            registration.setLoadOnStartup(1);  
            //registration.addMapping("/");  
            registration.addMapping(new String[]{"*.html"});  
            registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml");  
            servletContext.addListener(new ContextLoaderListener( ));
          
            
        }
    }


    运行结果:

    结果:可以正常访问。 不过 第一次访问的时候,会再出现 一次UserController Bean的初始化。

    代码改一下, servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml,classpath:springmvc/springmvc.xml"); 

    改成  servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml"); 

    结果:可以正常访问。 UserController的bean 第一次访问的时候才 生成,有点懒加载的意思。

    =========

    如果  servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml,classpath:springmvc/springmvc.xml"); 

    把 //registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml");   注释掉。

    会出现什么情况呢。


         结果:可以正常启动。 但是 不能正常访问。 DispatcherServlet 会找默认的  配置。 找不到 ,就发飙了。

    第二种写法:

    public void onStartup(ServletContext servletContext) throws ServletException {
            System.out.println("startUP=++++=+++++++++++++++++++++++++++++");
            // servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml,classpath:springmvc/springmvc.xml");  
            // servletContext.addListener(new ContextLoaderListener()); 
            // AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();  
            XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
            List<String> locationList = new ArrayList<String>();
            locationList.add("classpath:application.xml");
            //locationList.add("classpath:springmvc/springmvc.xml");
            rootContext.setConfigLocations((String[])locationList.toArray(new String[locationList.size()]));
    //        ContextLoader contextLoader = new ContextLoader(rootContext);
    //        contextLoader.initWebApplicationContext(servletContext);
            servletContext.addListener(new ContextLoaderListener( rootContext));
                
            //AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();  
           // webContext.register(WebConfig.class);
            ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet());  
                
            registration.setLoadOnStartup(1);  
            //registration.addMapping("/");  
            registration.addMapping(new String[]{"*.html"});  
            registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml");  
            //servletContext.addListener(new ContextLoaderListener( ));
          
            
        }

    结果:

    信息: Spring WebApplicationInitializers detected on classpath: [com.zj.controller.MyWebAppInitializer@5794495e]
    startUP=++++=+++++++++++++++++++++++++++++
    log4j:WARN No appenders could be found for logger (org.springframework.web.context.support.StandardServletEnvironment).
    log4j:WARN Please initialize the log4j system properly.
    四月 13, 2017 1:38:20 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring root WebApplicationContext
    UserServiceImpl------------------------调用初始化方法....
    四月 13, 2017 1:38:21 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["http-bio-8080"]
    四月 13, 2017 1:38:21 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["ajp-bio-8009"]
    四月 13, 2017 1:38:21 下午 org.apache.catalina.startup.Catalina start
    信息: Server startup in 4462 ms
    四月 13, 2017 1:38:31 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring FrameworkServlet 'dispatcher'
    UserController=====================
    3333333333333333333333333333

    结果发现: 可以正常访问,第一次访问加载UserController bean

    如果 : ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet());  
             改成 ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));  

    结果:可以正常启动,但是无法正常访问。提示 The requested resource () is not available.,不知道咋回事儿。

    =============

    如果代码改成这样:

    public void onStartup(ServletContext servletContext) throws ServletException {
            System.out.println("startUP=++++=+++++++++++++++++++++++++++++");
            // servletContext.setInitParameter("contextConfigLocation", "classpath:application.xml,classpath:springmvc/springmvc.xml");  
            // servletContext.addListener(new ContextLoaderListener()); 
            // AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();  
            XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
            List<String> locationList = new ArrayList<String>();
            locationList.add("classpath:application.xml");
            locationList.add("classpath:springmvc/springmvc.xml");
            rootContext.setConfigLocations((String[])locationList.toArray(new String[locationList.size()]));
    //        ContextLoader contextLoader = new ContextLoader(rootContext);
    //        contextLoader.initWebApplicationContext(servletContext);
            servletContext.addListener(new ContextLoaderListener( rootContext));
                
            //AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();  
           // webContext.register(WebConfig.class);
            ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));  
                
            registration.setLoadOnStartup(1);  
            //registration.addMapping("/");  
            registration.addMapping(new String[]{"*.html"});  
            //registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml");  
            //servletContext.addListener(new ContextLoaderListener( ));
          
            
        }

    结果:正常启动,可以正常访问。controller bean提前加载。

    信息: Spring WebApplicationInitializers detected on classpath: [com.zj.controller.MyWebAppInitializer@6710bb13]
    startUP=++++=+++++++++++++++++++++++++++++
    log4j:WARN No appenders could be found for logger (org.springframework.web.context.support.StandardServletEnvironment).
    log4j:WARN Please initialize the log4j system properly.
    四月 13, 2017 1:48:46 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring root WebApplicationContext
    UserServiceImpl------------------------调用初始化方法....
    UserController=====================
    四月 13, 2017 1:48:47 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["http-bio-8080"]
    四月 13, 2017 1:48:47 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["ajp-bio-8009"]
    四月 13, 2017 1:48:47 下午 org.apache.catalina.startup.Catalina start
    信息: Server startup in 4797 ms
    四月 13, 2017 1:49:02 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring FrameworkServlet 'dispatcher'
    3333333333333333333333333333
    //代码也可以改成这样,自己写 ContextLoader  加载bean
    public
    void onStartup(ServletContext servletContext) throws ServletException { System.out.println("startUP=++++=+++++++++++++++++++++++++++++"); XmlWebApplicationContext rootContext = new XmlWebApplicationContext(); List<String> locationList = new ArrayList<String>(); locationList.add("classpath:application.xml"); locationList.add("classpath:springmvc/springmvc.xml"); rootContext.setConfigLocations((String[])locationList.toArray(new String[locationList.size()])); ContextLoader contextLoader = new ContextLoader(rootContext); contextLoader.initWebApplicationContext(servletContext); //servletContext.addListener(new ContextLoaderListener( rootContext)); ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext)); registration.setLoadOnStartup(1); //registration.addMapping("/"); registration.addMapping(new String[]{"*.html"}); //registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml"); //servletContext.addListener(new ContextLoaderListener( )); }

    =============

    或者采用注解方式。这里只给mvc配置采用了注解。

    @Configuration  
    @EnableWebMvc  
    @ComponentScan(basePackages = "com.zj.controller")  
    public class WebConfig {  
          
        @Bean  
        public InternalResourceViewResolver internalResourceViewResolver() {  
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();  
            viewResolver.setPrefix("/WEB-INF/pages/");  
            viewResolver.setSuffix(".jsp");  
            return viewResolver;  
        }  
        
        public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
          }
    }
        public void onStartup(ServletContext servletContext) throws ServletException {
            System.out.println("startUP=++++=+++++++++++++++++++++++++++++");
        
            XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
            List<String> locationList = new ArrayList<String>();
            locationList.add("classpath:application.xml");
            //locationList.add("classpath:springmvc/springmvc.xml");
            rootContext.setConfigLocations((String[])locationList.toArray(new String[locationList.size()]));
            ContextLoader contextLoader = new ContextLoader(rootContext);
            contextLoader.initWebApplicationContext(servletContext);
             
            AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();  
            webContext.register(WebConfig.class);
            ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new DispatcherServlet(webContext));  
                
            registration.setLoadOnStartup(1);  
            //registration.addMapping("/");  
            registration.addMapping(new String[]{"*.html"});  
            //registration.setInitParameter("contextConfigLocation", "classpath:springmvc/springmvc.xml");  
            //servletContext.addListener(new ContextLoaderListener( ));
          
            
        }

    结果: 正常启动,正常访问。controller bean 延迟加载。

    信息: Spring WebApplicationInitializers detected on classpath: [com.zj.controller.MyWebAppInitializer@5a278fe0]
    startUP=++++=+++++++++++++++++++++++++++++
    log4j:WARN No appenders could be found for logger (org.springframework.web.context.support.StandardServletEnvironment).
    log4j:WARN Please initialize the log4j system properly.
    四月 13, 2017 1:58:09 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring root WebApplicationContext
    UserServiceImpl------------------------调用初始化方法....
    四月 13, 2017 1:58:09 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["http-bio-8080"]
    四月 13, 2017 1:58:09 下午 org.apache.coyote.AbstractProtocolHandler start
    信息: Starting ProtocolHandler ["ajp-bio-8009"]
    四月 13, 2017 1:58:09 下午 org.apache.catalina.startup.Catalina start
    信息: Server startup in 4644 ms
    四月 13, 2017 1:58:18 下午 org.apache.catalina.core.ApplicationContext log
    信息: Initializing Spring FrameworkServlet 'dispatcher'
    UserController=====================
    3333333333333333333333333333

    综上: springmvc配置 和 普通的bean的配置 分开加载,可以延迟加载controller 的Bean。

    不过我感觉很奇怪,<load-on-startup>1</load-on-startup>应该是启动时候立即加载。 0 及0 以上应该是容器启动时候加载,负数为懒加载,

    在web.xml起作用,但是在代码方式registration.setLoadOnStartup(1) 为啥还是第一次访问才加载呢? 是我版本问题,还是代码问题呢?


            

  • 相关阅读:
    Google Chrome 默认非安全端口列表
    js判断类型的方法
    博客园样式排版自定义
    easyloader.js源代码分析
    JQuery操作cookies
    js获取iframe里面的dom
    封装GetQueryString()方法来获取URL的value值
    js 获取系统时间:年月日 星期 时分秒(动态)
    vue 滚动加载数据
    props 父组件给子组件传递参数
  • 原文地址:https://www.cnblogs.com/aji2014/p/6702365.html
Copyright © 2011-2022 走看看