当前位置: 首页 > news >正文

Spring MVC程序开发

目录

1.什么是Spring MVC?

1.1MVC定义

1.2MVC和Spring MVC的关系

2.为什么要学习Spring MVC?

3.怎么学Spring MVC?

3.1Spring MVC的创建和连接

3.1.1创建Spring MVC项目

3.1.2@RequestMapping 注解介绍

3.1.3 @RequestMapping 是 post 还是 get 请求?

​编辑 3.1.4 @GetMapping 和 PostMapping

3.2 获取参数

3.2.1 传递单个参数

3.2.2 传递对象

3.2.3 表单参数传递/传递多个参数(⾮对象)

 

3.2.4 后端参数重命名(后端参数映射)

3.2.5 设置参数必传@RequestParam

3.2.6 @RequestBody 接收JSON对象

3.2.7 获取URL中参数@PathVariable

3.2.8 上传⽂件@RequestPart

3.2.9 获取Cookie/Session/header

​编辑 3.2.10返回Jason对象

4.请求转发和请求重定向


1.什么是Spring MVC?

官⽅对于 Spring MVC 的描述是这样的:
Spring Web MVC is the original web framework built on the Servlet API and has been included
comes from the name of its source module (
in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,”
Spring Web MVC is the original web framework built on the Servlet API and has been included
spring-webmvc as “Spring MVC”. ), but it is more commonly known as “Spring MVC”.
翻译成中文:
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为“Spring MVC”。
上述定义中我们可以得出两个关键信息:
1.Spring MVC是一个Web框架。
2.SPring MVC是基于servlet API构建的。
然⽽要真正的理解什么是 Spring MVC?我们⾸先要搞清楚什么是 MVC?

1.1MVC定义

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
Model(模型)是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
View(视图)是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的。
Controller(控制器)是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据,
控制⽤户输⼊,并向模型发送数据。

1.2MVC和Spring MVC的关系

MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项目就可以感知到用户的请求。

2.为什么要学习Spring MVC?

现在绝⼤部分的 Java 项⽬都是基于 Spring(或 Spring Boot)的,⽽ Spring 的核⼼就是 Spring
MVC。也就是说 Spring MVC 是 Spring 框架的核⼼模块,⽽ Spring Boot 是 Spring 的脚⼿架,因此我们可以推断出,现在市⾯上绝⼤部分的 Java 项⽬约等于 Spring MVC 项⽬,这是我们要学 Spring MVC 的原因。
在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架,如下图所
示:
简单来说,咱们之所以要学习 Spring MVC 是因为它是⼀切项⽬的基础,我们以后创建的所有
Spring、Spring Boot 项⽬基本都是基于 Spring MVC 的。

3.怎么学Spring MVC?

学习 Spring MVC 我们只需要掌握以下 3 个功能:
1. 连接的功能:将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的
Spring 程序。
2. 获取参数的功能:⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
3. 输出数据的功能:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户。
对于 Spring MVC 来说,掌握了以上 3 个功能就相当于掌握了 Spring MVC。

3.1Spring MVC的创建和连接

Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同(Spring MVC 使⽤ Spring Boot 的⽅式创建),在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项⽬。
在 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作⽤。

3.1.1创建Spring MVC项目

Spring MVC 可以基于 Spring Boot 创建,也就是创建⼀个 Spring Boot 项⽬,勾选上 Spring Web
模块即可,如下图所示:

接下来,创建⼀个类,实现⽤户到 Spring 程序的互联互通,具体实现代码如下:

@Controller//让Spring框架启动时加载
@ResponseBody//返回非页面数据
@RequestMapping("/user")//路由器规则注册
public class UserController {@RequestMapping("/hi")public String sayHi(){return "hi,Spring MVC";}
}

访问地址127.0.0.1:8081/user/hi就可以打印出想要的文字:

3.1.2@RequestMapping 注解介绍

@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路
由映射的。
路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类
的某个⽅法的过程就叫路由映射。

@RequestMapping的基础使用:

@Controller
@RequestMapping("/web")
public class WebController {@RequestMapping("/index")public String index(){
//        return "hello,springmvc";return "/index.html";}@ResponseBody
//    @RequestMapping(value = "/indexData",method = RequestMethod.POST)
//    @PostMapping("/indexData")@GetMapping("/indexData")public String indexData(){return "hello,springmvc";
//        return "/index.html";}
}
@RequestMapping 即可修饰类,也可以修饰⽅法,当修饰类和⽅法时,访问的地址是类 + 方法。
@RequestMapping 也可以直接修饰⽅法,代码实现如下:
@Controller
@ResponseBody
public class UserController1 {@RequestMapping("/hi")public String sayHi(){return "hi,Spring";}
}

3.1.3 @RequestMapping 是 post 还是 get 请求?

使⽤ PostMan 测试⼀下,默认情况下使⽤注解 @RequestMapping 是否可以接收 GET 或 POST 请求?

根据postman可以发现,@RequestMapping既有post请求,也有get请求。那如果只想要post或者get请求呢?

指定 GET/POST ⽅法类型:

我们可以显示的指定 @RequestMapping 来接收 GET的情况,如下所示:
public class UserController1 {@RequestMapping(value = "/hi2",method = RequestMethod.GET)public String sayHi2(){return "hi,Spring";}
}

 3.1.4 @GetMapping 和 PostMapping

get 请求的 3 种写法:
//写法1
@RequestMapping("/hi2")
//写法2
@RequestMapping(value = "/hi2",method = RequestMethod.GET)
//写法3
@GetMapping("hi2")

post请求的2种写法:

//写法1
@RequestMapping(value = "hi2",method = RequestMethod.POST)
//写法2
@PostMapping("hi2")

3.2 获取参数

3.2.1 传递单个参数

在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,⽐如以下代码:

    @RequestMapping("/get2")public String get2(String name){return "name:"+name;}

 在 postman 中访问⽅法:

3.2.2 传递对象

并且 Spring MVC 可以⾃动实现参数对象的赋值,⽐如Student对象:

@Data
public class Student {private Integer id;private  String name;private Integer age;private String nickname;
}

 传递对象代码实现:

    @RequestMapping("/get6")public String get6(Student student){return student.toString();}
前端访问:

最终执行结果:

3.2.3 表单参数传递/传递多个参数(⾮对象)

    @RequestMapping("/get3")public String get3(String name,Integer age){return "name:"+name+","+"age:"+age;}

重要说明:当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置是不影响后端获取参数的结果。

3.2.4 后端参数重命名(后端参数映射)

某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个time 给后端,⽽后端⼜是有 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值。
具体示例如下,后端实现代码:
    @RequestMapping("/m4")public Object method4(@RequestParam("time") String createtime){System.out.println("时间:" + createtime);return "/index.html";}

3.2.5 设置参数必传@RequestParam

上⾯的列⼦,如果我们是前端传递⼀个⾮ time 的参数,就会出现程序报错的情况,如下图所示:

这是因为后端已经声明了前端必须传递⼀个 time 的参数,但是前端没有给后端传递,我们查看
@RequestParam 注解的实现细节就可以发现端倪,注解实现如下:

⾮必传参数设置

如果我们的实际业务前端的参数是⼀个⾮必传的参数,我们可以通过设置 @RequestParam 中的
required=false 来避免不传递时报错,具体实现如下:
    @RequestMapping("/m4")public Object method4(@RequestParam(value = "time",required = false) String createtime) {System.out.println("时间:" + createtime);return "/index.html";}

3.2.6 @RequestBody 接收JSON对象

后端接收代码:

    @RequestMapping("/get8")public String get8(@RequestBody Student student){log.info(student.toString());return student.toString();}

3.2.7 获取URL中参数@PathVariable

后端实现代码:

    //取url地址@RequestMapping("/get9/{shopid}/{dealid}")public String get9(@PathVariable Integer shopid,@PathVariable("dealid") Integer dealId) {return "shopid:"+shopid+",dealId:"+dealId;}

字段需要保持一致,如果不一致,需要在@PathVariable设置参数的名称 

3.2.8 上传⽂件@RequestPart

后端代码实现:

    @RequestMapping("/get10")public String get10(@RequestPart("file") MultipartFile file) throws IOException {log.info(file.getOriginalFilename());file.transferTo(new File("D:/temp/"+file.getOriginalFilename()));return "success";}

3.2.9 获取Cookie/Session/header

后端代码实现:

使用@CookieValue注解来实现

    @RequestMapping("/get11")public String grt11(@CookieValue(name = "bite1",required = false) String bite){return "bite:"+bite;}

如果cookie中不存在这个key,就会报错

我们手动设置cookie,页面就会访问成功,所以cookie是可以造假的。

获取session:

后端代码实现:

    @RequestMapping("/get12")public String grt12(@SessionAttribute(required = false) String username){return "username:"+username;}

直接执行是获取不到session的,我们需要在后端中设置session 

设置session:

后端代码实现:
 

    @RequestMapping("/set1")public String set1(HttpSession session){session.setAttribute("username","bite");return "success";}

先执行这个方法,就会在前端获取到session:

 session在存储时就是以键值对的方式存储的

获取header:

后端代码实现:

    @RequestMapping("/get13")public String get13(@RequestHeader("User-Agent") String userAgent){return "userAgent:"+userAgent;}

 3.2.10返回Jason对象

    @RequestMapping("/get14")public Map<String,String> get14(){Map<String,String>map=new HashMap<>();map.put("k1","v1");map.put("k2","v2");map.put("k3","v3");map.put("k4","v4");map.put("k5","v5");return map;}

 

使用filder抓包:

4.请求转发和请求重定向

return 不但可以返回⼀个视图,还可以实现跳转,跳转的⽅式有两种:
forward 是请求转发;
redirect:请求重定向。
请求转发和重定向的使⽤对⽐:
@Controller
@RequestMapping("/index")
public class IndexController {/*** 请求转发* @return*/@RequestMapping("/forward")public String forward(){return "forward:/index.html";}@RequestMapping("/redirect")public String redirect(){return "redirect:/index.html";}
}
举例说明 forward 和 redirect
forward(请求转发)和 redirect(请求重定向)的区别,举例来说,例如,你告诉你妈妈,你想吃辣条,如果你妈妈,说好,我帮你去买,这就是 forward 请求转发;如果你妈妈让你⾃⼰去买,那么就是请求 redirect 重定向。
“转发”和“重定向”理解:在中国官⽅发布的内容越少事也越⼤, “转发”和“重定向”也是⼀样:字越
少,责任越⼤ 。转发是服务器帮转的,⽽重定向是让浏览器重新请求另⼀个地址。
forward 和 redirect 具体区别如下:
1. 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发。
2. 请求重定向地址发⽣变化,请求转发地址不发⽣变化。
3. 请求重定向与直接访问新地址效果⼀直,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问。

使用forward不会改变url

 

使用redirect会改变url。

 

相关文章:

Spring MVC程序开发

目录 1.什么是Spring MVC? 1.1MVC定义 1.2MVC和Spring MVC的关系 2.为什么要学习Spring MVC? 3.怎么学Spring MVC? 3.1Spring MVC的创建和连接 3.1.1创建Spring MVC项目 3.1.2RequestMapping 注解介绍 3.1.3 RequestMapping 是 post 还是 get 请求&#xff1f; ​…...

医疗知识图谱问答——文本分类解析

前言 Neo4j的数据库构建完成后&#xff0c;现在就是要实现医疗知识的解答功能了。因为是初版&#xff0c;这里的问题解答不会涉及深度学习&#xff0c;目前只是一个条件查询的过程。而这个过程包括对问题的关键词拆解分类&#xff0c;然后提取词语和类型去图数据库查询&#xf…...

JS关于多张图片上传显示报错不影响后面图片上传方法

关于多张图片上传或者下载显示报错后会程序会终止执行&#xff0c;从而影响后面图片上传。 解决方法&#xff1a; /*能正常访问的图片*/ const url https://2vimg.hitv.com/100/2308/0109/5359/dqKIZ7d4cnHL/81Vu0c.jpg?x-oss-processimage/format,webp; /*不能正常下载的图…...

MySQL踩坑之sql_mode的用法

目录 定义 报错重现 ​编辑 原因分析 sql_mode值说明 查看当前sql_mode 设置sql_mode 定义 什么是sql_mode?玩了这么久的MySQL语句࿰...

消息队列总结(4)- RabbitMQ Kafka RocketMQ高性能方案

1.RabbitMQ的高性能解决方案 1.1 发布确认机制 RabbitMQ提供了3种生产者发布确认的模式&#xff1a; 简单模式&#xff08;Simple Mode&#xff09;&#xff1a;生产者发送消息后&#xff0c;等待服务器确认消息已经被接收。这种模式下&#xff0c;生产者发送消息后会阻塞&am…...

websocket服务端大报文发送连接自动断开分析

概述 当前springboot版本&#xff1a;2.7.4 使用依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>现象概述&#xff1a; 客户端和服务端已经有心跳…...

想写几个上位机,是选择学c#还是 c++ qt呢?

C#基本也就上位机开发开发&#xff0c;另外做做日常用的小工具很方便。 结合PLC&#xff0c;以太网做上位机&#xff0c;这个基本上控制这块都比较有需求。 另外我们用C#也做一些工具的二次开发&#xff0c;感觉还行。 C用qt框架其实学习起来可能稍微复杂些&#xff0c;但是…...

JavaScript 简单实现观察者模式和发布-订阅模式

JavaScript 简单实现观察者模式和发布-订阅模式 1. 观察者模式1.1 什么是观察者模式1.2 代码实现 2. 发布-订阅模式2.1 什么是发布-订阅模式2.2 代码实现2.2.1 基础版2.2.2 取消订阅2.2.3 订阅一次 1. 观察者模式 1.1 什么是观察者模式 概念&#xff1a;观察者模式定义对象间…...

java集成短信服务 测试版 qq邮箱简单思路

java集成短信服务 注册一个帐号 使用的是容联云&#xff0c;百度搜一下官网 用手机注册一个帐号就行&#xff0c;免费体验不需要认证 注册后会有八块钱送&#xff0c;可以使用免费的给自己设置三个固定手机号发送短信&#xff0c;不需要认证。 此页面的 三个信息需要在代码中…...

#P0994. [NOIP2004普及组] 花生采摘

题目描述 鲁宾逊先生有一只宠物猴&#xff0c;名叫多多。这天&#xff0c;他们两个正沿着乡间小路散步&#xff0c;突然发现路边的告示牌上贴着一张小小的纸条&#xff1a;“欢迎免费品尝我种的花生&#xff01;――熊字”。 鲁宾逊先生和多多都很开心&#xff0c;因为花生正…...

Elasticsearch和Kibana的安装及验证

金翅大鹏盖世英&#xff0c;展翅金鹏盖世雄。 穿云燕子锡今鸽&#xff0c;踏雪无痕花云平。 ---------------- 2023.7.31.101 ----------------- 本文密钥&#xff1a;365 Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎&#xff0c;常用来进行全文检索、…...

细讲TCP三次握手四次挥手(一)

计算机网络体系结构 在计算机网络的基本概念中&#xff0c;分层次的体系结构是最基本的。计算机网络体系结构的抽象概念较多&#xff0c;在学习时要多思考。这些概念对后面的学习很有帮助。 网络协议是什么&#xff1f; 在计算机网络要做到有条不紊地交换数据&#xff0c;就必…...

【linux-zabbix】zabbix-agent启动报错:Daemon never wrote its PID file. Failing.

背景&#xff1a; 发现有部分的agent失联&#xff0c;排查发现机器正常&#xff0c;agent没起来。 排查日志发现&#xff1a; # journalctl -xe -- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel -- -- Unit zabbix-agent.service has begun start…...

【微信小程序】初始化 wxCharts,调用updateData动态更新数据

要初始化 wxCharts&#xff0c;你需要按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保已将 wx-charts.js 文件正确引入到小程序的相应页面或组件中。可以通过以下方式引入&#xff1a; const wxCharts require(../../../../components/wx-charts.js);请根据你的项目…...

【C语言初阶(19)】实用的 VS 调试技巧

文章目录 Ⅰ 调试的介绍Ⅱ 常用调试快捷键Ⅲ 调试的时候查看程序当前信息⒈查看临时变量的值⒉查看内存信息⒊查看调用堆栈⒋查看汇编信息⒌查看寄存器信息 Ⅳ 观察形参指针指向的数组Ⅴ 易于调试的代码该如何编写⒈const 修饰指针变量⒉良好代码示范 Ⅵ 编程中常见的错误 Ⅰ 调…...

虚拟机之间配置免密登录

目录 一、配置主机名映射 二、虚拟机配置SSH免密登录 三、验证 一、配置主机名映射 即修改/etc/hosts文件&#xff0c;将几台服务器和主机名进行映射。 注意每台服务器都要进行同样的配置。这样在各自服务器下&#xff0c;我们就可以通过主机名访问对应的ip地址了。 当然&…...

【contenteditable属性将元素改为可编辑状态】

元素添加contenteditable属性之后点击即可进入编辑状态 像这种只修改一条属性不必再打开弹框进行编辑&#xff0c;使用contenteditable会很方便 添加失焦、回车、获焦事件 如 <p :contenteditable"item.contenteditable || false"keydown.enter"key($event…...

Android 第三方库CalendarView

Android 第三方库CalendarView 根据需求和库的使用方式&#xff0c;自己弄了一个合适自己的日历&#xff0c;仅记录下&#xff0c;方便下次弄其他样式的日历。地址 需求&#xff1a; 只显示当月的数据 默认的月视图有矩形的线 选中的天数也要有选中的矩形框 今天的item需要…...

钉钉群消息推送

1. 添加钉钉群机器人 PC端登录&#xff08;当前版本手机端无法进行推送关键词设置&#xff09;&#xff0c;群设置--> 机器人 --> webhook进行安全设置复制webhook对应的url 2. 群消息推送 钉钉群消息支持纯文本和markdown类型 2.1 调用示例源码 import com.alibaba.…...

css clip-path 属性介绍

circle() – 圆 语法&#xff1a;circle( [<shape-radius>]? [at <position>]? ) shape-radius 圆的半径 position 圆的中心点位置 使用方法&#xff1a; clip-path: circle(); // 以元素的中心点为圆的中心点&#xff0c;最小宽度一半为圆的半径。clip-path: c…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...