基于servlet3.0搭建spring mvc应用 无web.xml 无spring boot

发布于:2022-11-09 ⋅ 阅读:(9) ⋅ 点赞:(0) ⋅ 评论:(0)

1、概述

还记得我刚学Java Web的时候,是17年,那时候servlet和jsp还在延续它的辉煌,ssh和ssm仍然是企业潮流,开发工具还是eclipse,有时候一个Tomcat字符集乱码的问题都要解决很久,老师教我们从servlet,到jsp,再到ssh和ssm,可是学到最后,即使我们的项目能跑了,可再让我们手工搭一遍,根本就摸不着头脑,因为配置太多了,我们搞不清楚原理,搞不清楚为什么要这么配置,自然也就记不住。

然后,往往是这些复杂的配置,才能更好地反映我们的基本功扎不扎实,现在的spring boot封装太过完美了,以及有了更强大的开发工具IDEA,几分钟就能搭建出一个能跑的项目来。还记得当年我们都是在web.xml里配置各种servlet,filter,用了spring boot之后,突然一瞬间就再也没碰过web.xml了,以前由于有各种东西要学,其中的猫腻也就没有追究了。

如今的我,已经不是当年的我。本文的目的,不是为了回味当年的servlet、jsp和web.xml的,我会教你如何不用web.xml,去搭建一个spring mvc应用,注意,我不会引入spring boot,也不会引入持久层框架,而是专注于一个最小化的、能跑的spring mvc应用,我会在IDEA中配置Tomcat,然后将应用放到Tomcat中。最终的效果是,我们能在浏览器上访问静态资源,也能在浏览器上访问Controller。

相信我,你会学到很多基础但很重要的东西。

2、Spring MVC请求流程

  1. 请求到达DispatcherServlet
  2. DispatcherServlet会将请求委托给控制器,但控制器会有多个,因此需要先查找一个或多个处理器映射(handler mapping)
  3. 找到控制器,且控制器处理好后,就会将模型与视图(逻辑名,需要解析)返给DispatcherServlet
  4. DispatcherServlet再去调用视图解析器,视图解析器将解析出来的视图返给DispatcherServlet
  5. 最后将视图和模型打包发给前端

3、Spring MVC不需要web.xml的原理

传统方式,servlet会配置在web.xml中,然后打成war包。但是基于servlet3.0规范和3.1增强,就不需要web.xml了。

原理:

  • 在servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话就会用它来配置Servlet容器。Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类会查找实现WebApplicationInitializer的类并将配置的任务交给他们来完成,Spring3.2引入了一个便利的WebApplicationInitializer基础实现,就是AbstractAnnotationConfigDispatcherServletInitializer。
  • 也就是说,AbstractAnnotationConfigDispatcherServletInitializer的任意子类都会自动地配置DispatcherServlet和Spring应用上下文;
  • 继承AbstractAnnotationConfigDispatcherServletInitializer,需要实现三个方法:
    • getServletMappings:将一个或多个路径映射到DispatcherServlet上
    • getRootConfigClasses:用于定义DispatcherServlet应用上下文中的bean
    • getServletConfigClasses:配置ContextLoaderListener创建的上下文中的bean
  • Tomcat7.0以上版本支持servlet3.0

两个上下文的关系:

  • 当DispatcherServlet启动时,它会创建Spring应用上下文,并加载配置文件或配置类中声明的bean
  • 在Spring web环境中,还有一个上下文,这个上下文是由ContextLoaderListener创建的
  • 一般DispatcherServlet用于加载包含web组件的bean,如控制器、视图解析器、处理器映射,而ContextLoaderListener负责加载应用中其它的bean,如dao
  • AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener

4、搭建Spring MVC项目

1、项目整体架构

在这里插入图片描述

2、pom.xml

首先,在IDEA中创建一个Maven项目,完整的pom.xml如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bobo</groupId>
    <artifactId>spring-mvc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <spring.version>5.2.5.RELEASE</spring.version>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <resource.delimiter>@</resource.delimiter>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>${java.version}</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

3、WebAppInitializer

这个类就是上文提到的AbstractAnnotationConfigDispatcherServletInitializer类的子类。

package com.bobo.springmvc.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    @Override
    protected String[] getServletMappings() {
        System.out.println("getServletMappings executed");
        return new String[]{"/"};
    }
}

4、RootConfig

ComponentScan用于扫描项目中的组件。

package com.bobo.springmvc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.bobo.springmvc")
public class RootConfig {
}

5、WebConfig

需要使用@EnableWebMvc启用Spring MVC,并且配置视图解析器。

WEB-INF目录是web应用的安全目录,里面的资源不能被浏览器直接访问。如果资源是放到src/main/web目录下的,是可以被浏览器直接访问的。

package com.bobo.springmvc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.bobo.springmvc"})
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".html");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    /**
     * 配置静态资源的处理.将静态资源的请求转发到servlet容器中的默认servlet,而不是让DispathcerServlet去处理
     * @param configurer
     */
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

6、HelloController

package com.bobo.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {
    @RequestMapping(method = RequestMethod.GET,value = "/toHello")
    public String toHello(){
        System.out.println("请求到达HelloController.toHello方法");
        return "hello";
    }
}

7、web目录

仔细看,src/main/web文件夹中间有一个实心的蓝色圆点,它标志这是一个web目录,这个是怎么生成的呢?

在IDEA的Project Structure->Modules找到对应的项目,然后右键->Add->选择Web,然后在Web Resource Directories新增一个选项,如下图所示。
在这里插入图片描述
首页index.html和hello.html的内容随意,只要最后能被访问到就行。

8、配置Tomcat

下载:https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.83/bin/ ,解压

修改logging.properties,将java.util.logging.ConsoleHandler.encoding设为GBK,否则IDEA控制台会中文乱码。

将项目导出为artifact,如下图所示
在这里插入图片描述
这里有一个Output directory,如下图所示,我们先记住它。
在这里插入图片描述
在IDEA中配置好Tomcat,然后部署项目,如下图所示。
在这里插入图片描述
添加进来后,将下面的路径改为/。其实不改也行,主要是为了方便。
在这里插入图片描述

5、运行测试

一切都准备好后,运行项目,我们发现在Output Directory目录下多了些东西,这是Tomcat启动时生成的。使用tree /F命令,输出如下所示。

F:.
│  index.html
│
├─META-INF
│      MANIFEST.MF
│
└─WEB-INF
    ├─classes
    │  └─com
    │      └─bobo
    │          └─springmvc
    │              ├─config
    │              │      RootConfig.class
    │              │      WebAppInitializer.class
    │              │      WebConfig.class
    │              │
    │              └─controller
    │                      HelloController.class
    │
    ├─lib
    │      spring-aop-5.2.5.RELEASE.jar
    │      spring-beans-5.2.5.RELEASE.jar
    │      spring-context-5.2.5.RELEASE.jar
    │      spring-core-5.2.5.RELEASE.jar
    │      spring-expression-5.2.5.RELEASE.jar
    │      spring-jcl-5.2.5.RELEASE.jar
    │      spring-web-5.2.5.RELEASE.jar
    │      spring-webmvc-5.2.5.RELEASE.jar
    │
    └─views
            hello.html

在浏览器上分别访问首页http://localhost:8080/和Controller http://localhost:8080/toHello 进行测试,如果能成功访问,那就说明大功告成啦!