Appearance
SpringMVC教程 - 18 全注解开发
在 Servlet 2.5 及以前,Web 容器不会扫描你的类路径,你的项目中有没有 Servlet、Filter、Listener 它都不知道。
只能通过 web.xml 告诉容器:我有哪些组件。
例如必须这样声明:
xml
<!-- DispatcherServlet 配置 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>而从 Servlet 3.0 开始,容器启动时会自动扫描 classpath 中的类:
- 扫描是否存在
@WebServlet - 扫描是否存在
@WebFilter - 扫描是否存在
@WebListener - 扫描是否存在实现
ServletContainerInitializer接口的类
也就是说 web.xml 已经不是必须的了,Spring 提供了一个 SpringServletContainerInitializer 类,它就实现了 ServletContainerInitializer 接口,所以在容器启动时,就会调用 SpringServletContainerInitializer 中的 onStartup() 方法。而在 onStartup() 方法中调用了 WebApplicationInitializer 接口,所以我们只要写一个类,实现 WebApplicationInitializer 接口,就可以在容器启动时注册 DispatcherServlet,无需 web.xml。
而 SpringMVC 帮我们提供了一个继承自 WebApplicationInitializer 的抽象类 AbstractAnnotationConfigDispatcherServletInitializer ,它帮我们把 SpringMVC 的初始化逻辑都准备好了,我们只要继承它,然后填写几个配置类就可以了。

下面就不使用 XML,从 0 开始,搭建一个使用全注解方式开发的项目。
18.1 新建项目
这里我们直接使用 IDEA 新建一个 J2EE 的项目,会自动帮我们生成 web.xml。
待会我们将 web.xml 删掉就好了。
可以只用 Maven 模板来创建,可以选择 maven-archetype-webapp 的模板,或者直接使用 Jakarta EE 项目。

我这里直接使用 Jakarta EE ,我的 IDEA 版本比较旧,新版本不是这样,但是操作都差不多。

点击 Next,第二步会为项目选择一些依赖,这里我只选择了 Servlet,待会我在 pom.xml 中自己引入依赖:

现在项目建好了,下面引入一下项目的依赖,我删除了创建项目后自动引入的依赖,直接将之前项目引入的所有依赖都添加了进来:
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.foooor</groupId>
<artifactId>hello-web</artifactId>
<version>1.0-SNAPSHOT</version>
<name>hello-web</name>
<!-- 打包为 war 包 -->
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- Servlet API -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<!-- Tomcat容器会提供,这里主要是写代码时需要引入,否则会报错 -->
<scope>provided</scope>
</dependency>
<!-- Spring MVC 核心依赖,会包含 Spring 核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.2.11</version>
</dependency>
<!-- 引入thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>
<!-- Jackson 用于 JSON 序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.20.0</version>
</dependency>
<!-- 日志实现,Logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.19</version>
</dependency>
<!-- Lombok 可以生成 getter/setter 等方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
</dependency>
<!-- JSR-380 核心依赖,提供 Bean Validation 的标准接口和注解 -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>4.0.0-M1</version>
</dependency>
<!-- Hibernate Validator 实现(推荐) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>9.1.0.Final</version>
</dependency>
<!-- 提供表达式语言支持,用于错误消息插值 -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>5.0.0-M1</version>
</dependency>
<!-- 让 Jackson 支持 Java 8 日期时间类型(LocalDate、LocalDateTime 等) -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.20.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<warName>hello-web</warName> <!-- 可选:自定义 WAR 文件名 -->
</configuration>
</plugin>
<!-- 编译配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<arg>-parameters</arg> <!-- 开启参数名保留,用于反射获取参数名 -->
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>另外删除项目自动生成的的 web.xml、index.jsp、HelloServlet 。
18.2 统一异常处理准备
我们先讲项目中的各种功能的组件都先创建好,然后再配置。
这些类之前都创建和使用过,这里我们重新搭建一下完整的项目。
1 创建自定义错误码类
针对不同的错误信息,在项目中定义统一的错误码。
java
package com.foooor.helloweb.common;
public enum ErrorCode {
SUCCESS(0, "成功"),
SYSTEM_ERROR(1000, "系统内部错误"),
PARAM_ERROR(1001, "参数错误"),
NO_PERMISSION(1002, "无权限访问"),
USER_NOT_FOUND(1003, "用户不存在");
public final int code;
public final String msg;
ErrorCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}2 统一返回结果类
创建 Result 类,统一 Restful API 返回的数据结构:
java
package com.foooor.helloweb.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // Lombok 自动生成 getter/setter 等方法
@NoArgsConstructor
@AllArgsConstructor // Lombok 自动生成全参构造方法
public class Result {
private int code;
private String message;
private Object data;
public static Result success() {
return new Result(200, "success", null);
}
public static Result success(Object data) {
return new Result(200, "success", data);
}
public static Result error(ErrorCode errorCode) {
return new Result(errorCode.code, errorCode.msg, null);
}
public static Result error(int code, String message) {
return new Result(code, message, null);
}
public static Result error(int code, String message, Object data) {
return new Result(code, message, data);
}
public static Result error(ErrorCode errorCode, Object data) {
return new Result(errorCode.code, errorCode.msg, data);
}
}3 自定义业务异常类
创建业务异常类,在代码中,可以根据情况随时抛出异常,然后走统一异常管理流程,返回错误信息。
java
package com.foooor.helloweb.common;
public class BizException extends RuntimeException {
private ErrorCode errorCode;
public BizException(ErrorCode errorCode) {
super(errorCode.msg);
this.errorCode = errorCode;
}
public ErrorCode getErrorCode() {
return errorCode;
}
/**
* 工厂方法,根据 ErrorCode 创建 BizException
*/
public static BizException of(ErrorCode errorCode) {
return new BizException(errorCode);
}
}4 创建统一异常处理类
定义统一异常处理类,并交由 Spring 容器管理。
java
package com.foooor.helloweb.common;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingRequestValueException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常处理兜底
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Object handle(Exception ex,
HttpServletRequest request,
HandlerMethod handlerMethod) {
log.error("全局异常:{}", ex);
return handleException(ErrorCode.SYSTEM_ERROR, request, handlerMethod);
}
/**
* 业务异常处理
*/
@ExceptionHandler(BizException.class)
@ResponseBody
public Object handleBizException(BizException e,
HttpServletRequest request,
HandlerMethod handlerMethod) {
log.info("业务异常:{}", e);
return handleException(e.getErrorCode(), request, handlerMethod);
}
/**
* 类型转换异常处理
*/
@ExceptionHandler({
HttpMessageNotReadableException.class,
MissingRequestValueException.class,
MethodArgumentTypeMismatchException.class,
HttpMessageConversionException.class,
HandlerMethodValidationException.class,
ServletRequestBindingException.class,
BindException.class,
TypeMismatchException.class
})
@ResponseBody
public Object handleRequestParamException(Exception ex,
HttpServletRequest request,
HandlerMethod handlerMethod) {
log.info("参数异常:{}", ex);
return handleException(ErrorCode.PARAM_ERROR, request, handlerMethod);
}
/**
* 统一异常处理
*/
private Object handleException(ErrorCode errorCode,
HttpServletRequest request,
HandlerMethod handlerMethod) {
log.error("SpringMVC 异常:{}", errorCode);
if (shouldReturnJson(request, handlerMethod)) {
return Result.error(errorCode);
}
// 2.否则返回页面
ModelAndView mv = new ModelAndView();
mv.addObject("error", errorCode.msg);
mv.setViewName("error"); // 视图名称
return mv;
}
/**
* 判断是否返回 JSON 格式异常信息
*/
private boolean shouldReturnJson(HttpServletRequest request,
HandlerMethod handlerMethod) {
// 1. Controller / Method 标注
if (handlerMethod != null &&
(handlerMethod.hasMethodAnnotation(ResponseBody.class) ||
handlerMethod.getBeanType().isAnnotationPresent(RestController.class))) {
return true;
}
// 2. AJAX 头
String xhr = request.getHeader("X-Requested-With");
if ("XMLHttpRequest".equalsIgnoreCase(xhr)) {
return true;
}
// 3. Accept 头
String accept = request.getHeader("Accept");
return accept != null && accept.contains("application/json");
}
}18.3 拦截器过滤器准备
1 创建拦截器
创建一个拦截器,后面演示一下如何配置拦截器。
java
package com.foooor.helloweb.intercepter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Slf4j
@Component // 注册为 Spring 组件
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// 记录开始时间,存入 request
request.setAttribute("startTime", System.currentTimeMillis());
return true; // 放行
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// 获取时间
Long start = (Long) request.getAttribute("startTime");
// 计算耗时
long duration = System.currentTimeMillis() - start;
log.info("----执行耗时:{}, uri:{}, handler:{}, 耗时:{} ms", request.getMethod(), request.getRequestURI(), handler, duration);
}
}2 创建过滤器
创建一个过滤器,后面演示一下如何配置过滤器。
java
package com.foooor.helloweb.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.foooor.helloweb.common.ErrorCode;
import com.foooor.helloweb.common.Result;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GlobalExceptionFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
chain.doFilter(req, res);
} catch (Exception e) {
if (isAjax(request)) {
writeJson(response, Result.error(ErrorCode.SYSTEM_ERROR));
} else {
// 其他异常,跳转到自定义错误页面
response.sendRedirect("/error");
}
}
}
/**
* 写 JSON 响应
*/
private void writeJson(HttpServletResponse response, Result result) throws IOException {
// 1. 一定要先判断 response 是否已经提交
if (response.isCommitted()) {
return;
}
// 2. 重置响应(避免之前的 HTML / 错误页面内容)
response.reset();
// 3. 设置状态码
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
// 4. 设置返回类型和编码
response.setContentType("application/json;charset=UTF-8");
// 5. 写 JSON
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
response.getWriter().write(json);
response.getWriter().flush();
}
/**
* 判断是否是 AJAX 请求
*/
private boolean isAjax(HttpServletRequest request) {
// 1. X-Requested-With(传统 AJAX)
String xRequestedWith = request.getHeader("X-Requested-With");
if ("XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
return true;
}
// 2. Accept 包含 application/json(fetch / axios)
String accept = request.getHeader("Accept");
if (accept != null && accept.contains("application/json")) {
return true;
}
// 3. Content-Type 是 JSON(POST / PUT)
String contentType = request.getContentType();
if (contentType != null && contentType.contains("application/json")) {
return true;
}
return false;
}
}18.4 创建类型转换器
创建一个类型转换器,后面演示一下如何配置:
java
package com.foooor.helloweb.converter;
import com.foooor.helloweb.pojo.User;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
public class StringToUserConverter implements Converter<String, User> {
/**
* 转换字符串到 User 对象
* 假设字符串格式为 "id,name,email"
*/
@Override
public User convert(String source) {
try {
// 假设字符串格式为 "id,name,email"
String[] parts = source.split(",");
if (parts.length == 3) {
Long id = Long.parseLong(parts[0]);
String phoneNumber = parts[1];
String nickname = parts[2];
return new User(id, phoneNumber, nickname);
}
} catch (Exception e) {
// 处理转换异常
e.printStackTrace();
}
return null;
}
}- 用于将
user=123,ZhangSan,123@foooor.com参数解析为用户对象。
涉及到 User 类:
java
package com.foooor.hellospringmvc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String email;
}18.5 创建业务类
1 创建Controller
创建 Controller,跳转到 index.html
java
package com.foooor.helloweb.controller;
import com.foooor.helloweb.common.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/hello")
@ResponseBody
public Result hello() {
return Result.success("你好");
}
}2 页面准备
在 webapp/WEB-INFO/template/ 目录下 index.html:
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
Hello For技术栈!
</body>
</html>好了,该准备的都准备了,下面开始配置。
18.6 创建SpringMVC配置类
创建配置类主要是的代替之前使用的 springmvc-servlet.xml 配置文件。
一般会在项目下创建一个 config 子包,在包下创建 SpringMvcConfig.java (自定义名称)类,如下:
之前在 XML 中配置的都需要在这里进行配置:
java
package com.foooor.helloweb.config;
import com.foooor.helloweb.converter.StringToUserConverter;
import com.foooor.helloweb.intercepter.LogInterceptor;
import org.hibernate.validator.HibernateValidator;
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.format.support.FormattingConversionService;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* SpringMVC的配置类,用来代替springmvc-servlet.xml
* 根据需要做如下配置:
* 1.配置组件扫描
* 2.开启注解驱动
* 3.配置拦截器
* 4.配置视图解析器,包括视图解析器、模板引擎、模板解析器(传统项目)
* 5.配置静态资源处理(传统项目)
* 6.配置视图控制器,对于不需要controller处理数据的请求,直接返回视图
* 7.配置消息转换器,包括字符串消息转换器、JSON消息转换器
* 8.文件上传配置
* 9.配置自定义的类型转换服务
* 10.数据校验器
*/
@Configuration
// 1.配置组件扫描
@ComponentScan(basePackages = "com.foooor.helloweb")
// 2.开启注解驱动
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
// 注入拦截器
@Autowired
private LogInterceptor logInterceptor;
/**
* 3.配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器,拦截所有请求,除了 /login 和 /register
registry.addInterceptor(logInterceptor)
.addPathPatterns("/**") // 拦截所有请求,可以添加多个拦截.addPathPatterns("/order/**", "/user/**")
.excludePathPatterns("/login", "/register"); // 添加例外
// TODO 可以通过 registry.addInterceptor 配置多个拦截器
}
/**
* 4.以下三个方法是用来配置Thymeleaf模板解析器、模板引擎、视图解析器,用于渲染Thymeleaf模板
* 配置Thymeleaf模板解析器
*/
@Bean
public SpringResourceTemplateResolver templateResolver(ApplicationContext applicationContext) {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/template/"); // 模板文件的前缀,也就是模板文件所在的目录
resolver.setSuffix(".html"); // 模板文件的后缀,也就是模板文件的扩展名
resolver.setCharacterEncoding("UTF-8"); // 模板文件的字符编码,模板文件本身用什么编码写的
resolver.setTemplateMode(TemplateMode.HTML); // 模板文件的模式,这里使用HTML5模式
resolver.setCacheable(false); // 模板文件是否缓存,这里设置为false,因为开发阶段需要频繁修改模板文件,改动即可生效,生产环境打开
return resolver;
}
/**
* 配置Thymeleaf模板引擎
*/
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver);
return engine;
}
/**
* 配置Thymeleaf视图解析器
*/
@Bean
public ViewResolver viewResolver(SpringTemplateEngine springTemplateEngine) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(springTemplateEngine);
resolver.setCharacterEncoding("UTF-8"); // 返回给浏览器的响应内容用什么编码
return resolver;
}
/**
* 5.配置静态资源处理(传统项目)
* 代替xml中的配置:<mvc:resources mapping="/static/**" location="/static/" />
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
/**
* 6.配置视图控制器,对于不需要controller处理数据的请求,直接返回视图
* 代替xml中的配置:<mvc:view-controller path="/login" view-name="login"/>
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 访问 /login 时,直接返回 index.html 视图
registry.addViewController("/login").setViewName("login");
}
/**
* 7.配置消息转换器,因为StringHttpMessageConverter的默认编码是ISO-8859-1,这里将其设置为UTF-8
* 避免单纯返回字符串的时候出现乱码
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) converter)
.setDefaultCharset(StandardCharsets.UTF_8);
}
}
}
/**
* 8.文件上传配置
*/
@Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
/**
* 9.配置自定义的类型转换服务
*/
@Bean
public FormattingConversionService conversionService() {
FormattingConversionService service = new FormattingConversionService();
service.addConverter(new StringToUserConverter());
return service;
}
// 10.数据校验器
@Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
return validator;
}
}- 使用的时候,根据需要配置就可以了。
18.7 创建Web初始化类
接下来,我们需要创建一个 Web 初始化类,用于代替 web.xml 。
主要用于注册 DispatcherServlet 和配置 Spring 容器。这个类需要继承AbstractAnnotationConfigDispatcherServletInitializer抽象类。
java
package com.foooor.helloweb.config;
import com.foooor.helloweb.filter.GlobalExceptionFilter;
import jakarta.servlet.Filter;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletRegistration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import java.io.File;
/**
* 代替web.xml,所以在这个类中编写的就是web.xml的配置
*/
@Configuration // 标识是配置文件
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 根配置类,一般是spring的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
// 这里暂时没有用到spring的配置类
return new Class<?>[0];
}
/**
* servlet配置类,一般是springmvc的配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{SpringMvcConfig.class};
}
/**
* 配置DispatcherServlet的映射路径
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 配置web.xml中的过滤器
*/
@Override
protected Filter[] getServletFilters() {
// 配置字符编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
// 用于处理post请求中的_method参数,支持Restful风格的请求
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
// 全局异常处理过滤器
GlobalExceptionFilter globalExceptionFilter = new GlobalExceptionFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter, globalExceptionFilter};
}
/**
* 配置文件上传的参数
*/
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
String tempDir = System.getProperty("java.io.tmpdir") + "/upload";
File dir = new File(tempDir);
if (!dir.exists()) {
dir.mkdirs();
}
MultipartConfigElement multipartConfig = new MultipartConfigElement(
tempDir, // 临时目录(文件太大时会写入这里)
50 * 1024 * 1024, // 单个文件最大 50MB
20 * 1024 * 1024, // 设置整个表单上传的所有文件总大小的最大值 100MB
2 * 1024 * 1024 // 超过 2MB 才写入磁盘
);
registration.setMultipartConfig(multipartConfig);
registration.setLoadOnStartup(1);
}
}AbstractAnnotationConfigDispatcherServletInitializer有很多方法可以重写,用来做不同的配置。getRootConfigClasses()返回根容器的配置类,对应Spring的配置,这里我们暂时没有使用 Spring 配置类,所以先空着不配置;getServletConfigClasses()返回SpringMVC容器的配置类,对应之前的 SpringMVC 配置类springmvc-servlet.xml,getServletMappings()配置DispatcherServlet的映射路径,这里配置为/,表示处理所有请求。
18.8 配置日志文件
添加一下日志配置,后面在项目中可以更方便的打印日志,这里使用 logback 日志框架实现。
在项目的 resources 目录下,创建 logback.xml,配置如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义日志文件存放路径,这里指定放在 Tomcat 根目录下的 logs 文件夹 -->
<property name="LOG_PATH" value="${catalina.base}/logs" />
<!-- 定义日志文件路径 -->
<property name="LOG_FILE" value="${LOG_PATH}/hello-web.log" />
<!-- 定义控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 定义文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天生成新日志文件 -->
<fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留 30 天的日志 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 还可以单独指定某些包下的日志级别 -->
<logger name="com.foooor.helloweb" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</logger>
<!-- root 日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>18.9 测试
启动项目,测试即可。
内容未完......