• WebApplicationInitializer究 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用


    本文转自http://hitmit1314.iteye.com/blog/1315816

    大家应该都已经知道Spring 3.1对无web.xml式基于代码配置的servlet3.0应用。通过spring的api或是网络上高手们的博文,也一定很快就学会并且加到自己的应用中去了。PS:如果还没,也可以小小参考一下鄙人的上一篇文章<<探 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用>>。 

            经过一天的深度research, 我了解,理解以及重现了springframework的那一小段代码。 

            OK,第一步,入手点,WebApplicationInitializer接口。因为我们只需实现这个接口覆写它的一个方法,就可以做到配置web.xml同样的功效。看它的源码,其实看和不看没什么两样: 

    Java代码  收藏代码
    1. package org.springframework.web;  
    2.   
    3. import javax.servlet.ServletContext;  
    4. import javax.servlet.ServletException;  
    5. public interface WebApplicationInitializer {  
    6.     void onStartup(ServletContext servletContext) throws ServletException;  
    7. }  


       就这么点儿,有效代码5行,弄地我一头雾水,就是一个普通接口,声明了一个方法。连注解都没有,server是怎么找到实现了它的类的?如果这样,何不找我定义的其它接口(的实现类完成配置工作)呢。可见现在java的解耦技术,真令人汗颜。

     第二步,这个接口旁边(同包)有个SpringServletContainerInitializer, 看下它是何方神圣吧: 

    Java代码  收藏代码
    1. package org.springframework.web;  
    2.   
    3. import java.lang.reflect.Modifier;  
    4. import java.util.Collections;  
    5. import java.util.LinkedList;  
    6. import java.util.List;  
    7. import java.util.ServiceLoader;  
    8. import java.util.Set;  
    9. import javax.servlet.ServletContainerInitializer;  
    10. import javax.servlet.ServletContext;  
    11. import javax.servlet.ServletException;  
    12. import javax.servlet.annotation.HandlesTypes;  
    13.   
    14. import org.springframework.core.annotation.AnnotationAwareOrderComparator;  
    15.   
    16. @HandlesTypes(WebApplicationInitializer.class)  
    17. public class SpringServletContainerInitializer implements ServletContainerInitializer {  
    18. public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)  
    19.             throws ServletException {  
    20.   
    21.         List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();  
    22.         if (webAppInitializerClasses != null) {  
    23.             for (Class<?> waiClass : webAppInitializerClasses) {  
    24.                 // Be defensive: Some servlet containers provide us with invalid classes,  
    25.                 // no matter what @HandlesTypes says...  
    26.                 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&             WebApplicationInitializer.class.isAssignableFrom(waiClass)) {  
    27.                     try {  
    28.                         initializers.add((WebApplicationInitializer) waiClass.newInstance());  
    29.                     }  
    30.                     catch (Throwable ex) {  
    31.                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);  
    32.                     }  
    33.                 }  
    34.             }  
    35.         }  
    36.   
    37.         if (initializers.isEmpty()) {  
    38.             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");  
    39.             return;  
    40.         }  
    41.   
    42.         Collections.sort(initializers, new AnnotationAwareOrderComparator());  
    43.         servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);  
    44.   
    45.         for (WebApplicationInitializer initializer : initializers) {  
    46.             initializer.onStartup(servletContext);  
    47.         }  
    48.     }  
    49.   
    50. }  



         以上的有效代码28行。刚看时也很迷茫,其实慢慢就理解了。拟个伪代码吧,方便大家理解: 
          1,定义一个类SpringServletContainerInitializer,并标明该类要操作的一个类WebApplicationInitializer 
          2, 该类会行使ServletContainerInitializer接口的一个行为onStartup,从而将一个集合中的初始化设置 全部配置到ServletContext的实例中。 
          3,具体的onStartup方法中,建立合格配置列表, 
          4,如果确定集合中有配置,逐一检查配置是否是合格配置,具体判断依据:这个类不是接口,不是抽象类,而且是所要操作的那个接口的一个实现类。满足此依据,合格。将合格的配置类实例化放入合格配置列表。过程中有错要通知控制台。 
         5,如若执行完步骤4,发现没有合格配置,在ServletContext记录该结果,并结束onStartup行为。 
         6,将找到配置按一定排列方式(AnnotationAwareOrder)排序。 
         7,在ServletContext中记录找到结果。 
         8,逐一执行配置。 即驱动每一个WebApplicationInitializer的实现类行使其onStartup行为。 

         第三步很明显了,去research 接口ServletContainerInitializer和注解HandleType。在这里:http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContainerInitializer.html 

        该接口允许一个库或运行时,(运行时应该指server)声明为一个web程序的启动状态,并执行任何所需的程序中注册的servlet,filter,listener来响应它...... 
         其它也就不用看了,可以想象得到支持Servlet3机制的服务器,会找到这样接口的实现类,执行onStartup行为。至于如何找,无非也是这样一系列的反射机制的应用。自己做一个试试吧: 
         自定义的WebApplicationInitializer: 

    Java代码  收藏代码
    1. package com.gxino.imagecapture.cfg;  
    2.   
    3. import javax.servlet.ServletContext;  
    4. import javax.servlet.ServletException;  
    5.   
    6. public interface WebParameter {  
    7.     public void loadInfo(ServletContext servletContext) throws ServletException;  
    8. }  



         自定义的ServletContainerInitializer,我做得很简单,直接去执行找到配置类中的loadInfo方法 

    Java代码  收藏代码
    1. package com.gxino.imagecapture.cfg;  
    2.   
    3. import java.lang.reflect.Modifier;  
    4. import java.util.Set;  
    5.   
    6. import javax.servlet.ServletContainerInitializer;  
    7. import javax.servlet.ServletContext;  
    8. import javax.servlet.ServletException;  
    9. import javax.servlet.annotation.HandlesTypes;  
    10.   
    11. @HandlesTypes(WebParameter.class)  
    12. public class WebConfiguration implements ServletContainerInitializer {  
    13.   
    14.     @Override  
    15.     public void onStartup(Set<Class<?>> webParams, ServletContext servletCtx)  
    16.             throws ServletException {  
    17.         if (webParams != null) {  
    18.             for (Class<?> paramClass : webParams) {  
    19.                 if (!paramClass.isInterface() && !Modifier.isAbstract(paramClass.getModifiers()) &&  
    20.                         WebParameter.class.isAssignableFrom(paramClass)) {  
    21.                     try {  
    22.                         ((WebParameter) paramClass.newInstance()).loadInfo(servletCtx);  
    23.                     }  
    24.                     catch (Throwable ex) {  
    25.                         throw new ServletException("Failed to instantiate WebParam class", ex);  
    26.                     }  
    27.                 }  
    28.             }//loop  
    29.         }//Web Params  
    30.     }//onStartup  
    31.   
    32. }  


          写个测试Servlet: 

    Java代码  收藏代码
    1. package com.gxino.imagecapture.ctrl;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import javax.servlet.ServletException;  
    6. import javax.servlet.http.HttpServlet;  
    7. import javax.servlet.http.HttpServletRequest;  
    8. import javax.servlet.http.HttpServletResponse;  
    9.   
    10. import com.gxino.imagecapture.cfg.WebParameter;  
    11.   
    12. public class TestServlet extends HttpServlet {  
    13.       
    14.     public void doGet(HttpServletRequest req, HttpServletResponse resp){  
    15.         System.out.println("Some client access once");  
    16.         try {  
    17.             req.getRequestDispatcher("/index.jsp").forward(req, resp);  
    18.         } catch (ServletException | IOException e) {  
    19.             // TODO Auto-generated catch block  
    20.             e.printStackTrace();  
    21.         }  
    22.     }  
    23.       
    24. }  



           实现WebParam配置接口来配置刚才的Servlet: 

    Java代码  收藏代码
    1. package com.gxino.imagecapture.cfg;  
    2.   
    3. import javax.servlet.ServletContext;  
    4. import javax.servlet.ServletException;  
    5. import javax.servlet.ServletRegistration;  
    6.   
    7.   
    8. public class ServletParameter implements WebParameter {  
    9.   
    10.     @Override  
    11.     public void loadInfo(ServletContext servletContext) throws ServletException {  
    12.         ServletRegistration.Dynamic testServlet=servletContext.addServlet("test","com.gxino.imagecapture.ctrl.TestServlet");  
    13.         testServlet.setLoadOnStartup(1);  
    14.         testServlet.addMapping("/index.html");  
    15.     }  
    16.   
    17. }  


         启动服务器,访问http://localhost:xxxx/xxxxx/index.html 
      
         失败。Debug. 发现没有走这些代码。应该还差关键环节。看来还得知道Servlet3中是怎么找ServletContainerInitializer的。再回刚才ServletContainerInitializer的api有这样一句:该接口的实现必须声明一个JAR资源放到程序中的META-INF/services下,并且记有该接口那个实现类的全路径,才会被运行时(server)的查找机制或是其它特定机制找到。那篇api需要仔细阅读啊。 
         到org.springframework.web-3.0.1.RELEASE.jar中能找到META-INF/services下的javax.servlet.ServletContainerInitializer文件,内容为org.springframework.web.SpringServletContainerInitializer同样,我们专门作这样一个包,在mkdir好的META-INF/services下vi 一个文件命名为javax.servlet.ServletContainerInitializer,内容为自定的那个WebConfiguration的全路径类名。 然后在META-INF的parent路径下运行jar cvf test.jar META-INF。一切完毕,将其放到WEB-INF/lib下。启动。 
         
         这回大功告成。 
         
         访问http://localhost:xxxx/xxxxx/index.html。页面跳到了index.jsp下。 
         并且控制台打出: Some client access once 

         再使个劲,将Servlet和Servlet配置合二为一: 

    Java代码  收藏代码
    1. package com.gxino.imagecapture.ctrl;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import javax.servlet.ServletContext;  
    6. import javax.servlet.ServletException;  
    7. import javax.servlet.ServletRegistration;  
    8. import javax.servlet.http.HttpServlet;  
    9. import javax.servlet.http.HttpServletRequest;  
    10. import javax.servlet.http.HttpServletResponse;  
    11.   
    12. import com.gxino.imagecapture.cfg.WebParameter;  
    13.   
    14. public class TestServlet extends HttpServlet implements WebParameter{  
    15.   
    16.     @Override  
    17.     public void loadInfo(ServletContext servletContext) throws ServletException {  
    18.         ServletRegistration.Dynamic testServlet=servletContext.addServlet("test", "com.gxino.imagecapture.ctrl.TestServlet");  
    19.         testServlet.setLoadOnStartup(1);  
    20.         testServlet.addMapping("/index.html");  
    21.     }  
    22.     public void doGet(HttpServletRequest req, HttpServletResponse resp){  
    23.         System.out.println("Some client access once");  
    24.         try {  
    25.             req.getRequestDispatcher("/index.jsp").forward(req, resp);  
    26.         } catch (ServletException | IOException e) {  
    27.             // TODO Auto-generated catch block  
    28.             e.printStackTrace();  
    29.         }  
    30.     }  
    31.       
    32. }  



    这回我们看到,配置文件与servlet放到了一起。这样将回节省大量时间。 

        以后直接运用Spring Framework的WebApplicationInitializer也知道是怎么一回事儿了。而且可以将Spring 的applicationContext.xml与web.xml融合在一个类中。即注解为@Configuration,并实现WebApplicationInitializer.回头试试。

  • 相关阅读:
    关于新人的培养与程序的测试
    关于23种设计模式的有趣见解
    看足球学习管理团队
    《Effective C#》Item 1
    测试系列之五回归测试
    VS2005 VS2008新建网站和新建项目里选Web应用程序区别
    SaaS的研究
    DropDownList控件选中项的深入研究
    用ie9浏览器若出现看视频有声音没图像的问题处理
    zencart目录页出现c.html网址的解决方法
  • 原文地址:https://www.cnblogs.com/panxuejun/p/7090919.html
Copyright © 2020-2023  润新知