SpringMVC入门
时间:2023-01-22 22:00:00
1.什么是MVC?
MVC根据模型、视图、控制器对软件架构的理念进行划分。
其中的M:Model,模型层,指的是JavaBean,用于数据处理。
其中javabean有两种:
一是实体类Bean,用于存储数据,如常见数据User类,student类;
二是处理业务。Bean,一般指工程Service或Dao处理业务逻辑和数据访问的对象。
V:View,视图层是与用户互动的页面,显示数据等。html页面或jsp页面。
C:Controller,工程中的控制层servlet,用于接受请求和响应结果。
MVC结构执行流程如下图所示:
2.什么是SpringMVC?
SpringMVC是Spring框架的一个分支,该springMVC框架的主要功能是接收浏览器的请求响应,处理数据,然后返回页面显示,可以理解为和Servlet同样的工作。
3.为什么要用?SpringMVC?
我们在使用Servlet处理,需要做的是接收参数,业务处理,返回结果(页面跳转或返回JSON数据)。其中各Servlt除了不同的业务处理,接收参数和返回结果是相同的,所以我们可以包装这两个功能,SpringMVC就帮我们完成了这样的封装,因此SpringMVC也是对Servlet优化。
4.如何使用SpringMVC
(1)使用IDEA创建maven-web项目,点击File->new->project->选择maven->按图示勾选->next->填写项目名称,完成创建。
项目创建完成后,需要自动生成web.xml替换文件内容如下:
(2)引入springmvc依赖
org.springframework spring-webmvc 5.2.15.RELEASE
(3)将DispatcherServlet注册到web.xml文件上
DispactherServlet org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml DispactherServlet /
(4)创建springmvc.xml配置文件
我们选择在main->resources该文件在目录下创建,创建方法如下图所示(注:项目自动生成的目录中没有java和resources目录,需要手动创建)。
这里需要注意的是,如果没有引入,springmvc依赖不会出现Spring Config选项。
(5)写入包扫描的配置信息
(6)创建controller设置访问名称,在服务器上部署项目后,通过访问"hello01"访问此方法。
//该注释标记为处理层,有此注释,springmvc会根据配置信息对该类进行扫描 @Controller public class HelloController { ///将此方法上映射请求路径。通过这条路径访问这种方法 @RequestMapping(value = "/hello01") public String hello01(){ System.out.println("业务处理"); return "hello01.jsp"; //响应页面 } }
5.SpringMVC操作流程;
(1)根据上述配置,客户端有请求http://localhost:8080/springmvc01/hello01;
(2) 来到tomcat服务器。
(3)springmvc前端控制器DipatcherServlet接受所有请求。
(4)查看您的请求地址和哪个地址@RequestMaping匹配。
(5) 执行对应的方法。方法会返回一个字符串。springmvc将字符串分析为要转发的网页。
(6)通过视图解析器拼接字符串。
(7)获取拼接地址,找到相应的网页。
(8) 向客户渲染网页
6.如何在controller层接收请求参数
(1)接收少量参数时,可采用相同的方法形参数和请求参数名完成参数的接收
(2)当接收到的参数数量较大时,如提交表单数据,我们可以包装实体类接收这些参数
注意:
由于浏览器的默认编码和idea默认编码是可能的一致,将会出现乱码的问题,这里就需要通过编码过滤器来解决该问题,这里我们可以自定义过滤器,来完成编码的过滤;但是自己编写的过滤器,需要注意一点,jdk1.8之前的版本需要重写接口中的init和destory方法,而jdk1.9及以后则不需要重写这两个方法。
除了自定义过滤器外,springmvc也为我们提供了一个过滤器,我们只需要在web.xml文件中添加配置信息则可以进行使用:
EncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
EncodingFilter
/*
(3)当接受的参数含有日期类型时,则可以在时间类型的属性上添加@DateTimeFormat(pattern="yyyy-MM-dd"),并在springmvc.xml配置文件中开启特殊注解驱动:
7.如何将controller层的数据返回页面并展示
在servlet中返回浏览器的数据可以存储在request、session中,而springmvc中的controller也就相当于servlet的功能,因此我们也可以将数据存储在request和session中,在浏览器中通过:EL表达式。${scope.key} 来获取存储的数据,但是当使用request时,则将数据与服务器进行了绑定,数据安全性会降低,因此springmvc提供了一个Model类来存储返回的数据,但是该类存储的返回默认为request,需要通过注解@SessionAttributes(value = {"user"})来将该key值设置为session范围。
8.使用重定向跳转
@RequestMapping("list5")
public String list5(){
System.out.println("使用了重定向跳转!");
//在返回的页面前添加redirect:字样,springmvc则会进行重定向跳转
return "redirect:list.jsp";
}
9.springmvc如何返回json数据?
(1)在我们实现异步请求、ajax请求时都要求我们返回json数据
(2)在Servlet中我们通过FastJson,手动将java数据转化为json数据,同时需要将our.print(json)输出json数据,并需要将out进行关闭。
(3)而springmvc不需要我们手动进行json数据的转换,它内置了一个jackson的jar来实现json数据的转化;
9.1引入jackson依赖
com.fasterxml.jackson.core
jackson-databind
2.13.2.2
9.2 引入依赖后的执行
当返回的数据为javabean对象时,该jar包则会自动将该对象数据转化为json数据。
注意:若返回的对象中包含时间属性,则需要在该属性上添加@JsonFormat(pattern="yyyy-MM-dd")注解,否则所显示的值为从1970年至数据值之间的毫秒数。
10.springmvc的全局异常处理
在执行各类方法时可能会遇到各种异常,而且可能会有多种方法出现相同的异常,我们可以通过一个全局异常处理的操作,对各种方法中的异常进行处理,避免异常处理代码的反复编写。
如何使用全局异常处理?
(1)创建一个异常类:并在类前添加 @ControllerAdvice注解
//表示该为类controller的异常处理类
@ControllerAdvice
public class AllExceptinHandle {
//当发生RuntimeException就会触发该方法
@ExceptionHandler(value = RuntimeException.class)
public String error01(){
return "error01.jsp";
}
//当发生Exception就会触发该方法
@ExceptionHandler(value = Exception.class)
public String error02(){
return "error02.jsp";
}
}
(2)在springmvc.xml配置文件中的包扫描部分添加上该类所在包的路径;
(3)在工程运行过程中如果发生对应异常,则会跳转到该类中,执行对应异常处理的方法。
11.springmvc的拦截器
过滤器: 过滤掉某些资源,
拦截器只会拦截controller层的资源路径。
使用拦截器:
(1)创建类,实现HandlerInterceptor接口
public class InterceptorOne implements HandlerInterceptor {
//拦截器的处理方法。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("经过了该拦截器");
//该方法的返回值为布尔类型,其中true:表示该拦截器放行 false:则不放行
return true;
}
}
(2)将该拦截器注册到springmvc配置文件中
12.文件上传
文件上传的原理:一般来说,将文件上传后,数据库中存储的应该是该文件的访问路径,而当我们需要使用该数据时,根据数据库中所存储的文件访问路径继续该文件的使用。
12.1 文件上传至本地服务器
(1)引入文件上传依赖
commons-fileupload
commons-fileupload
1.4
(2)创建页面,并通过表单中的标签进行文件选择
<%--
method: 提交方式 文件上传必须为post提交。
enctype:该属性的默认值为application/x-www-form-urlencoded 表示提交的表单数据不能包含文件数据
multipart/form-data:可以包含文件数据
input的type属性必须为file类型,而且必须有name属性
--%>
(3)在springmvc.xml配置文件中添加文件上传解析器
(4)创建upload01方法
//注意:MultipartFile 参数名必须和中name属性相同
@RequestMapping("/upload01")
public String upload01(MultipartFile myfile, HttpServletRequest request) throws Exception{
//(1)得到本地服务目录的地址
String path = request.getSession().getServletContext().getRealPath("upload");
//(2)判断该目录是否存在
File file=new File(path);
if(!file.exists()){
file.mkdirs();
}
//(3)//把myfile保存到本地服务中某个文件夹下。 可使用UUID工具类来自动生成一段不重复的字符串,来作为文件的名称
String filename= UUID.randomUUID().toString().replace("-","")+myfile.getOriginalFilename();
File target=new File(path+"/"+filename);
myfile.transferTo(target); //把myfile转移到目标目录下
return "";
}
12.2结合elementui、vue、axios实现文件上传
(1)页面布局,可从elementui官网组件获取(elementUI组件https://element.eleme.io/#/zh-CN/component/installation)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--action:文件上传的路径--%>
(2)后台接口
与普通上传到本地文件类似,但是需要将文件所在地址返回给页面用图片文件的回显。
//创建返回结果类,用于存储返回结果
//使用lombok插件可通过以下三个注解实现get、set方法以及有参无参构造方法的建立
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult {
private Integer code;
private String msg;
private Object data;
}
@RequestMapping("/upload02")
@ResponseBody
public CommonResult upload01(MultipartFile file, HttpServletRequest request) throws Exception{
String path = request.getSession().getServletContext().getRealPath("upload");
File thisFile = new File(path);
if (!thisFile.exists()){
thisFile.mkdirs();
}
String fileName = UUID.randomUUID().toString().replace("-", "")+file.getOriginalFilename();
File newFile = new File(path+"/"+fileName);
file.transferTo(newFile);
//返回文件所在地址
return new CommonResult(100,"上传成功","http://localhost:8080/SpringMVC10/upload/"+fileName);
}
12.3上传文件到远程文件服务器(这里我使用的是,OSS阿里云服务器)
12.3.1 为什么需要使用远程文件服务器?
文件上传到本地时,有两个缺点,第一就是若不进行配置,所上传的文件会在本地服务器重启时被删除掉。
第二,如果搭建集群的情况下,文件将不能够在集群中共享。
想解决以上的两个问题,就可以通过远程服务器来实现,这样在集群模式下,可以让多个服务器都能够访问这个远程服务器,来实现文件的存取。
12.3.2 如何使用阿里云OSS对象存储实现远程文件上传
阿里云https://www.aliyun.com/?accounttraceid=42bce330710e4d96b294aaae23000be8hjou
(1)注册阿里云账号
此处略过
(2)找到对象存储OSS
(3)开通对象存储OSS服务
(4) 开通后进入控制台,创建Bucket(可以理解为你的文件所存储的位置)
需要变动的仅有读写权限,其余的默认即可
(5)申请阿里云密钥
我们需要使用密钥来通过java代码来访问OSS。
这里需要进行验证,验证通过后
(4)使用idea进行文件上传到阿里云oss
前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--引入css样式--%>
<%--引入vue--%>
后端接口实现
@RequestMapping("/uploadToOSS")
@ResponseBody
public CommonResult uploadToOSS(MultipartFile file, HttpServletRequest request) throws Exception{
String endpoint = "oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = 自己的密钥id";
String accessKeySecret = "自己的密钥密码";
// 填写Bucket名称,例如examplebucket。
String bucketName = "com-zjw";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String nowTime = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
String fileName = nowTime+"/"+UUID.randomUUID().toString().replace("-", "")+file.getOriginalFilename();
String objectName = fileName;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = file.getInputStream();
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
}
finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
String url = "https://"+bucketName+"."+endpoint+"/"+objectName;
//String url = endpoint.replace("//","//"+bucketName+".")+"/"+objectName;
System.out.println(url);
return new CommonResult(100,"上传成功",url);
}
这样写我们会发现,如果我们需要进行多次文件上传的话,就需要书写很多的重复代码,为了解决这个问题,我们可以抽取出一个工具类,在接口的实现方法中直接调用,简化我们的接口实现方法
public class UploadFileUntil {
public static String upload(MultipartFile file) throws IOException {
String endpoint = "oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "自己的密钥id";
String accessKeySecret = "自己的密钥密码";
// 填写Bucket名称,例如examplebucket。
String bucketName = "com-zjw";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String nowTime = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
String fileName = nowTime+"/"+ UUID.randomUUID().toString().replace("-", "")+file.getOriginalFilename();
String objectName = fileName;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
InputStream inputStream = file.getInputStream();
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, inputStream);
}
finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
String url = "https://"+bucketName+"."+endpoint+"/"+objectName;
return url;
}
}
这样我们的接口实现方法就会简化很多
@RequestMapping("/uploadAvatar")
public CommonResult upalodAvatar(MultipartFile file){
try {
String url = UploadFileUntil.upload(file);
return new CommonResult(100,"上传成功",url);
} catch (IOException e) {
e.printStackTrace();
}
return new CommonResult(100,"上传失败",null);
}
(5)带用户头像的表单提交
前端代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
添加用户
<%--引入vue--%>
提交
接口实现方法
@RestController
public class UserController {
@RequestMapping("/uploadAvatar")
public CommonResult upalodAvatar(MultipartFile file){
try {
String url = UploadFileUntil.upload(file);
return new CommonResult(100,"上传成功",url);
} catch (IOException e) {
e.printStackTrace();
}
return new CommonResult(100,"上传失败",null);
}
@PostMapping("/addUser")
public CommonResult addUser(@RequestBody User user){
System.out.println(user);
return new CommonResult(100,"添加成功",null);
}
}
两个接口一个实现头像上传的功能,并且将图片的url传给前端界面,回显在页面上,并且给表单数据的imageUrl赋值,在表单的提交方法中,将数据传给controller层,之后与数据库交互,将数据存储到数据库中。
(6)补充的零散内容
@RestController
类上等价于 @COntroller+@ResponseBody,该注解下所有的方法都是返回json数据
@RequestMapping: 作用: 把请求路径映射到响应的方法上。
@RequestParam(value = "u"):设置你接受的请求参数名。查询参数
@RequestMapping(value = "/addUser",method = RequestMethod.POST)
method:表示该接口接受的请求方式.不设置可以接受任意请求方式
@GetMapping("addUser"):表示只接受get提交方式的请求
@RequestBody:把请求的json数据转换为java对象。从前端到后端
@ResponseBody:把java转换为json数据 从后端转前端