# Spring 自定义 404 错误页面教程

> 原文： [http://zetcode.com/spring/custom404page/](http://zetcode.com/spring/custom404page/)

Spring 自定义 404 错误页面教程展示了如何在 Spring Web 应用中创建自定义 404 错误页面。

Spring 是用于创建企业应用的流行 Java 应用框架。

## 404 代码

HTTP 404 或 404 未找到是计算机网络通信中的超文本传输​​协议（HTTP）标准响应代码，用于指示客户端能够与给定服务器进行通信，但是服务器找不到所请求的资源。

## Spring 自定义 404 错误页面示例

以下应用使用创建自定义 404 错误页面。 默认情况下，当找不到资源时，将显示 Tomcat 的 404 错误页面。

```java
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           ├───config
│   │           │       MyWebInitializer.java
│   │           │       WebConfig.java
│   │           └───controller
│   │                   ControllerAdvisor.java
│   │                   MyController.java
│   └───resources
│       │   logback.xml
│       └───templates
│           │   index.html
│           └───error
│                   404.html
└───test
    └───java

```

这是项目结构。

`pom.xml`

```java
<?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.zetcode</groupId>
    <artifactId>custom404page</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.11.RELEASE</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
            </plugin>

        </plugins>
    </build>

</project>

```

在`pom.xml`文件中，我们具有以下依存关系：`logback-classic`，`javax.servlet-api`，`spring-webmvc`和`thymeleaf-spring5`以及`thymeleaf`。

`resources/logback.xml`

```java
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <logger name="org.springframework" level="ERROR"/>
    <logger name="com.zetcode" level="INFO"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>%d{HH:mm:ss.SSS} %blue(%-5level) %magenta(%logger{36}) - %msg %n
            </Pattern>
        </encoder>
    </appender>

    <root>
        <level value="INFO" />
        <appender-ref ref="consoleAppender" />
    </root>
</configuration>

```

这是`logback.xml`配置文件。

`com/zetcode/config/MyWebInitializer.java`

```java
package com.zetcode.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

@Configuration
public class MyWebInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {

        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {

        return new String[]{"/"};
    }

    @Override
    protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
        var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
        dispatcher.setThrowExceptionIfNoHandlerFound(true);
        return dispatcher;
    }
}

```

`MyWebInitializer`初始化 Spring Web 应用。 它包含一个配置类：`WebConfig`。

```java
@Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
    var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
    dispatcher.setThrowExceptionIfNoHandlerFound(true);
    return dispatcher;
}

```

使用`setThrowExceptionIfNoHandlerFound()`，我们将 Spring 配置为在找不到资源时抛出`NoHandlerFoundException`。 如果我们在此行中添加注释，则会显示 Web 服务器的 404 错误页面。

`com/zetcode/config/WebConfig.java`

```java
package com.zetcode.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
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.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.zetcode"})
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public SpringResourceTemplateResolver templateResolver() {

        var templateResolver = new SpringResourceTemplateResolver();

        templateResolver.setApplicationContext(applicationContext);
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");

        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {

        var templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);

        return templateEngine;
    }

    @Bean
    public ViewResolver viewResolver() {

        var resolver = new ThymeleafViewResolver();
        var registry = new ViewResolverRegistry(null, applicationContext);

        resolver.setTemplateEngine(templateEngine());
        registry.viewResolver(resolver);

        return resolver;
    }
}

```

`WebConfig`配置 Thymeleaf 模板引擎。 Thymeleaf 模板文件位于类路径的`templates`子目录中。

`com/zetcode/controller/MyController.java`

```java
package com.zetcode.controller;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.time.LocalDateTime;

@Controller
public class MyController {

    @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
    public String home(Model model) {

        model.addAttribute("now", LocalDateTime.now());

        return "index";
    }
}    

```

`MyController`包含主页的一种路由。

`com/zetcode/controller/ControllerAdvisor.java`

```java
package com.zetcode.controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

@ControllerAdvice
public class ControllerAdvisor {

    @ExceptionHandler(NoHandlerFoundException.class)
    public ModelAndView handle(Exception ex) {

        var mv = new ModelAndView();
        mv.addObject("message", ex.getMessage());
        mv.setViewName("error/404");

        return mv;
    }
}

```

`ControllerAdvisor`包含`NoHandlerFoundException`的处理器。 它显示`404.html`错误页面，位于`resources/templates/error/404.html`中。

`resources/templates/error/404.html`

```java
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Resource not found</title>
</head>
<body>

<h2>404 - resource not found</h2>

<p>
    <span th:text="${message}" th:remove="tag"></span>
</p>

</body>
</html>

```

`404.html`是我们的自定义 404 错误页面。

`resources/templates/index.html`

```java
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>

<p>
    This is home page.
</p>

<p>
    Today is <span th:text="${now}"></span>
</p>
</body>
</html>

```

这是主页。

在本教程中，我们在 Spring 应用中创建了一个自定义 404 错误页面。

您可能也对这些相关教程感兴趣： [Spring WebJars 教程](/spring/webjars/)， [Spring `@GetMapping`教程](/spring/getmapping/)， [Spring `DefaultServlet`教程](/spring/defaultservlet/)， [Spring Web 应用简介](/articles/springwebfirst/)和 [Java 教程](/lang/java/)。