转载

一个最基础的 Spring 4 MVC Maven 项目

这是一个最基本的 Spring 4 MVC 的 Maven 项目,非 SpringBoot 的,SpringBoot 由于有许多自动化配置特性,会更简单些。本例全部用 Java 代码作为配置,免除了创建 web.xml 和如 dispatcher-servlet.xml 这样的文件。本人更倾向于 Java 配置,它的优势在于能进行编译期检查,逻辑性也强,配置文件只是改动无需重新编译,都是要重启服务的; 关于使用 XML 配置文件的方式可参考文后的链接。

本文侧重于 Spring MVC 项目提供 RESTful JSON API, 因而静态 Web 内容提及较少。创建一个 Maven 项目的方式,可以直接创建一个 pom.xml 文件,然后编辑它的内容,使用 IntelliJ IDEA 的话只需要选择导入为一个 Maven 项目就成,Eclipse 的话可能还要事先运行 mvn eclipse:eclipse 初始化一下。

项目结构布局

一个最基础的 Spring 4 MVC Maven 项目

就是一个普通的 Maven 项目,稍稍不同的是 src/main 目录中除了 java 和 resources 之外,还有 webapp 目录,用于存放 web 静态文件或模板文件的。

pom.xml 文件内容

<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/maven-v4_0_0.xsd">
 
    <modelVersion>4.0.0</modelVersion>
    <groupId>demo</groupId>
    <artifactId>springmvc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
 
    <properties>
        <jdk.version>1.8</jdk.version>
        <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
 
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 
        <spring.version>4.3.16.RELEASE</spring.version>
        <maven-war-plugin.version>2.4</maven-war-plugin.version>
        <tomcat7.version>2.2</tomcat7.version>
        <servlet.version>3.0.1</servlet.version>
        <jackson.version>2.9.5</jackson.version>
    </properties>
 
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
 
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
 
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>${jdk.version}</source>
                    <target>${jdk.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>${maven-war-plugin.version}</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
 
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>${tomcat7.version}</version>
                <configuration>
                    <path>/springmvc</path>
                    <contextReloadable>true</contextReloadable>
                </configuration>
            </plugin>
 
        </plugins>
    </build>
</project>

pom.xml 中我去除了关于 slfj, logback 相关的依赖, 也不在本文的重点。

  • maven-war-plugin 用于创建可发布的 war 包
  • tomcat7-maven-plugin 用于测试项目,它的 contextReloadable  属性可在类改动重新编译后使 Spring 重新启动

在 IntelliJ IDEA 中如果想要修改 Java 类后自动重启 Spring 上下文的话,有两种方式

  1. 启用自动编译功能,Preferences/Build, Execution, Deployment/Compiler, 勾选上 Build project automatically
  2. 类修改后,从 Build 菜单中,选 Recompile... , Build Project , Build Module , Rebuild Project  都会触发 Spring 上下文重启。

WebInitializer 类

public class WebInitializer implements WebApplicationInitializer {
 
    private static final Logger logger = LoggerFactory.getLogger(WebInitializer.class);
 
    @Override
    public void onStartup(ServletContext container) throws ServletException {
        logger.info("Starting container......");
 
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(WebAppConfig.class);
        context.setServletContext(container);
        context.refresh();
 
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
 
        FilterRegistration.Dynamic filter = container.addFilter("encoding", CharacterEncodingFilter.class);
        filter.setInitParameter("encoding", "UTF-8");
        filter.addMappingForServletNames(EnumSet.allOf(DispatcherType.class), true, "dispatcher");
    }
}

这是 Seervlet 3 才有的特性,在 Spring-Web 项目中有一个实现了 Servlet3 ServletContainerInitializer 接口的类 SpringServletContainerInitializer , 它会在 Spring 容器启动时扫描 WebApplicationInitializer 的所有实现类,并调用它们的 onStartup 文件。由此才实现了免 web.xml 文件配置 servlet 的方式。

并且这个 WebInitializer 把 Spring 的上下文 AnnotationConfigWebApplicationContext 与 Servlet 容器上下文 ServletContext 关联起来了。注册了 Spring 本身的 Java 配置 WebAppConfig 类。创建了 /dispatcher servlet 的映射。

WebAppConfig 类

@EnableWebMvc
@ComponentScan(basePackages = {"controller"})
public class WebAppConfig {
 
}

这个类对于当前实例来说没多少内容,主要就是 EnableWebMvc 启用 SpringMVC 特性,并指示 Spring 上下文扫描 controller 包。因为该例只用一个 controller 来进行演示,未涉及到 service, model 等内容。

UserController 类

package controller;
 
//省略了 import 部分
 
@RestController
@RequestMapping("/users")
public class UserController {
 
    @RequestMapping(value="/{userId}", method=RequestMethod.GET)
    public Map<String, Object> getUserInfo(@PathVariable("userId") Integer userId) {
        return ImmutableMap.of("UserId", userId, "Name", "Yanbin");
    }
}

此处注解没什么好说的,普通的 Spring RESTful 的 controller 的注解

测试运行 tomcat

在项目目录下,执行 mvn tomcat7:run 命令

[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ springmvc ---  [INFO] Running war on http://localhost:8080/springmvc  [INFO] Creating Tomcat server configuration at /Users/yanbin/workspace/springmvc/target/tomcat  [INFO] create webapp with contextPath: /springmvc  Apr 30, 2018 7:54:04 AM org.apache.coyote.AbstractProtocol init  INFO: Initializing ProtocolHandler ["http-bio-8080"]  Apr 30, 2018 7:54:04 AM org.apache.catalina.core.StandardService startInternal  INFO: Starting service Tomcat  Apr 30, 2018 7:54:04 AM org.apache.catalina.core.StandardEngine startInternal  INFO: Starting Servlet Engine: Apache Tomcat/7.0.47  Apr 30, 2018 7:54:06 AM org.apache.catalina.core.ApplicationContext log  INFO: 1 Spring WebApplicationInitializers detected on classpath  2018-04-30 07:54:06 [localhost-startStop-1] INFO WebInitializer - Starting container......  2018-04-30 07:54:06 [localhost-startStop-1] INFO AnnotationConfigWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Mon Apr 30 07:54:06 CDT 2018]; root of context hierarchy  2018-04-30 07:54:06 [localhost-startStop-1] INFO AnnotationConfigWebApplicationContext - Registering annotated classes: [class config.WebAppConfig]  2018-04-30 07:54:06 [localhost-startStop-1] INFO AutowiredAnnotationBeanPostProcessor - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring  2018-04-30 07:54:07 [localhost-startStop-1] INFO RequestMappingHandlerMapping - Mapped "{[/users/{userId}],methods=[GET]}" onto public java.util.Map<java.lang.String, java.lang.Object> controller.UserController.getUserInfo(java.lang.Integer)  2018-04-30 07:54:07 [localhost-startStop-1] INFO RequestMappingHandlerAdapter - Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Mon Apr 30 07:54:06 CDT 2018]; root of context hierarchy  Apr 30, 2018 7:54:07 AM org.apache.catalina.core.ApplicationContext log  INFO: Initializing Spring FrameworkServlet 'dispatcher'  2018-04-30 07:54:07 [localhost-startStop-1] INFO DispatcherServlet - FrameworkServlet 'dispatcher': initialization started  2018-04-30 07:54:07 [localhost-startStop-1] INFO DispatcherServlet - FrameworkServlet 'dispatcher': initialization completed in 19 ms  Apr 30, 2018 7:54:07 AM org.apache.coyote.AbstractProtocol start  INFO: Starting ProtocolHandler ["http-bio-8080"]

这样就启动了我们的 SpringMVC 项目,可以测试 http://localhost:8080/springmvc/users/1234

➜ / curl -v http://localhost:8080/springmvc/users/1234  * Trying ::1...  * TCP_NODELAY set  * Connected to localhost (::1) port 8080 (#0)  > GET /springmvc/users/1234 HTTP/1.1  > Host: localhost:8080  > User-Agent: curl/7.54.0  > Accept: */*  >  < HTTP/1.1 200 OK  < Server: Apache-Coyote/1.1  < Content-Type: application/json;charset=UTF-8  < Transfer-Encoding: chunked  < Date: Mon, 30 Apr 2018 13:04:52 GMT  <  * Connection #0 to host localhost left intact  {"UserId":1234,"Name":"Yanbin"}

因为我们在 pom.xml 中引入了 jackson, 所以 Controller 方法返回的对象被自动转换为 JSON 格式的数据

http://localhost:8080/springmvc/ 将会访问到 src/webapp/index.jsp 的内容,如果文件存在的话。

关于响应格式

如果在项目中还引入了 Jackson 的另一个 xml 包

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>${jackson.version}</version>
</dependency>

mvn tomcat7:run 重启 Tomcat, 访问 http://localhost:8080/springmvc/users/1234, 这时候看到输出的是 xml 格式

➜ / curl -v http://localhost:8080/springmvc/users/1234  .......  >  < HTTP/1.1 200 OK  < Server: Apache-Coyote/1.1  < Content-Type: application/xml;charset=UTF-8  < Transfer-Encoding: chunked  < Date: Mon, 30 Apr 2018 13:06:47 GMT  <  * Connection #0 to host localhost left intact  <Map><UserId>1234</UserId><Name>Yanbin</Name></Map>

那么在这种情况下如何再次获得 JSON 的响应数据呢?有三种办法

请求时指定响应头

➜ / curl -H "Accept:application/json" http://localhost:8080/springmvc/users/1234  {"UserId":1234,"Name":"Yanbin"}

@RequestMapping 指定 produces

Controller 类或方法上指定 @RequestMapping 的 produces 属性为 json, 例如

@RequestMapping(value = "/users", produces = "application/json")  //或  @RequestMapping(value="/{userId}", method=RequestMethod.GET, produces = "application/json")

这时候不带 Accept 头默认的响应就是 JSON 了

借助于 WebMvcConfigurerAdapter 设置默认的响应类型

修改前面的 WebAppConfig 类的内容如下

@EnableWebMvc
@ComponentScan(basePackages = {"controller"})
public class WebAppConfig extends WebMvcConfigurerAdapter {
 
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);
    }
 
}

如果在 Controller 或请求头中没有特别说明默认输出 JSON 格式响应。也就是说这时候请求头中如果要求得到 XML 格式的数据还是没问题的。

➜ / curl -H "Accept:text/xml" http://localhost:8080/springmvc/users/1234  <Map><UserId>1234</UserId><Name>Yanbin</Name></Map>

链接:

  1. Spring MVC Hello World 例子 (web.xml 和 dispatcher-servlet.xml 方式)
  2. Spring 4 MVC Tutorial Maven Example - Spring Java Configuration
原文  https://unmi.cc/basic-spring-4-mvc-maven-project/
正文到此结束
Loading...