实践 Spring MVC:基于 DispatcherServlet 构建 Web 应用程序,掌握控制器、视图解析器、数据绑定等核心组件。

好的,各位观众老爷们!今天咱们就来聊聊Spring MVC,这可是Java Web开发中的一股清流,用好了它,保证你的代码优雅得像诗,运行效率快得像风!💨

咱们今天的目标是啥?就是基于DispatcherServlet这个“中央调度员”,一步一步构建一个Web应用程序,彻底搞懂控制器(Controller)、视图解析器(ViewResolver)、数据绑定这些核心组件。保证各位听完,直接就能上手写出漂亮的Spring MVC程序!

第一幕:DispatcherServlet——Web应用的总导演

首先,想象一下,你开了一家剧院。观众(用户)来了,肯定得有人负责接待、安排座位、通知演员上场等等。在Spring MVC的世界里,DispatcherServlet就扮演着这个“总导演”的角色。

简单来说,DispatcherServlet就是Spring MVC的核心,所有进入Web应用程序的请求,都要先经过它这一关。它负责:

  1. 接收HTTP请求:不管是GET、POST、PUT、DELETE,统统笑纳。
  2. 查找处理器映射器(HandlerMapping):找到能处理这个请求的“演员”(Controller)。
  3. 调用处理器适配器(HandlerAdapter):让“演员”按照剧本(处理逻辑)表演。
  4. 处理视图解析器(ViewResolver):把“演员”的表演结果(数据)渲染成观众喜欢的形式(HTML、JSON等)。
  5. 返回响应:把渲染好的结果送回给观众(浏览器)。

是不是有点像一个精密的流水线?

DispatcherServlet的工作流程图

graph LR
    A[Client Request] --> B(DispatcherServlet)
    B --> C{HandlerMapping}
    C -- Found Handler --> D[HandlerAdapter]
    C -- Not Found --> E[NoHandlerFoundException]
    D --> F[Handler (Controller)]
    F --> G{Model & View}
    G --> H(ViewResolver)
    H --> I[View]
    I --> J[Rendered View]
    J --> K(Response)
    E --> K
    K --> A
    style B fill:#f9f,stroke:#333,stroke-width:2px
    style F fill:#ccf,stroke:#333,stroke-width:2px
    style I fill:#ccf,stroke:#333,stroke-width:2px

第二幕:Controller——舞台上的主角

Controller,就是舞台上的“演员”,负责处理具体的业务逻辑。它接收DispatcherServlet分配的任务,进行处理,然后把处理结果(数据)交给视图解析器进行渲染。

咱们来写一个简单的Controller:

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello, Spring MVC!");
        return "hello"; // 返回视图名称
    }
}

解释一下:

  • @Controller:这个注解告诉Spring,这是一个Controller。
  • @RequestMapping("/hello"):这个注解定义了URL映射,当用户访问/hello时,这个方法会被调用。
  • Model model:这是一个Model对象,用来存放数据,供视图使用。
  • model.addAttribute("message", "Hello, Spring MVC!"):往Model里放了一个名为"message"的数据。
  • return "hello":返回视图的名称,告诉DispatcherServlet去哪个视图文件里渲染。

是不是很简单?😎

第三幕:HandlerMapping——“演员”经纪人

HandlerMapping就像“演员”的经纪人,负责根据请求的URL找到合适的Controller来处理。Spring MVC提供了多种HandlerMapping,比如:

  • BeanNameUrlHandlerMapping:根据Bean的名称(URL)来查找Controller。
  • SimpleUrlHandlerMapping:通过配置文件来指定URL和Controller的映射关系。
  • RequestMappingHandlerMapping:基于@RequestMapping注解来查找Controller,也是最常用的。

咱们上面的例子就使用了RequestMappingHandlerMapping,因为它能识别@RequestMapping注解。

第四幕:HandlerAdapter——“演员”的翻译

HandlerAdapter就像“演员”的翻译,负责把DispatcherServlet的请求转换成Controller能够理解的形式,并执行Controller的方法。

Spring MVC也提供了多种HandlerAdapter,比如:

  • HttpRequestHandlerAdapter:处理实现了HttpRequestHandler接口的Controller。
  • SimpleControllerHandlerAdapter:处理实现了Controller接口的Controller。
  • RequestMappingHandlerAdapter:处理使用了@RequestMapping注解的Controller,也是最常用的。

咱们上面的例子就使用了RequestMappingHandlerAdapter,因为它能处理@RequestMapping注解。

第五幕:ViewResolver——“化妆师”和“服装师”

ViewResolver就像“化妆师”和“服装师”,负责把Controller返回的数据渲染成用户看得懂的形式(HTML、JSON等)。它根据Controller返回的视图名称,找到对应的视图文件,然后把数据填充进去,生成最终的响应。

Spring MVC提供了多种ViewResolver,比如:

  • InternalResourceViewResolver:解析JSP视图,也是最常用的。
  • FreeMarkerViewResolver:解析FreeMarker视图。
  • ThymeleafViewResolver:解析Thymeleaf视图。

咱们来配置一个InternalResourceViewResolver

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

解释一下:

  • prefix:指定视图文件存放的目录。
  • suffix:指定视图文件的后缀名。

这样,当Controller返回"hello"时,InternalResourceViewResolver就会去/WEB-INF/views/hello.jsp找视图文件。

第六幕:数据绑定——把观众的掌声变成演员的动力

数据绑定,就是把用户提交的数据(比如表单数据、URL参数)转换成Controller方法需要的参数。Spring MVC提供了强大的数据绑定功能,可以自动完成类型转换、数据验证等操作。

咱们来写一个带数据绑定的Controller:

@Controller
public class UserController {

    @RequestMapping("/user/register")
    public String register(@RequestParam("username") String username,
                           @RequestParam("password") String password,
                           Model model) {
        model.addAttribute("username", username);
        model.addAttribute("password", password);
        return "register_success";
    }
}

解释一下:

  • @RequestParam("username") String username:这个注解告诉Spring,把请求参数中名为"username"的值赋给username变量。
  • @RequestParam("password") String password:同理,把请求参数中名为"password"的值赋给password变量。

这样,当用户提交表单时,Spring MVC会自动把表单数据绑定到Controller方法的参数上,省去了手动获取参数的麻烦。🤩

第七幕:验证——确保观众的“尖叫声”是赞美而不是吐槽

数据验证,就是对用户提交的数据进行校验,确保数据的合法性。Spring MVC提供了强大的验证框架,可以方便地进行各种验证,比如非空验证、长度验证、格式验证等。

咱们来用JSR-303(Bean Validation)进行验证:

首先,定义一个User类:

public class User {

    @NotEmpty(message = "用户名不能为空")
    @Size(min = 6, max = 20, message = "用户名长度必须在6-20之间")
    private String username;

    @NotEmpty(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
    private String password;

    // getter and setter
}

然后,在Controller中使用@Valid注解进行验证:

@Controller
public class UserController {

    @RequestMapping("/user/register")
    public String register(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            // 验证失败,返回错误信息
            return "register_form";
        }
        // 验证成功,处理注册逻辑
        model.addAttribute("username", user.getUsername());
        model.addAttribute("password", user.getPassword());
        return "register_success";
    }
}

解释一下:

  • @Valid User user:这个注解告诉Spring,对User对象进行验证。
  • BindingResult result:这个对象用来存放验证结果。
  • result.hasErrors():判断是否有验证错误。

这样,当用户提交表单时,Spring MVC会自动对User对象进行验证,如果有错误,会把错误信息放到BindingResult对象里,Controller可以根据这些错误信息进行处理。

第八幕:异常处理——“舞台事故”的完美公关

异常处理,就是在程序发生异常时,能够优雅地处理,而不是让程序崩溃。Spring MVC提供了多种异常处理机制,比如:

  • HandlerExceptionResolver:全局异常处理,可以处理任何Controller抛出的异常。
  • @ExceptionHandler:局部异常处理,只能处理当前Controller抛出的异常。
  • @ControllerAdvice:全局异常处理,可以处理所有Controller抛出的异常。

咱们来用@ExceptionHandler进行局部异常处理:

@Controller
public class UserController {

    @RequestMapping("/user/register")
    public String register(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            // 验证失败,返回错误信息
            return "register_form";
        }
        // 验证成功,处理注册逻辑
        model.addAttribute("username", user.getUsername());
        model.addAttribute("password", user.getPassword());
        return "register_success";
    }

    @ExceptionHandler(Exception.class)
    public String handleException(Exception e, Model model) {
        model.addAttribute("error", "系统发生错误:" + e.getMessage());
        return "error";
    }
}

解释一下:

  • @ExceptionHandler(Exception.class):这个注解告诉Spring,这个方法用来处理Exception类型的异常。
  • Exception e:这个参数用来接收抛出的异常对象。

这样,当UserController的任何方法抛出Exception类型的异常时,handleException方法会被调用,并把异常信息放到Model里,然后返回"error"视图。

第九幕:拦截器——幕后英雄,默默守护着舞台

拦截器(Interceptor)就像幕后英雄,可以在请求到达Controller之前或之后,进行一些额外的处理,比如:

  • 权限验证:判断用户是否有权限访问某个Controller。
  • 日志记录:记录请求的URL、参数、执行时间等信息。
  • 性能监控:监控Controller的执行时间,发现性能瓶颈。

咱们来写一个简单的拦截器:

public class LogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在Controller方法执行之前执行
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        System.out.println("请求URL:" + request.getRequestURL());
        return true; // 返回true表示继续执行,返回false表示中断执行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在Controller方法执行之后,视图渲染之前执行
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("执行时间:" + executeTime + "ms");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在整个请求完成之后执行,包括视图渲染之后
        System.out.println("请求完成");
    }
}

解释一下:

  • preHandle:在Controller方法执行之前执行,可以进行权限验证、日志记录等操作。
  • postHandle:在Controller方法执行之后,视图渲染之前执行,可以修改ModelAndView对象。
  • afterCompletion:在整个请求完成之后执行,包括视图渲染之后,可以进行清理工作。

然后,配置拦截器:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/> <!-- 拦截所有请求 -->
        <bean class="com.example.interceptor.LogInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

这样,每次请求都会经过LogInterceptor,记录请求的URL和执行时间。

第十幕:配置——搭建舞台,准备好一切

最后,咱们需要配置Spring MVC,告诉它如何工作。

  1. 配置DispatcherServlet:在web.xml文件中配置DispatcherServlet。
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  1. 配置Spring MVC配置文件:在dispatcher-servlet.xml文件中配置HandlerMapping、HandlerAdapter、ViewResolver等组件。
<context:component-scan base-package="com.example.controller"/>

<mvc:annotation-driven/>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

总结陈词:

好了,各位!咱们今天就聊到这里。Spring MVC的核心组件,DispatcherServlet、Controller、HandlerMapping、HandlerAdapter、ViewResolver、数据绑定、验证、异常处理、拦截器,都给大家掰开了、揉碎了讲了一遍。

希望大家能够举一反三,灵活运用,写出优雅、高效的Spring MVC程序!记住,编程就像写诗,要有灵感,更要有技巧!

如果大家觉得还不过瘾,下次咱们再聊聊Spring Boot集成Spring MVC,让你的开发效率飞起来!🚀

感谢各位的观看!咱们下期再见!👋

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注