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

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

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

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

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

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

以上的有效代码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:

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

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

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

写个测试Servlet:

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

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

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

启动服务器,访问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配置合二为一:

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

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

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

servlet3.0无web.xml的更多相关文章

  1. DispatcherServlet和ContextLoaderListener,还有spring+servlet3.0 无web.xml启动问题

    上篇提到: 关于spring +springmvc中两个spring应用上下文(DispatcherServlet和ContextLoaderListener)的问题,挺让人迷糊的. 他们都是加载Be ...

  2. springmvc学习指南 之---第27篇 spring如何实现servlet3.0无web.xml 配置servlet对象的

    writedby 张艳涛 基于web.xml配置,有人说麻烦,tomcat给按照servlet3.0,实现了基于注解@WebServlet,有人说springmvc的springmvc.xml配置麻烦 ...

  3. servlet3.0,web.xml的metadata-complete的作用

    metadata-complete是servlet3.0规范中的新增的属性,该属性接受两个属性值,true或false.当该属性值为true时,该web应用将不会加载Annotation配置的web组 ...

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

    本文转自http://hitmit1314.iteye.com/blog/1315816 大家应该都已经知道Spring 3.1对无web.xml式基于代码配置的servlet3.0应用.通过spri ...

  5. SpringMVC基于代码的配置方式(零配置,无web.xml)直接继承WebMvcConfigurerAdapter

    基于配置文件的web项目维护起来可能会更方便,但是有时候我们会有一些特殊的需求,比如防止客户胡乱更改配置,这时候我们需要给配置隐藏到代码中. 1.创建一个动态web项目(无需web.xml) 2.右键 ...

  6. SpringMVC基于代码的配置方式(零配置,无web.xml)

    基于配置文件的web项目维护起来可能会更方便,可是有时候我们会有一些特殊的需求,比方防止客户胡乱更改配置,这时候我们须要给配置隐藏到代码中. 1.创建一个动态web项目(无需web.xml) 2.右键 ...

  7. SpringMVC 零配置 无web.xml

    对SpringMVC启动流程的讲解 https://www.cnblogs.com/beiyan/p/5942741.html 与SpringMVC的整合 https://hanqunfeng.ite ...

  8. web.xml 中以编码方式添加filter并设置初始化参数AbstractAnnotationConfigDispatchServletInitializer

    web.xml中配置filter <?xml version="1.0" encoding="UTF-8"?> <web-app versio ...

  9. servlet3.0 JQuary Ajax基本使用

    servlet3.0 没有web.xml文件,需要使用注解进行配置. js: $(document).ready(function(){ $("#btn").click(funct ...

随机推荐

  1. 错误:SyntaxError: identifier starts immediately after numeric literal

    转载:http://blog.csdn.net/shalousun/article/details/39995443在用JavaScript时,当你使用一个字符传作为函数的参数常常会看到语法错误,在f ...

  2. vue运行碰到的问题

    Expected indentation of 0 spaces but found 2 解决方案: 文件中加入"indent": ["off", 2]就可以了 ...

  3. centos查看实时网络带宽占用情况方法【转】

    Linux中查看网卡流量工具有iptraf.iftop以及nethogs等,iftop可以用来监控网卡的实时流量(可以指定网段).反向解析IP.显示端口信息等. centos安装iftop的命令如下: ...

  4. 获取图书isbn信息 共享图书开发 图书信息接口开发过程中的心得体会

    最近做一个图书共享的项目,需要用户扫一扫书籍后面的一维码,获取到书籍的isbn号码,然后通过这个isbn号码能够直接获取到这本书的名字.简介.价格.图片等信息. 于是百度搜了下,之前很多的豆瓣的接口, ...

  5. postgres外部表

    在创建外部表的时候遇见: CREATE EXTENSION file_fdw;2018-12-21 17:32:23.822 CST [31237] ERROR:  could not open ex ...

  6. springboot(2)-阶段篇

    web开发 spring boot web开发非常的简单,其中包括常用的json输出.filters.property.log等 json 接口开发 在以前的spring 开发的时候需要我们提供jso ...

  7. 15、Nginx动静分离实战

    1.Nginx动静分离基本概述 动静分离, 通过中间件将动静分离和静态请求进行分离. 那为什么要通过中间件将动态请求和静态请求进行分离? 减少不必要的请求消耗, 同时能减少请求的延时. 通过中间件将动 ...

  8. 使用QEMU模拟树莓派

    QEMU上的树莓派 我们开始设置一个Lab VM.我们将使用Ubuntu并在其中模拟我们所需的ARM版本. 首先,获取最新的Ubuntu版本并在VM中运行它: https://www.ubuntu.c ...

  9. vs2017的主题颜色的配置

    相信很多小伙伴开发的时候很怀念sublime的主题,我也特别的喜欢其中的mono主题,所以闲来无事在vs上调了一下色感觉好看多了.(其实也可以下载主题然后用“导入导出设置但是颜色有点奇葩,还是越简单越 ...

  10. Centos 7搭建Gitlab服务器以及操作(创建项目,创建群组,创建用户,添加密钥)

    一. 安装并配置依赖包 在CentOS系统上安装所需的依赖:ssh,防火墙,postfix(用于邮件通知) ,wget,以下这些命令也会打开 系统防火墙中的HTTP和SSH端口访问 安装前准备 命令: ...