【SpringMVC笔记】 - 12 - 全注解开发

【SpringMVC笔记】 - 12 - 全注解开发

一、全注解开发的核心背景

在 Servlet3.0 规范发布之前,Java Web 开发中必须依赖web.xml文件完成 Servlet、Filter、Listener 等核心组件的配置,以及 SpringMVC 的前端控制器(DispatcherServlet)初始化等关键操作。

Servlet3.0 带来了全新的特性:允许不再编写 web.xml 文件,而是通过 Java 类和注解的方式完成所有 Web 应用的配置,实现 "零 XML" 开发。

Servlet3.0 规范中定义了ServletContainerInitializer接口,Web 服务器启动时会自动扫描并加载该接口的实现类,调用其onStartup方法完成 Servlet 上下文的初始化。

Spring3.1 版本针对该规范提供了适配实现,核心是WebApplicationInitializer接口,

AbstractAnnotationConfigDispatcherServletInitializer作为该接口的抽象子类,是 SpringMVC 全注解开发的核心入口 ------ 开发者只需继承该类,即可替代web.xml完成 DispatcherServlet 的配置、Spring/SpringMVC 容器初始化、过滤器配置等核心操作。

二、核心配置类:替代 web.xml 的 WebAppInitializer

2.1 类的作用

WebAppInitializer类是全注解开发中替代web.xml的核心,负责:

  1. 指定 Spring 和 SpringMVC 的配置类;
  2. 配置 DispatcherServlet 的映射路径(对应web.xml<servlet-mapping>);
  3. 配置 Web 应用的过滤器(对应web.xml<filter><filter-mapping>)。

2.2 完整实现代码

java 复制代码
package com.zzz.config;

import jakarta.servlet.Filter;
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;

/**
 * 替代web.xml的核心配置类
 * 继承AbstractAnnotationConfigDispatcherServletInitializer后,Web服务器启动时自动初始化Servlet上下文
 */
@Configuration
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    /*
     * 配置Spring根容器的配置类(非MVC相关,如数据源、事务等)
     * 对应web.xml中配置的ContextLoaderListener加载的Spring配置
     * 若暂时无Spring核心配置,返回空数组即可
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    /*
     * 配置SpringMVC容器的配置类(MVC相关:拦截器、视图解析器、异常处理等)
     * 对应web.xml中DispatcherServlet的<init-param>指定的contextConfigLocation
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    /*
     * 配置DispatcherServlet的映射规则(url-pattern)
     * "/" 表示拦截所有请求(除了JSP),替代web.xml中<servlet-mapping>的<url-pattern>
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /*
     * 配置Web应用的过滤器,替代web.xml中的<filter>配置
     * 此处配置了字符编码过滤器和RESTful风格的HiddenHttpMethodFilter
     */
    @Override
    protected Filter[] getServletFilters() {
        // 1. 字符编码过滤器:解决请求/响应中文乱码
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceRequestEncoding(true); // 强制请求编码为UTF-8
        characterEncodingFilter.setForceResponseEncoding(true); // 强制响应编码为UTF-8

        // 2. HiddenHttpMethodFilter:支持PUT/DELETE等请求方法(RESTful)
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

        // 返回过滤器数组,多个过滤器按顺序生效
        return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
    }
}

2.3 关键方法说明

方法名 作用 对应 web.xml 配置项
getRootConfigClasses 指定 Spring 根容器配置类(非 MVC) <context-param> + ContextLoaderListener
getServletConfigClasses 指定 SpringMVC 配置类 DispatcherServlet 的<init-param>
getServletMappings 配置 DispatcherServlet 的映射路径 <servlet-mapping>
getServletFilters 配置过滤器,返回过滤器数组 <filter> + <filter-mapping>

三、SpringMVC 核心配置类:SpringMvcConfig

SpringMvcConfig是 SpringMVC 的核心配置类,替代传统的springmvc.xml文件,负责配置视图解析器、组件扫描、注解驱动、静态资源处理、拦截器、异常处理器等 MVC 核心功能。该类需实现WebMvcConfigurer接口,并重写对应方法完成个性化配置。

3.1 基础配置:组件扫描 + 注解驱动

java 复制代码
package com.zzz.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
* SpringMVC核心配置类(替代springmvc.xml)
* 实现WebMvcConfigurer接口:提供MVC相关配置的扩展点
*/
@Configuration // 标记为配置类,替代xml的<beans>标签
@ComponentScan("com.zzz") // 组件扫描:扫描控制器、拦截器等组件,替代<context:component-scan>
@EnableWebMvc // 开启MVC注解驱动:支持@RequestMapping、@ResponseBody等注解,替代<mvc:annotation-driven>
public class SpringMvcConfig implements WebMvcConfigurer {
    // 后续配置方法均在此类中扩展
}

3.2 视图解析器配置(Thymeleaf)

SpringMVC 默认不内置 Thymeleaf 支持,需手动配置模板解析器、模板引擎、视图解析器三个 Bean,替代springmvc.xml中 Thymeleaf 的相关配置:

java 复制代码
// 在SpringMvcConfig类中添加以下Bean配置
// 1. 模板解析器:指定模板位置、编码、后缀等
@Bean
public ITemplateResolver templateResolver(ApplicationContext applicationContext) {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext); // 注入Spring上下文
    resolver.setPrefix("/WEB-INF/templates/"); // 模板文件前缀(对应jsp的/WEB-INF/views/)
    resolver.setSuffix(".html"); // 模板文件后缀
    resolver.setTemplateMode(TemplateMode.HTML); // 模板模式为HTML
    resolver.setCharacterEncoding("UTF-8"); // 编码格式
    resolver.setCacheable(false); // 开发环境关闭缓存,修改模板后无需重启
    return resolver;
}

// 2. 模板引擎:整合模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    return templateEngine;
}

// 3. 视图解析器:整合模板引擎,返回解析后的视图
@Bean
public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setCharacterEncoding("UTF-8"); // 响应编码
    viewResolver.setTemplateEngine(templateEngine); // 关联模板引擎
    viewResolver.setOrder(1); // 视图解析器优先级(多个时生效)
    return viewResolver;
}

3.3 静态资源处理

传统springmvc.xml中通过<mvc:default-servlet-handler/>开启静态资源处理,全注解方式需重写configureDefaultServletHandling方法:

java 复制代码
/*
 * 开启默认Servlet处理:让SpringMVC接管静态资源(css/js/img等)
 * 替代<mvc:default-servlet-handler/>
 */
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable(); // 启用默认Servlet处理静态资源
}

3.4 视图控制器(View Controller)

无需编写控制器方法,直接映射请求到视图,替代<mvc:view-controller>

java 复制代码
/*
 * 视图控制器:简化无业务逻辑的页面跳转
 * 替代<mvc:view-controller path="/test" view-name="test"/>
 */
@Override
public void addViewControllers(ViewControllerRegistry registry) {
    // 访问/test路径时,直接跳转至test.html视图
    registry.addViewController("/test").setViewName("test");
}

3.5 异常处理器配置

替代<mvc:exception-resolvers>,配置全局异常映射,将异常类型映射到指定错误页面:

java 复制代码
/*
 * 配置全局异常处理器:替代<mvc:exception-resolvers>
 * 将指定异常类型映射到错误视图,并传递异常对象到页面
 */
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();

    // 配置异常类型与视图名的映射(key:异常全类名,value:视图名)
    Properties exceptionMappings = new Properties();
    exceptionMappings.setProperty("java.lang.Exception", "error"); // 所有Exception映射到error.html

    resolver.setExceptionMappings(exceptionMappings); // 设置异常映射
    resolver.setExceptionAttribute("exception"); // 页面获取异常的变量名(如${exception})

    resolvers.add(resolver); // 添加到异常处理器列表
}

对应的错误页面(/WEB-INF/templates/error.html):

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th = "http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Error</title>
</head>
<body>
<h1>Error</h1>
<hr>
<div th:text="${exception}"></div> <!-- 显示异常信息 -->
</body>
</html>

3.6 拦截器配置

替代<mvc:interceptors>,配置自定义拦截器,并指定拦截 / 排除路径:

java 复制代码
/*
 * 配置拦截器:替代<mvc:interceptors>
 * 注册自定义拦截器,指定拦截路径和排除路径
 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 创建自定义拦截器实例
    MyInterceptor myInterceptor = new MyInterceptor();
    
    // 注册拦截器,并配置路径规则
    registry.addInterceptor(myInterceptor)
            .addPathPatterns("/**") // 拦截所有请求
            .excludePathPatterns("/hello"); // 排除/hello路径不拦截
}

自定义拦截器实现(MyInterceptor.java):

java 复制代码
package com.zzz.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Component
public class MyInterceptor implements HandlerInterceptor {
    // 控制器方法执行前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor -> preHandle");
        return true; // 返回true:放行;false:拦截
    }

    // 控制器方法执行后,视图渲染前调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor -> postHandle");
    }

    // 视图渲染完成后调用(无论是否异常)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor -> afterCompletion");
    }
}

四、控制器实现

java 复制代码
package com.zzz.controller;

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

/**
 * 示例控制器:通过@ComponentScan扫描生效
 */
@Controller
public class HelloController {

    // 处理/hello的POST请求,返回hello视图(对应/WEB-INF/templates/hello.html)
    @RequestMapping(value = "/hello", method = RequestMethod.POST)
    public String hello(){
        return "hello";
    }
}

对应的视图页面(/WEB-INF/templates/hello.html):

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>

五、全注解开发的核心优势

  1. 零 XML 配置 :摆脱web.xmlspringmvc.xml的繁琐配置,所有配置通过 Java 类 + 注解完成,代码更易维护;
  2. 类型安全:配置通过 Java 代码编写,编译期即可发现错误,而非运行期;
  3. 扩展性强WebMvcConfigurer接口提供了丰富的扩展方法,可灵活配置拦截器、异常处理、静态资源、视图控制器等;
  4. 符合 Servlet3.0 规范:适配现代 Web 容器(Tomcat7+),充分利用 Servlet3.0 的新特性;
  5. 与 Spring 生态无缝整合:注解驱动的配置方式与 Spring Boot 的自动配置理念一致,为后续学习 Spring Boot 打下基础。

六、注意事项

  1. 包扫描范围@ComponentScan需指定正确的包路径(如com.zzz),确保控制器、拦截器等组件被扫描到;
  2. Thymeleaf 缓存 :开发环境需关闭模板缓存(resolver.setCacheable(false)),避免修改模板后需重启服务器;
  3. 拦截器路径规则addPathPatterns("/**")表示拦截所有请求,excludePathPatterns需精准配置排除路径(如静态资源、特定接口);
  4. 字符编码过滤器 :必须设置ForceRequestEncodingForceResponseEncodingtrue,否则可能出现中文乱码;
  5. 视图解析器前缀 / 后缀SpringResourceTemplateResolverprefix需与实际模板文件路径一致(如/WEB-INF/templates/),否则会找不到视图文件。
    开发环境需关闭模板缓存(resolver.setCacheable(false)),避免修改模板后需重启服务器;
  6. 拦截器路径规则addPathPatterns("/**")表示拦截所有请求,excludePathPatterns需精准配置排除路径(如静态资源、特定接口);
  7. 字符编码过滤器 :必须设置ForceRequestEncodingForceResponseEncodingtrue,否则可能出现中文乱码;
  8. 视图解析器前缀 / 后缀SpringResourceTemplateResolverprefix需与实际模板文件路径一致(如/WEB-INF/templates/),否则会找不到视图文件。
相关推荐
我命由我123452 小时前
Android 开发问题:Raw use of parameterized class ‘Class‘
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
阿丰资源2 小时前
基于SpringBoot的高校心理教育辅导系统(附源码+数据库+文档)
数据库·spring boot·后端
橙子圆1232 小时前
SpringMVC5.0
java·spring·servlet
Aliex_git2 小时前
Nuxt 学习笔记(二)
前端·笔记·学习
南境十里·墨染春水2 小时前
C++笔记·-- STL unordered_map
开发语言·c++·笔记
三品吉他手会点灯2 小时前
C语言学习笔记 - 17.C编程预备计算机专业知识 - 数据类型
c语言·笔记·学习
噜噜噜阿鲁~2 小时前
python学习笔记 | 7.4、高级特性-生成器
笔记·python·学习
handler012 小时前
【Linux 笔记】GDB 调试速查手册
linux·运维·c语言·c++·笔记
九思十安2 小时前
HNU2026-算法设计与分析-笔记 3 摊还分析
笔记·算法