SpringMVC

1. MVC架构模式

MVC是一种软件架构设计模式,用于组织应用程序的结构,将业务逻辑、数据和界面显示分离:

  • Model(模型):负责数据处理和业务逻辑。包括数据定义、数据存取、数据状态变化、业务规则等。
  • View(视图):负责数据的展示和用户界面。将Model处理后的数据以特定方式呈现给用户。
  • Controller(控制器):负责接收用户输入,并调用模型和视图完成用户请求。作为Model和View的协调者。

MVC的优点:

  • 职责分离,代码结构清晰
  • 提高代码的复用性和可维护性
  • 支持并行开发
  • 便于测试和维护

在Web开发中,MVC的一般流程:

  1. 用户通过View发送请求到Controller
  2. Controller调用Model处理请求
  3. Model将处理结果返回给Controller
  4. Controller根据返回结果选择合适的View
  5. View渲染数据并呈现给用户

2. SpringMVC简介

SpringMVC是Spring框架的一个模块,是一个基于MVC设计模式的优秀的Web框架。

特点:

  • 基于组件技术,跟Spring框架无缝集成
  • 可以使用Spring的IoC和AOP等核心功能
  • 清晰的角色划分:Controller、验证器、命令对象、表单对象、模型对象、DispatcherServlet、处理器映射、视图解析器等
  • 支持多种视图技术:JSP、Velocity、Thymeleaf、FreeMarker等
  • 强大的数据绑定与类型转换
  • 灵活的控制器配置方式(XML或注解)
  • RESTful风格的支持
  • 国际化支持
  • 更加简洁的异常处理

核心组件:

  • DispatcherServlet:前端控制器,负责接收请求并转发给相应的处理组件
  • HandlerMapping:处理器映射,负责查找处理用户请求的Controller
  • HandlerAdapter:处理器适配器,根据映射调用相应的Controller
  • Controller:处理器,实现业务逻辑,返回ModelAndView
  • ModelAndView:模型和视图,用于Controller处理结果的传递
  • ViewResolver:视图解析器,负责解析视图名并渲染视图

3. SpringMVC运行原理

SpringMVC的工作流程是围绕DispatcherServlet展开的:

  1. 用户发送请求:用户向服务器发送HTTP请求,请求被前端控制器DispatcherServlet接收
  2. 前端控制器分发请求:DispatcherServlet根据请求信息调用HandlerMapping
  3. 处理器映射确定处理器:HandlerMapping根据URL找到对应的Handler(Controller),并返回给DispatcherServlet
  4. 调用处理器适配器:DispatcherServlet通过HandlerAdapter对Handler进行适配调用
  5. 执行处理器:Handler(Controller)执行业务逻辑处理
  6. 返回ModelAndView:Handler执行完成后,返回ModelAndView对象给HandlerAdapter
  7. HandlerAdapter返回ModelAndView:HandlerAdapter将ModelAndView返回给DispatcherServlet
  8. 视图解析:DispatcherServlet将ModelAndView传给ViewResolver进行解析
  9. 渲染视图:ViewResolver根据视图名找到实际的View
  10. 返回视图:View使用Model数据进行渲染
  11. 响应用户:DispatcherServlet将渲染结果返回给用户

4. 基于配置文件方式搭建环境

基于XML配置文件搭建SpringMVC环境的步骤:

  1. 创建Maven Web项目

  2. 添加依赖:在pom.xml中添加SpringMVC相关依赖


    
    
        org.springframework
        spring-webmvc
        5.3.23
    
    
    
    
        javax.servlet
        javax.servlet-api
        4.0.1
        provided
    
    
    
    
        javax.servlet
        jstl
        1.2
    

  1. 配置web.xml:注册DispatcherServlet

    
    
    
        springmvc
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextConfigLocation
            classpath:springmvc-servlet.xml
        
        
        1
    
    
    
    
        springmvc
        /
    
    
    
    
        characterEncodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        
        
            forceEncoding
            true
        
    
    
        characterEncodingFilter
        /*
    

  1. 创建SpringMVC配置文件:在resources目录下创建springmvc-servlet.xml


    
    
    
    
    
    
    
    
    
        
        
    
    
    
    

  1. 创建控制器
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {
    
    @RequestMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello SpringMVC!");
        return "hello"; // 返回视图名
    }
}
  1. 创建视图:在/WEB-INF/views/目录下创建hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Hello SpringMVC


    

${message}

  1. 部署并测试:启动服务器,访问http://localhost:8080/hello

5. 基于注解方式搭建环境

基于Java配置(无XML)搭建SpringMVC环境的步骤:

  1. 添加依赖:同上

  2. 创建Web应用初始化类(代替web.xml)

package com.example.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }
    
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }
    
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
    
    @Override
    protected javax.servlet.Filter[] getServletFilters() {
        // 配置编码过滤器
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        return new Filter[] { characterEncodingFilter };
    }
}
  1. 创建Web配置类(代替springmvc-servlet.xml)
package com.example.config;

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.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
    
    // 配置视图解析器
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
    // 配置静态资源处理
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
  1. 创建Root配置类(用于Service和Repository等其他Spring组件)
package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(basePackages = {"com.example"},
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
    })
public class RootConfig {
    // 配置数据源、事务管理等
}
  1. 创建控制器
package com.example.controller;

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

@Controller
public class HelloController {
    
    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello SpringMVC (Annotation-based)!");
        return "hello";
    }
}
  1. 创建视图和部署测试:同上

这种方式的优点是不需要XML配置文件,完全基于Java注解配置,更加简洁明了。

6. SpringMVC的跳转及视图解析器的配置

视图解析器配置

在SpringMVC中,视图解析器负责根据控制器返回的视图名找到对应的视图:

XML配置方式:



    
    
    
    
    
    

Java配置方式:

@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    resolver.setOrder(1);
    return resolver;
}

页面跳转方式

SpringMVC支持两种页面跳转方式:

  1. 转发(Forward):服务器内部跳转,URL不变,请求对象可共享
// 方式1:直接返回视图名
@RequestMapping("/test1")
public String test1() {
    return "test"; // 转发到/WEB-INF/views/test.jsp
}

// 方式2:使用forward:前缀(不会被视图解析器解析)
@RequestMapping("/test2")
public String test2() {
    return "forward:/WEB-INF/views/test.jsp"; // 直接转发到指定路径
}

// 方式3:转发到另一个控制器
@RequestMapping("/test3")
public String test3() {
    return "forward:/test1"; // 转发到/test1对应的控制器
}
  1. 重定向(Redirect):客户端跳转,URL改变,请求对象不共享
// 使用redirect:前缀
@RequestMapping("/test4")
public String test4() {
    return "redirect:/test1"; // 重定向到/test1
}

// 重定向到外部URL
@RequestMapping("/test5")
public String test5() {
    return "redirect:https://www.example.com"; // 重定向到外部网站
}

多种视图类型

SpringMVC支持多种视图类型,可以为不同类型配置不同的视图解析器:

配置多个视图解析器:



    
    
    
     




    
    
    




    
    

7. SpringMVC和Ajax的交互

SpringMVC提供了对Ajax请求的良好支持,通常使用@ResponseBody注解和Jackson库处理JSON数据:

  1. 添加JSON依赖

    com.fasterxml.jackson.core
    jackson-databind
    2.13.4

  1. 创建Controller处理Ajax请求
@Controller
@RequestMapping("/api")
public class AjaxController {
    
    @GetMapping("/users")
    @ResponseBody
    public List getUsers() {
        List users = userService.findAll();
        return users; // 自动转换为JSON
    }
    
    @PostMapping("/users")
    @ResponseBody
    public ResponseEntity createUser(@RequestBody User user) {
        User savedUser = userService.save(user);
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }
    
    @GetMapping("/users/{id}")
    @ResponseBody
    public ResponseEntity getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user == null) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity<>(user, HttpStatus.OK);
    }
}
  1. 前端发送Ajax请求
// 使用jQuery发送Ajax请求
$(document).ready(function() {
    // GET请求获取用户列表
    $.ajax({
        url: '/api/users',
        type: 'GET',
        success: function(data) {
            // 处理返回的数据
            $.each(data, function(index, user) {
                $('#userList').append('
  • ' + user.name + '
  • '); }); }, error: function(xhr, status, error) { console.error('Error:', error); } }); // POST请求创建用户 $('#createUserForm').submit(function(e) { e.preventDefault(); var userData = { name: $('#name').val(), email: $('#email').val(), age: $('#age').val() }; $.ajax({ url: '/api/users', type: 'POST', contentType: 'application/json', data: JSON.stringify(userData), success: function(data) { alert('用户创建成功:' + data.name); }, error: function(xhr, status, error) { alert('创建失败: ' + error); } }); }); });
    1. 使用@RestController简化

    如果控制器中的所有方法都返回数据而不是视图,可以使用@RestController注解简化:

    @RestController
    @RequestMapping("/api")
    public class UserRestController {
        
        @Autowired
        private UserService userService;
        
        @GetMapping("/users")
        public List getUsers() {
            return userService.findAll();
        }
        
        @PostMapping("/users")
        public ResponseEntity createUser(@RequestBody User user) {
            User savedUser = userService.save(user);
            return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
        }
    }
    
    1. 处理JSON格式的请求和响应

    配置消息转换器:

    
        
            
                
                    
                        
                        
                            
                                
                            
                        
                    
                
            
        
    
    

    Java配置方式:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        
        @Override
        public void configureMessageConverters(List> converters) {
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            converter.setObjectMapper(objectMapper);
            converters.add(converter);
        }
    }
    

    8. Spring 参数注入

    SpringMVC提供了丰富的参数注入方式,可以将HTTP请求参数绑定到控制器方法的参数上:

    1. 基本类型参数

    @GetMapping("/test")
    public String test(String name, int age) {
        // 自动获取请求参数name和age
        return "test";
    }
    

    2. @RequestParam注解

    @GetMapping("/test")
    public String test(
        @RequestParam(value = "userName", required = false, defaultValue = "guest") String name,
        @RequestParam(value = "userAge", defaultValue = "0") int age) {
        
        // userName参数对应name变量,userAge参数对应age变量
        return "test";
    }
    

    3. 对象参数(自动装配)

    public class User {
        private String name;
        private int age;
        private Address address; // 嵌套对象
        
        // getter和setter方法
    }
    
    @PostMapping("/register")
    public String register(User user) {
        // 自动将请求参数映射到User对象的属性
        // 例如:name参数映射到user.name,address.city映射到user.address.city
        return "success";
    }
    

    4. @PathVariable注解(获取URL路径变量)

    @GetMapping("/users/{id}")
    public String getUser(@PathVariable("id") Long userId) {
        // 获取URL路径中的id变量
        return "userDetail";
    }
    
    // 当变量名与路径变量名相同时,可以简化为
    @GetMapping("/users/{id}/posts/{postId}")
    public String getUserPost(@PathVariable Long id, @PathVariable Long postId) {
        // 获取URL路径中的多个变量
        return "postDetail";
    }
    

    5. @RequestHeader注解(获取请求头)

    @GetMapping("/test")
    public String test(@RequestHeader("User-Agent") String userAgent) {
        // 获取请求头中的User-Agent
        return "test";
    }
    

    6. @CookieValue注解(获取Cookie值)

    @GetMapping("/test")
    public String test(@CookieValue(value = "sessionId", required = false) String sessionId) {
        // 获取名为sessionId的Cookie值
        return "test";
    }
    

    7. @RequestBody注解(获取请求体)

    @PostMapping("/api/users")
    @ResponseBody
    public User createUser(@RequestBody User user) {
        // 将请求体中的JSON数据绑定到User对象
        return userService.save(user);
    }
    

    8. @ModelAttribute注解

    // 在同一个控制器的所有处理方法调用前被调用
    @ModelAttribute
    public void populateModel(@RequestParam(required = false) Long userId, Model model) {
        if (userId != null) {
            model.addAttribute("user", userService.findById(userId));
        }
    }
    
    @GetMapping("/users/edit")
    public String editUser(@ModelAttribute("user") User user) {
        // 从Model中获取user属性
        return "userForm";
    }
    

    9. 数组和集合参数

    // 数组参数
    @GetMapping("/search")
    public String search(@RequestParam("keyword") String[] keywords) {
        // 获取多个同名参数,如?keyword=java&keyword=spring
        return "result";
    }
    
    // List参数
    @GetMapping("/search")
    public String search(@RequestParam("id") List ids) {
        // 获取多个同名参数,如?id=1&id=2&id=3
        return "result";
    }
    

    10. 日期类型参数

    // 使用@DateTimeFormat注解格式化日期
    @GetMapping("/test")
    public String test(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthDate) {
        // 将字符串参数转换为Date类型
        return "test";
    }
    

    11. 自定义参数绑定

    通过实现WebMvcConfigurer接口的addFormatters方法,注册自定义的Converter或Formatter:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        
        @Override
        public void addFormatters(FormatterRegistry registry) {
            // 注册自定义转换器
            registry.addConverter(new StringToEnumConverter());
        }
    }
    
    // 自定义转换器
    public class StringToEnumConverter implements Converter {
        
        @Override
        public UserStatus convert(String source) {
            return UserStatus.valueOf(source.toUpperCase());
        }
    }
    

    9. SpringMVC作用域传值

    SpringMVC提供了多种方式在不同作用域间传递数据:

    1. Model对象传值(Request作用域)

    @GetMapping("/test")
    public String test(Model model) {
        // 添加属性到request作用域
        model.addAttribute("message", "Hello World");
        model.addAttribute("user", new User("Tom", 20));
        
        // 或者一次添加多个属性
        Map map = new HashMap<>();
        map.put("count", 100);
        map.put("pageSize", 10);
        model.addAllAttributes(map);
        
        return "test";
    }
    

    2. ModelMap对象传值(Request作用域)

    @GetMapping("/test")
    public String test(ModelMap modelMap) {
        // ModelMap是LinkedHashMap的子类,具有Map的特性
        modelMap.addAttribute("message", "Hello World");
        modelMap.put("user", new User("Tom", 20));
        
        return "test";
    }
    

    3. ModelAndView对象传值(Request作用域)

    @GetMapping("/test")
    public ModelAndView test() {
        ModelAndView mav = new ModelAndView();
        
        // 设置视图名
        mav.setViewName("test");
        
        // 添加模型数据
        mav.addObject("message", "Hello World");
        mav.addObject("user", new User("Tom", 20));
        
        return mav;
    }
    

    4. @ModelAttribute注解传值(Request作用域)

    // 在Controller类的所有处理方法执行前执行
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("globalMessage", "This is a global message");
    }
    
    // 将方法返回值放入Model中
    @ModelAttribute("currentTime")
    public Date getCurrentTime() {
        return new Date();
    }
    
    // 方法参数上的@ModelAttribute
    @GetMapping("/test")
    public String test(@ModelAttribute("user") User user) {
        // 从Model中获取user对象
        return "test";
    }
    

    5. HttpServletRequest对象传值

    @GetMapping("/test")
    public String test(HttpServletRequest request) {
        // 使用原生Servlet API设置属性
        request.setAttribute("message", "Hello World");
        
        // 获取Session对象并设置属性
        HttpSession session = request.getSession();
        session.setAttribute("sessionMessage", "Hello from Session");
        
        return "test";
    }
    

    6. @SessionAttributes注解(Session作用域)

    // 在Controller类上使用@SessionAttributes注解
    @Controller
    @SessionAttributes({"user", "settings"})
    public class UserController {
        
        @GetMapping("/login")
        public String login(Model model) {
            // 添加到Model的同时也会添加到Session中
            model.addAttribute("user", new User("admin", "123456"));
            return "welcome";
        }
        
        @GetMapping("/settings")
        public String settings(Model model) {
            model.addAttribute("settings", new Settings());
            return "settings";
        }
        
        @GetMapping("/logout")
        public String logout(SessionStatus status) {
            // 清除Session中的指定属性
            status.setComplete();
            return "redirect:/login";
        }
    }
    

    7. HttpSession对象(Session作用域)

    @GetMapping("/login")
    public String login(HttpSession session) {
        // 直接使用HttpSession对象
        session.setAttribute("user", new User("admin", "123456"));
        return "welcome";
    }
    

    8. ServletContext对象(Application作用域)

    @GetMapping("/init")
    public String init(HttpServletRequest request) {
        // 获取ServletContext对象
        ServletContext application = request.getServletContext();
        // 设置应用级属性
        application.setAttribute("appName", "My SpringMVC App");
        application.setAttribute("startTime", new Date());
        
        return "index";
    }
    

    9. RedirectAttributes对象(重定向参数传递)

    @PostMapping("/users")
    public String createUser(User user, RedirectAttributes redirectAttributes) {
        // 保存用户
        userService.save(user);
        
        // 添加Flash属性(会保存在Session中,重定向后自动添加到Model中)
        redirectAttributes.addFlashAttribute("message", "用户创建成功");
        
        // 添加URL参数
        redirectAttributes.addAttribute("userId", user.getId());
        
        // 重定向到用户详情页,URL会变成/users/123?success=true
        return "redirect:/users/" + user.getId();
    }
    
    @GetMapping("/users/{id}")
    public String showUser(@PathVariable Long id, 
                          @RequestParam(required = false) Boolean success, 
                          Model model) {
        // 获取Flash属性,无需显式从Model中获取,自动添加到Model中
        
        return "userDetail";
    }
    

    10. 视图解析器

    SpringMVC支持多种视图技术,通过不同的视图解析器来解析不同类型的视图:

    1. InternalResourceViewResolver(JSP视图解析器)

    
        
        
        
    
    
    @Bean
    public ViewResolver jspViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setOrder(1);
        return resolver;
    }
    

    2. FreeMarkerViewResolver(FreeMarker视图解析器)

    
    
        
        
        
        
    
    
    
    
        
        
    
    
    @Bean
    public FreeMarkerViewResolver freeMarkerViewResolver() {
        FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
        resolver.setPrefix("");
        resolver.setSuffix(".ftl");
        resolver.setContentType("text/html;charset=UTF-8");
        resolver.setOrder(0);
        return resolver;
    }
    
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/ftl/");
        configurer.setDefaultEncoding("UTF-8");
        return configurer;
    }
    

    3. ThymeleafViewResolver(Thymeleaf视图解析器)

    
    
        
        
        
    
    
    
    
        
    
    
    
    
        
        
        
        
        
    
    
    @Bean
    public ViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        resolver.setCharacterEncoding("UTF-8");
        resolver.setOrder(0);
        return resolver;
    }
    
    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        return engine;
    }
    
    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setCacheable(false);
        return resolver;
    }
    

    4. ContentNegotiatingViewResolver(内容协商视图解析器)

    内容协商视图解析器可以根据请求的媒体类型选择合适的视图解析器:

    
        
        

    你可能感兴趣的:(SpringMVC,SpringMVC)