SpringMvc是一个很优秀的框架,做web开发的基本都会用到。它整个框架的本质其实就是一个servlet。
关于mvc的整体介绍可以参考我之前的另一篇博客
http://blog.csdn.net/z344310362/article/details/51387724

准备环境

需要servlet3.0以上的依赖包。
web容器需要需用支持servlet3.0的(如tomcat7以上)

技术路线

  1. servlet
    mvc的本质是一个servlet,放到mvc的框架里面,核心就是一个DispatcherServlet类。
    这个类的继承关系如下:

    DispatcherServlet->FrameworkServlet->HttpServlet->HttpServlet

    从最后的一个HttpServlet中我们可以看到,虽然它层层隐藏,但是最终就是一个servlet。
  2. mvc核心业务
    mvc的核心业务就是servlet的核心,它主要就是:

    • 初始化业务:init()->initStrategies()
      在初始化中mvc主要是在initStrategies() 这个方法里面完成了9个核心组件的注册。
    • 请求处理:service()->doDispatch()
      在处理请求中mvc的入口是在doDispatch() 方法里面的。这个方法中主要是根据请求匹配到对应的handler,进行具体的业务处理。
  3. servlet注册
    在这个demo中我们用到了servlet3.0以上才有的一个接口ServletContainerInitializer
    继承了这个接口后我们容器启动后会调用这个接口里面提供的onStartup 方法。这样我们的一些初始化动作就可以在里面完成,包括servlet的动态注册。
    我们的DispatcherServlet也就可以在里面注册了(以前注册一个servlet我们是在web.xml里面配置的),在这个demo中我们可以摒弃掉web.xml。

具体实现

  1. 结构预览
    这里写图片描述
    目前只是实现了一个大体的结构,所以很简单。红色矩形的2个类就是这个demo的核心类。

  2. MyContainerInitializer介绍

    @HandlesTypes({WebApplicationInitializer.class})
    public class MyContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        System.out.println("MyContainerInitializer start");
        LinkedList initializers = new LinkedList();
        Iterator var4;
        if(webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();
            while(var4.hasNext()) {
                Class initializer = (Class)var4.next();
                if(!initializer.isInterface() && !Modifier.isAbstract(initializer.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(initializer)) {
                    try {
                        initializers.add((WebApplicationInitializer)initializer.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
            if(initializers.isEmpty()) {
                servletContext.log("No WebApplicationInitializer types detected on classpath");
            } else {
                servletContext.log(initializers.size() + "WebApplicationInitializers detected on classpath");
                var4 = initializers.iterator();
                while(var4.hasNext()) {
                    WebApplicationInitializer initializer1 = (WebApplicationInitializer)var4.next();
                    initializer1.onStartup(servletContext);
                }
    
            }
        }
    }
    }

    这个类实现了ServletContainerInitializer这个接口,所以容器在启动时会调用里面的onStartup 方法。
    类声明的上面有一个@HandlesTypes({WebApplicationInitializer.class})
    它标明了WebApplicationInitializer这个类的子类在启动时会添加到onStartup 方法里面的一个参数Set<Class<?>> webAppInitializerClasses 里面。这样我们通过这个参数就可以对这些类进行调用。

    public interface WebApplicationInitializer {
    void onStartup(ServletContext var1) throws ServletException;
    }
  3. DispatcherServletInitializer介绍
    这个类的主要用途是将DispatcherServlet动态注册到我们的容器里面。

    public class DispatcherServletInitializer implements WebApplicationInitializer{
    public static final String DEFAULT_SERVLET_NAME = "dispatcher";
    @Override
    public void onStartup(ServletContext var1) throws ServletException {
        this.registerDispatcherServlet(var1);
    
    }
    protected void registerDispatcherServlet(ServletContext servletContext) {
        ServletRegistration.Dynamic registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, new DispatcherServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("*.do");
    }
    }

    这个类实现了WebApplicationInitializer 这个接口,所以它在启动的时候会被添加到2中的MyContainerInitializer 里面的onstartup里面。Set<Class<?>>
    这个类里面用到了ServletRegistration.Dynamic 来动态注册servlet。

  4. DispatcherServlet介绍
    看似复杂的springmvc被剥光后就剩下一个DispatcherServlet类。

    /**
    * @Author:zhourj
    * @Description:
    * @Date Create in 12:01 2016/11/13 0013
    */
    public class DispatcherServlet extends FrameworkServlet {
    
    protected void onRefresh() {
        this.initStrategies();
    }
    
    /**
     * 初始化MVC的9个组件
     */
    protected void initStrategies() {
        /*this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);*/
    }
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.doDispatch(request,response);
    }
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("mvc 分配请求");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            // response.setContentLength(responseContent.length());
            writer.write("mvc处理结果");
            writer.flush();
            writer.close();
        } catch (Exception e) {
    
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
    }

    目前我基本没有实现这个类的什么方法,就是展示他的大体结构。
    从3中的代码registration.addMapping("*.do"); 我们知道这个servlet将所有”.do”的请求的包含给自己处理了。所有要让我们的mvc响应到的请求就必需以“.do”结尾。然后具体请求的分配就交给doDispatch 这个方法处理,它具体会利用HandlerMappings组件来帮忙寻找具体的handler。

  5. pom.xml

    <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    </dependencies>
    <build>
    <finalName>test</finalName>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>
    </build>

    这里我只用到了servlet3.1的一个依赖。里面resource的配置是将根路径里面的配置文件拷贝到class里面。这里主要是我用IDEA的一个坑,没自动帮我拷贝进去。
    这里写图片描述
    这里需要注意到的要有这个META-INF,里面要有这个一个文件。这样启动的时候2中的MyContainerInitializer onstartup方法才能进去。这是servlet中规定的。


本文章主要是个人学习归纳
源代码下载:http://download.csdn.net/detail/z344310362/9694233

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐