IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringMVC -> 正文阅读

[Java知识库]SpringMVC


大家好呀,我是小笙,今天我来分享一下我的SpringMVC的学习笔记!

Spring Web MVC

概述

SpringMVC 是 WEB 层框架 [SpringMVC 接管了 Web 层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持 MVC 的开发模式/开发架构]

SpringMVC 采用低耦合的组件设计方式,具有更好扩展和灵活性

支持 REST 统一格式的 URL 请求

SpringMVC 是基于 Spring 框架进行开发的,核心组件如下

image-20220908143018595

SpringMVC 执行流程

image-20220917193945273

基本注解

@RequestMapping

概念:可以用来修饰类和方法(用来指定访问的 url,修饰类就类似同个类下访问方法的共同路径,修饰方法就是访问该方法的特有路径)

举例加深理解

@Controller
@RequestMapping(value = "/user") // 修饰类
public class UserServlet {
    
    // 访问路径: http://ip:port/工程路径/user/login
    @RequestMapping(value = "/login") // 修饰方法
    public String login(){
        System.out.println("login ok....");
        return "login_ok";
    }
    
    // 访问路径: http://ip:port/工程路径/user/bug
    @RequestMapping(value = "/bug") // 修饰方法
    public String bug(){
        System.out.println("bug ok....");
        return "bug_ok";
    }
}

注解源码分析

主要讲解一下 path、method、params

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    // 上文的路径指定,以下为路径匹配通配符(注意路径不能重复)
    // ?:匹配文件中的一个字符
    // *:匹配文件名中的任意字符
    // **:匹配多层路径
    @AliasFor("value")
    String[] path() default {}; 
    
    // 指定范围的方法:  GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE; (默认支持 GET,POST)
    // 注意:我们也可以通过注解就指定是什么请求形式,如下
    // @GetMappping、@PostMapping、@PutMapping、@DeleteMapping等等
    RequestMethod[] method() default {}; 

    // 表示请求必须包含名为"xx"的请求参数,但是值不做限定,格式如:params="xxx" 或者 params = {"xxx","yyy"}
    // params="xxx=100" 表示必须给一个 xxx 参数,并且值必须为100,不然报 400 错误
    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

@RequestHeader

概述:获取请求头信息比如:host、accept-encoding等等

@Controller
public class UserServlet {

    @GetMapping(value = "/test")
    // 不需要请求携带数据
    public String test(@RequestHeader("host") String host){
        System.out.println("host:" + host);
        return "ok";
    }
}

@PathVariable

概念:路径变量,就是在传 url 的时候把值也顺便传进来了

通过例子对比一下就清楚了

// 不使用该注解
// 请求url: http://localhost:8080/test?bookId=100
@RequestMapping(value = "/test") 
public String book(int bookId){
    System.out.println("书本id:" + bookId);
    return "ok";
}

// 使用该注解 @PathVariable
// 请求url: http://localhost:8080/test/100
@RequestMapping(value = "/test/{bookId}")  // 通过 url 传入值
public String book(@PathVariable int bookId){
    System.out.println("书本id:" + bookId);
    return "ok";
}

@RequestParam

概述:可以用来指定某形参的接收参数名

@Controller
public class UserServlet {
    /**
     * @RequestParam 指定接收的参数名
     * 不添加注解 url: http://localhost:8080/web工程路径/test?param="steven" 
     * 添加注解 url:http://localhost:8080/web工程路径/test?par="steven" 
     * required 指的是该形参是否是必须的
     */
    @GetMapping(value = "/test")
    public String test(@RequestParam(value = "par",required = false) String param){
        System.out.println(param);
        return "ok";
    }
}

@ModeAttribute

概述:前置处理,调用其他 Handler 的时候,都会先调用这个方法

@ModelAttribute
public void beforeHandler(){
    System.out.println("前置处理注解");
}

接收请求过程

请求方式 Rest

概述

概述:常用请求方式(GET、POST、PUT、DELETE) 分别对应 crud 的操作

但是由于浏览器发送的请求只支持 POST、GET 请求,所以我们需要添加 Spring 的 HiddenHttpMethodFilter 过滤器进行转换

注意:只能把浏览器发送的 POST 的请求转换成对应Spring 的 PUT、DELETE 请求

<!-- 
	配置 HiddenHttpMethodFilter 过滤器
	作用:把浏览器发送的 POST 的请求转换成对应 Spring 的 PUT、DELETE 请求
	注意:是在 web.xml 配置过滤器
-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!--
	加入两个常规配置
	1.能支持 SpringMVC 高级功能,比如 JSR303 校验,映射动态请求
	2.将 SpringMVC 不能处理的请求交给 Tomcat,比如请求 css,js等
	注意:在 spring.xml 配置信息,引入 mlns:mvc="http://www.springframework.org/schema/mvc"
-->
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
HiddenHttpMethodFilter 源码解析
// 如果想要发送 delete 或者 put 请求,则需要发送 post 请求并且携带 _method = delete 类似参数
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";

    // 把浏览器发送的 POST 的请求转换成对应 Spring 的 PUT、DELETE 请求
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }

        filterChain.doFilter((ServletRequest)requestToUse, response);
    }

    // 类加载初始化方法含有的列表:PUT、DELETE、PATCH
    static {
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }
}

接收 Java 对象

概述:就是将接收到的数据,赋值到对象的属性值上(注意对象的属性需要有 get 和 set 方法)并封装成对象(底层就是反射和注解)

例子如下

@Controller
public class UserServlet {
    @GetMapping(value = "/test")
    public String test(Obj obj){
        System.out.println(obj);
        return "ok";
    }
}

// @Data 是 lombok 类中用来代替 get、set、toString方法的注解
@Data
class Obj {
    private Integer id;
    private String name;
    // 如果属性是对象的话,请求的参数可以通过 obj2.id 的形式来传参(级联操作)
    private Obj2 obj2;
}

@Data
class Obj2 {
    private Integer id;
    private String name;
}

注意:前端请求参数赋值到接收的对象的属性上时候,SpringMVC 机制里还实现了将该对象放入到 request 请求域中,可以将这些数据带给需要显示参数的页面(属性名为类名首字母小写)

存放数据域的操作

存放数据到 request 域

方式 1:通过 HttpServletRequest 放入 request 域

需要导入类 servlet-api.jar 包

@RequestMapping(value = "/test") 
public String test(Master master, HttpServletRequest request, HttpServletResponse response) { 
    // 可以手动放入 request 域中
    request.setAttribute("address", "杭州"); 
    // 从本质看:请求响应的方法 return "xx", 是返回了一个字符串,其实本质是返回了一个 ModelAndView 对象,只是默认被封装起来了
    return "ok"; 
}

方式2:通过请求的方法参数 Map<String,Object> 放入 request 域

@RequestMapping(value = "/test") 
public String test(Master master, Map<String,Object> map) { 
    // 原理分析:springmvc 底层会遍历 map ,然后将 map 中的数据放入到 request
    map.put("address", "杭州"); 
    // 如果已经存放在 request 域中的数据将会被覆盖
    map.put("master", null); 
    return "ok"; 
}

方式 3: 通过返回 ModelAndView 对象实现 request 域数据

@GetMapping(value = "/test")
public ModelAndView test(Monster master){
    // 创建 ModelAndView 对象
    ModelAndView modelAndView = new ModelAndView(); 
    // 将需要数据放入到该对象,并将该数据放入到 request 域中
    modelAndView.addObject("address","杭州");
    // 指定跳转的页面名为 ok 的页面
    modelAndView.setViewName("ok");
    // 返回结果
    return modelAndView;
}

存放数据到 session 域

方式 1:通过 HttpSession 放入 session 域

@RequestMapping(value = "/test") 
public String test(Master master, HttpSession session) { 
    session.setAttribute("address","杭州");
    return "ok"; 
}

自定义视图

工作流程概述

  1. SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id
  2. SpringMVC 调用 BeanNameViewResolver 解析视图(从 IOC 容器中获取返回 id 值对应的 bean, 即自定义的 View 的对象)
  3. SpringMVC 调用自定义视图的 renderMergedOutputModel 方法渲染视图

注意:如果在 SpringMVC 调用目标方法, 返回自定义 View 在 IOC 容器中的 id 不存在, 则仍然按照默认的视图处理器机制处理

代码实现

// 自定义视图
@Component(value="view")
public class MyView extends AbstractView { 
    @Override 
    protected void renderMergedOutputModel(Map<String, Object> map, HttpServletRequest req, HttpServletResponse resp) throws Exception { 
        System.out.println("进入到自己的视图"); 
        // 确定到哪个页面去,默认的视图解析机制就无效 
        req.  
            ("/WEB-INF/pages/my_view.jsp").forward(req, resp); 
    } 
}

// handler 控制层
@RequestMapping(value = "/test") 
public String test(Master master, HttpSession session) {
    return "view"; 
}

配置视图解析器

<!-- 
    配置可以解析自定义的视图的解析器 BeanNameViewResolver
	默认视图解析器:InternalResourceViewResolver
-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> 
    <!--
		设置优先级,默认优先级很低值 Integer.MAX_VALUE 
 		视图解析优先级高,order 值越小,优先级越高 
	-->
    <property name="order" value="99"></property> 
</bean>

源码分析

BeanNameViewResolver 的源码分析

public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {
    // 优先级设置
    private int order = 2147483647;

    public BeanNameViewResolver() {
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public int getOrder() {
        return this.order;
    }

    // 视图解析器
    @Nullable
    public View resolveViewName(String viewName, Locale locale) throws BeansException {
        ApplicationContext context = this.obtainApplicationContext();
        // ioc 容器中是否存在该 beanName 对象名
        if (!context.containsBean(viewName)) {
            return null; 
            // 查看类型是否匹配,因为 AbstractView 实现了 View 接口,所以就是查看是否继承了 AbstractView 类
        } else if (!context.isTypeMatch(viewName, View.class)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Found bean named '" + viewName + "' but it does not implement View");
            }
            return null;
        } else {
            // 获取到 View 对象
            return (View)context.getBean(viewName, View.class);
        }
    }
}

InternalResourceViewResolver 默认视图源码分析

注意:默认视图解析器(优先级高)一旦执行,则不再执行自定义视图解析器

public class InternalResourceViewResolver extends UrlBasedViewResolver {
    private static final boolean jstlPresent = ClassUtils.isPresent("javax.servlet.jsp.jstl.core.Config", InternalResourceViewResolver.class.getClassLoader());
    @Nullable
    private Boolean alwaysInclude;

    public InternalResourceViewResolver() {
        Class<?> viewClass = this.requiredViewClass();
        if (InternalResourceView.class == viewClass && jstlPresent) {
            viewClass = JstlView.class;
        }

        this.setViewClass(viewClass);
    }

    // 配置文件中有配置该信息
    public InternalResourceViewResolver(String prefix, String suffix) {
        this();
        // 获取到前缀和后缀 拼接 url
        // <property name="prefix" value="/pages/"/>
        // <property name="suffix" value=".jsp"/>
        this.setPrefix(prefix);
        this.setSuffix(suffix);
    }

    public void setAlwaysInclude(boolean alwaysInclude) {
        this.alwaysInclude = alwaysInclude;
    }

    protected Class<?> requiredViewClass() {
        return InternalResourceView.class;
    }

    protected AbstractUrlBasedView instantiateView() {
        return (AbstractUrlBasedView)(this.getViewClass() == InternalResourceView.class ? new InternalResourceView() : (this.getViewClass() == JstlView.class ? new JstlView() : super.instantiateView()));
    }

    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        // 通过前缀 + viewName + 后缀,拼接出 url 并创建出 view 对象
        InternalResourceView view = (InternalResourceView)super.buildView(viewName);
        if (this.alwaysInclude != null) {
            view.setAlwaysInclude(this.alwaysInclude);
        }

        view.setPreventDispatchLoop(true);
        return view;
    }
}

请求转发、重定向

注意事项:

  1. 默认处理器返回的方式是请求转发,然后用视图解析器来进行处理(如上述)

  2. 可以在目标方法指定重定向或转发的 url 地,可以请求转发到 /WEBN-INF 目录中的资源

    // handler 控制层
    @RequestMapping(value = "/test") 
    public String test() {
        // 指定转发的 url 地址
        return " v  :url"; 
    }
    
  3. 可以使用重定向,但是注意不能重定向到 /WEBN-INF 目录中的资源

    // handler 控制层
    @RequestMapping(value = "/test") 
    public String test() {
        return "redirect:url"; 
     }
    
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:01:38  更:2022-10-22 21:04:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/10 18:26:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码