牛客网项目1.4 springMVC入门


Spring MVC入门

做web开发,首先对web有一个大体的了解,下图第一部分是网页的组成成分,我们看到的各式各样的网页就是浏览器根据这些文件渲染出来的,第二部分http协议,是应用层的协议,用来传输上方的这些文件,再下面是传输层和网络层的相关内容,具体的自行学习。

img

HTTP协议

首先了解http协议,这里提供Mozilla的官方文档进行学习

  • HyperText Transfer Protocol
  • 用于传输HTML等内容的应用层协议
  • 规定了浏览器和服务器之间如何通信,以及通信时的数据格式。

在这里截取一段了解一下

HTTP 流

当客户端想要和服务端进行信息交互时(服务端是指最终服务器,或者是一个中间代理),过程表现为下面几步:

  1. 打开一个TCP连接:TCP连接被用来发送一条或多条请求,以及接受响应消息。客户端可能打开一条新的连接,或重用一个已经存在的连接,或者也可能开几个新的TCP连接连向服务端。
  2. 发送一个HTTP报文:HTTP报文(在HTTP/2之前)是语义可读的。在HTTP/2中,这些简单的消息被封装在了帧中,这使得报文不能被直接读取,但是原理仍是相同的。
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
123
  1. 读取服务端返回的报文信息:
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html

<!DOCTYPE html... (here comes the 29769 bytes of the requested web page)
12345678910
  1. 关闭连接或者为后续请求重用连接。

当HTTP流水线启动时,后续请求都可以不用等待第一个请求的成功响应就被发送。然而HTTP流水线已被证明很难在现有的网络中实现,因为现有网络中有很多老旧的软件与现代版本的软件共存。因此,HTTP流水线已被在有多请求下表现得更稳健的HTTP/2的帧所取代。

我们在浏览器实际操作来查看一下,以上面推荐的网站为例,用谷歌浏览器打开,在页面上右键-检查,然后刷新一下,就可以看到右边出现了一堆请求,我们随便点一个就可以看到它的header,cookies之类的信息。那为什么有这么多请求呢,因为在浏览器解析的过程中,会发现html里面引用了其它资源,那么就会再请求这个资源文件。

img

Spring MVC

  • 三层架构
    • 表现层、业务层、数据访问层
  • MVC
    • Model:模型层
    • View:视图层
    • Controller:控制层
  • 核心组件
    • 前端控制器:DispatcherServlet

img

**要注意,服务端三层架构和MVC三层不是一一对应关系。*可以从上图看到大致的关系,Controller接收请求中的数据,调用业务层去处理,得到的数据封装到model传给视图层,视图层生成html返回给浏览器。在这个过程中,有一个核心组件,前端控制器*DispatcherServlet。从spring最新版本的文档找了一个图来解释,大体结构如下。

img

上面这个图不够具体,在老版的文档找到这个图,可以结合来看,大体流程就是请求来了由前端控制器解析,handlerMapping根据我们的注解找到相应的controller去处理,返回model给前端控制器,前端控制器再调用试图解析器处理,然后然会响应。解释得不够准确,深入了解建议spring官网查看文档

img

Thymeleaf模板引擎

在前面我们写了简单的示例,都是返回了字符串给浏览器,没有返回过网页,我们需要借用一个工具-模板引擎,那么比较流行的就是thymeleaf。

  • 模板引擎
    • 生成动态的HTML
  • Thymeleaf
    • 倡导自然模板,即以HTML文件为模板
  • 常用语法
    • 标准表达式,判断与循环,模板的布局

img

案例演示

我们首先在application.properties里面关掉thymeleaf的缓存。

#ThymeleafProperties
spring.thymeleaf.cache=false
12

那么上面这句话是什么意思呢,ctrl+n我们搜索一下thymeleaf的配置类也就是ThymeleafProperties。我截取了前面的一段代码,可以看到这个类使用了@ConfigurationProperties注解配置了前缀,我们使用这个前缀加上.cache实际是修改了ThymeleafProperties的cache属性,在我截取的代码最后一行可以看到。

@ConfigurationProperties(
    prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
    private static final Charset DEFAULT_ENCODING;
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    private boolean checkTemplate = true;
    private boolean checkTemplateLocation = true;
    private String prefix = "classpath:/templates/";
    private String suffix = ".html";
    private String mode = "HTML";
    private Charset encoding;
    private boolean cache;
1234567891011121314

同理,我们修改服务器启动端口,也可以查看对应的配置类,还可以修改其它参数,这里不做演示,可以查阅spring文档。

# ServerProperties
server.port=8080
12

案例一

这个案例我们演示如何获取请求和返回响应,具体每一行的意思我在注释里写好。在http方法里面,传入了servlet的request和response,我们对这两个对象进行操作。

package com.neu.langsam.nowcoder.controller;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

@Controller
@RequestMapping("/alpha")
public class AlphaController {

    @RequestMapping("http")
    public void http(HttpServletRequest request, HttpServletResponse response){
        //获取请求数据
        System.out.println(request.getMethod());//打印请求方法
        System.out.println(request.getServletPath());//打印请求路径
        Enumeration<String> enumeration=request.getHeaderNames();//古老的迭代器获取请求头
        while (enumeration.hasMoreElements()){
            String name=enumeration.nextElement();
            String value=request.getHeader(name);
            System.out.println(name+":"+value);
        }

        System.out.println(request.getParameter("code"));//打印请求参数

        //返回响应数据
        response.setContentType("text/html;charset=utf-8");//设置返回数据类型
        //java7吼将创建写在try()内会自动finnal销毁
        try (
                PrintWriter writer= response.getWriter();
                )
        {
            writer.write("<h1>牛客网</h1>");//利用writer写入html数据
            writer.write("<h2>牛客网</h2>");
            writer.write("<h3>牛客网</h3>");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1234567891011121314151617181920212223242526272829303132333435363738394041424344

启动项目,在浏览器中访问,可以看到页面显示了我们写入的html,同样可以打开检查,查看我们这一次的请求情况,查看idea控制台,可以看到输出了我们想要获取的数据,还可以在链接后接问号传参,传参的不做演示。

http://localhost:8080/alpha/http?code=test

img

img

案例二

案例一演示了比较基础的方法去操作,但实际上已经封装好了简单的方法,接下来演示如何更加简单的去处理。现在假设查询学生数据,学生比较多采用分页显示,那么就需要传入current参数告诉后端现在需要第几页以及limit参数规定每页多少条数据。那么怎么处理这样的请求呢。

GET 方法
  • 首先还是使用注解进行配置
    • @RequestMapping配置了访问路径和访问方法,这里我们规定了只接受GET方法,相应的还有POST,DELETE等方法自行了解。GET方法通常用来查询数据,POST通常添加或者修改数据。
    • @ResponseBody注解表示将方法返回的java对象转换为json或者xml格式的数据,直接写入响应的body区而不走视图解析器。
  • 在第一个getStudents方法中,我们使用了@RequestParam注解规定了请求参数,对于current参数,我们规定了不是必须的,默认值设置为1。我们就可以在地址栏传入参数,/students?current=2&limit=20
  • 在第二个getStudent方法中,使用了@PathVariable注解,同时路径中使用了{id},这样就可以直接/student/112传入id参数

这两种方法根据不同的需求,使用相应的方法。

//GET请求
    // /students?current=1&limit=20
//分页显示当前第一页每页20条数据
    @RequestMapping(path = "/students",method = RequestMethod.GET)
    @ResponseBody
    public String getStudents(
            @RequestParam(name = "current",required = false,defaultValue = "1") int current,
            @RequestParam(name = "limit",required = false,defaultValue = "10") int limit){
        System.out.println(current+" "+limit);
        return "some students";
    }

    // /student/123
    @RequestMapping(path = "/student/{id}",method = RequestMethod.GET)
    @ResponseBody
    public String getStudent(@PathVariable("id") int id){
        System.out.println(id);
        return "a student";
    }
123456789101112131415161718

img

img

POST方法

上面说到post方法通常用于增加或者修改数据,那么我们就需要有一个表单去填写数据。在static目录下新建html目录,在html目录下新建一个html文件。这是一个简单的网页,两个输入框加上一个提交按钮,规定了提交的路径是/alpha/student。

Get也可以传参数但是会回显到url,且url长度有限,通常提交数据用post

img

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>增加学生</title>
</head>
<body>
    <form method="post" action="/alpha/student">
        <p>
            姓名:<input type="text" name="name">

        </p>
        <p>
            年龄:<input type="text" name="age">
        </p>
        <p>
            <input type="submit" value="保存">
        </p>
    </form>
</body>
</html>
123456789101112131415161718192021

然后我们在controller里写处理这个访问路径的方法,这里需要注意一点,前面我们写了/student/{id}路径,比我们这里访问的/student多了一层,所以要另外写一个处理/student的方法。要获取浏览器提交的参数,我们只需要传入和浏览器表单里每一项一样名字的参数就行,spring会帮我们自动匹配。比如html里input年龄的参数名我们规定为age,那么处理请求时传入一个名为age的参数就可以接收到。

    //POST请求
    @RequestMapping(path = "/student",method = RequestMethod.POST)
    @ResponseBody
    public String saveStudent(String name,int age){
        System.out.println(name+"+"+age);
        return "success";
    }
1234567

img

img

img

案例三

演示了这么多,thymeleaf模板引擎怎么用呢。首先创建一个模板,在templates下创建demo目录,然后创建一个view.html,那可能会问和前面的静态html有啥区别呢。在html标签里使用xmlns规定了模板的来源,在p标签里使用thymeleaf的语法,意思是让页面的文字等于我们传入的那么和age的值。

img

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Teacher</title>
</head>
<body>
    <p th:text="${name}"></p>
    <p th:text="${age}"></p>
</body>
</html>
1234567891011DispatchServlet

再在controller里写相应方法处理。返回数据类型设为ModelAndView,也就是把模型和视图作为一个对象返回,DispatchServlet调用conrtoller某个方法,方法返回modelandview数据提交给模板引擎,模板引擎渲染成html。为了简单直接给了一个固定数据,并设置了模板的路径,demo路径下的view.html,这里就不用写后缀了,在浏览器中访问相应地址就行。

    //响应HTML数据
    @RequestMapping(path = "/teacher",method = RequestMethod.GET)
    public ModelAndView getTeacher(){
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.addObject("name","张三");//添加模型
        modelAndView.addObject("age","18");
        modelAndView.setViewName("/demo/view");//设置视图
        return modelAndView;
    }
123456789

img

同样,我们在检查里查看这次请求,可以看到响应的内容是一个html文件。

img

还有另外一种方式,效果一样。这个方法是从容器中获取模型的引用,修改模型的值,并直接返回视图的路径。对比来看第二种方式简洁一点。

上一种的简化
返回类型String指的是返回的路径
    @RequestMapping(path = "/school",method = RequestMethod.GET)
    public String getSchool(Model model){
        model.addAttribute("name","东北大学");
        model.addAttribute("age",97);
        return "/demo/view";
    }
123456

案例四

还有一种比较常见的情况,我们需要给浏览器返回JSON数据,通常是在一个异步请求中,实际上在前面我们已经使用过了。异步请求是什么呢,比如注册bilibili,当我输入一个昵称时,网页会提醒我昵称被占用,但是网页其它部分时没有刷新变动。当前网页不动不刷新,当是它悄悄访问了服务器

img

 //响应JSON数据(异步请求)
    //java对象-> JSPON字符串 ->Js对象
    @RequestMapping(path = "/emp",method = RequestMethod.GET)
    @ResponseBody
    public Map<String,Object> getEmp(){
        Map<String,Object> emp=new HashMap<>();
        emp.put("name","张三");
        emp.put("age","18");
        emp.put("salary",8000);
        return emp;
    }
1234567891011

返回JSON实际就是使用了前面提到的@ResponseBody注解。

img

同样,也可以返回一组数据。

@RequestMapping(path = "/emps",method = RequestMethod.GET)
    @ResponseBody
    public List<Map<String,Object>> getEmps(){
        List<Map<String,Object>> list=new ArrayList<>();
        Map<String,Object> emp=new HashMap<>();
        emp.put("name","张三");
        emp.put("age","18");
        emp.put("salary",1000);
        list.add(emp);
        emp=new HashMap<>();
        emp.put("name","李四");
        emp.put("age","18");
        emp.put("salary",2000);
        list.add(emp);
        emp=new HashMap<>();
        emp.put("name","王五");
        emp.put("age","18");
        emp.put("salary",3000);
        list.add(emp);
        emp=new HashMap<>();
        emp.put("name","帅哥");
        emp.put("age","18");
        emp.put("salary",4000);
        list.add(emp);
        return list;
    }
1234567891011121314151617181920212223242526

img

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值