一、Front设计模式
(1)概念:
Front(前端)设计模式就是有一个前端(不是前端专业那个前端,是最前面的意思)统一入口,在统一入口根据请求url调用自己的编写的普通方法。其中这个入口称为DispatcherServlet(Servlet分发器)。
(2)优点
-
只需要在一个Servlet中编写获取容器Bean的代码,减少了代码冗余。
-
不需要为每个控制器都创建一个类,而是可以在一个普通Java类中提供普通实例方法代表以前servlet中的services方法。
-
因为可以自己编写普通Java类,这类可以放入到Spring容器中,注入Service更方便
-
同时因为是自己编写的Java,所以可以进行一些封装,对其他操作进行简化。(代码中没有体现)
二、SpringMVC 环境搭建
(1)引入Spring-webmvc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
(2)创建Spring MVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描控制器类,千万不要把service等扫描进来,也千万不要在Spring配置文件扫描控制器类所在包 -->
<context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
<!-- 让Spring MVC的注解生效-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 静态资源放行
mapping:表示URL什么样的路径放行
location:去哪找资源
-->
<mvc:resources mapping="/images/**" location="/images/"/>
</beans>
(3)编写web.xml,让DispatcherServlet生效
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 参数名称必须叫做:contextConfigLocation。单词和大小写错误都导致配置文件无法正确加载 -->
<param-name>contextConfigLocation</param-name>
<!-- springmvc.xml 名称自定义,只要和后面创建的文件名称对应就可以了。 -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- Tomcat启动立即加载Servlet,而不是等到访问Servlet才去实例化DispatcherServlet -->
<!-- 配置上的效果:Tomcat启动立即加载Spring MVC框架的配置文件-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /表示除了.jsp结尾的uri,其他的uri都会触发DispatcherServlet。此处前往不要写成 /* -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置字符编码过滤器-->
<filter>
<filter-name>code</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>code</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
(4)创建一个类放入到Spring MVC容器中
package com.bjsxt.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller// 放入到Spring MVC容器中
public class FirstController {
/*
* 官方标准写法:
* 返回值是ModelAndView,对象中存储跳转资源路径及作用域值
*/
// 当前方法的映射路径
@RequestMapping("/first")
public ModelAndView test1(){
ModelAndView modelAndView = new ModelAndView("first.jsp");
return modelAndView;
}
/*
* 简化写法(平时使用的方式)
* 返回值是String,表示跳转的资源路径
*/
@RequestMapping("/first2")
public String test2(){
return "first.jsp";
}
}
三、@RequestMapping
(1)value"" 默认属性,配置映射路径,可以配置多个
(2)path="" 配置映射路径,可以配置多个
(3)name="" 备注
(4)method={RequestMethod.} 控制只能某种请求方法访问这个方法(控制单元),不配置默认什么请求方法都可以,如果不支持该请求方式报405状态码
(5)params="" 请求时必须包含某个参数,如果没有报400状态码
(6)consumes 设置请求体Content-Type类型,只能结合@RequestBody使用
(7)produces 设置响应体Content-Type类型,只能结合@ResponseBody使用
四、转发和重定向
(1)转发
在返回值字符串之前拼接 forward:
(2)重定向
在返回值字符串之前拼接 redirect:
五、作用域传值
(1)和原生servlet的api多了一个Model传值,实际还是request作用域传值。
(2)还有一种方式可以直接使用Map传值,实际上还是request作用域。
六、获取请求参数
(1)可以直接在参数列表中通过name名直接获取
(2)@RequestParam 设置请求参数的注解
1.name和value要接收请求参数的哪个参数
2.defaultValue:默认值。表示当请求参数中没有这个参数时给与的默认值
3.required:boolean类型,表示请求中是否必须包含参数
(3)接收一个自定义类型的参数,类中的属性名和请求参数名对应,默认通过set方法设置
(4)@DateTimeFormat接收日期类型参数,该注解也可以放在属性上
pattern属性设置格式:@DateTimeFormat(pattern="yyyy-MM-dd")
(5)当请求参数为多个同名参数时,可以通过数组接收。如果向使用集合接收,必须有@RequestParam("参数名")
(6)使用路径传参(restful)
请求路径写成
/bjsxt/test/chen/123/23
映射写为
/test/{name}/{password}/{age}
参数列表一定要加@PathVariable("name"),如果名字相同可以不写参数
(@PathVariable("name") String name,@PathVariable("name") String password,@PathVariable("name") int age)
七、视图解析器
解决资源路径过于复杂的问题。
实际方式:做字符串拼接,为每个资源路径都加前缀和后缀。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/page/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
八、文件上传
(1)在Html的form中enctype属性控制请求体数据类型
application/x-www-form-urlencoded :默认值,表示普通表单数据,传递的都是字符串参数
multipart/form-data:如果表单中除了普通字符串参数以外,还包含文件流数据,必须设置成这个
text/plain:大文本数据。传递的内容是比较大的字符串。常用在邮箱。
(2)导入commons-fileupload依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
(3)在springmvc容器中配置
<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置文件最大大小,单位字节 -->
<property name="maxUploadSize" value="1024"/>
</bean>
(4)使用及方法
@Controller
public class PeopleController {
/**
* 文件上传控制单元方法实现
*
* @param name 也可以使用JavaBean接收name的值
* @param address 也可以使用JavaBean接收address的值
* @param photo 名字必须和表单中文件域的name属性值相同
* @return
* @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
*/
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo) throws IOException {
photo.transferTo(new File("D:/images", photo.getOriginalFilename()));
return "/upload.jsp";
}
}
(5)生成唯一文件名的方式
时间戳+随机数
UUID(jdk自带)
/**
* 文件上传控制单元方法实现
* @param name 也可以使用JavaBean接收name的值
* @param address 也可以使用JavaBean接收address的值
* @param photo 名字必须和表单中文件域的name属性值相同
* @return
* @throws IOException transferTo抛出的异常,可以使用try...catch处理异常。示例中为了让代码看起来简洁直接抛出了。
*/
@RequestMapping("/upload")
public String upload(String name, String address, MultipartFile photo) throws IOException {
// 判断上传文件流是否为空。如果不为空继续执行
if(!photo.isEmpty()) {
// 获取项目部署后的文件绝对路径
// String realPath = req.getServletContext().getRealPath("/page");
// 使用UUID生成文件名称
// String fileName = UUID.randomUUID().toString();
// 使用时间戳+随机数生成文件名
long timeMillis = System.currentTimeMillis();
Random random = new Random();
String fileName = timeMillis + "" + random.nextInt(1000);
// 获取上传时文件名
String oldName = photo.getOriginalFilename();
// 获取上传时文件的扩展名
String suffix = oldName.substring(oldName.lastIndexOf("."));
// 保存文件到D:/images中。必须保存D盘下已经存在images文件夹
photo.transferTo(new File("D:/images",fileName + suffix));
}
return "/upload.jsp";
}
(6)字节输出流
// 方法返回值为void
@RequestMapping("/uploadfile/{filepath}")
public void showImage(@PathVariable String filepath, HttpServletResponse resp) throws IOException {
FileInputStream is = new FileInputStream("D:/images/" + filepath);
// Resonse对象getOutputStream()获取响应流。
ServletOutputStream os = resp.getOutputStream();
// Commons-io.jar中的工具类。
// copy(InputStream,OutputStream)表示表InputStream中内容拷贝到OutputStream中
IOUtils.copy(is, os);
}
九、文件下载
(1)在Http协议中,响应头参数Content-Disposition参数可取值有两个:
inline:默认值。表示浏览器能解析就显示,不能解析就下载。
attachment。以附件形式下载(恒下载)
@RequestMapping("/download")
public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
try {
// filename=的值就是客户端看到的下载文件名称
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
File file = new File(req.getServletContext().getRealPath("/images"), filename);
FileInputStream fis = new FileInputStream(file);
ServletOutputStream os = response.getOutputStream();
IOUtils.copy(fis, os);
} catch (IOException e) {
e.printStackTrace();
}
}
十、@ResponseBody
(1)放在方法上,可以把方法的返回值转换为json字符串,并设置到响应体中。
注意:会使得方法没有跳转功能。
(2)想要改变@ResonseBody注解的响应内容类型(Content-Type)只能通过@RequestMapping的produces属性进行设置。如果返回值是String类型并且返回值中有中文必须这样设置。
@RequestMapping(value="/demo1",produces = "text/html;charset=utf-8")
@ResponseBody
public String demo1() {
return "幽默涵养";
}
(3)@RestController写在类上,表示该类上所有方法都加了@ResponseBody
十一、@RequestBody
服务端接收请求体中包含JSON字符串的请求时,需要在参数前面添加@RequestBody。表示使用Jackson把请求体中JSON/XML格式的数据转换为JavaBean或Map
@RequestMapping("/testContentType")
@ResponseBody
public People testContentType(@RequestBody People peo) {
System.out.println(peo);
return peo;
}
十二、拦截器
(1)定义:Spring MVC中提供的一个类似filter(过滤器)的功能。
(2)使用
1.创建java类实现HandlerInterceptor接口,重写三个方法
参数:
Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。
ModelAndView: 存储了model和view信息的对象。
Exception:存储异常信息的对象,如果没有异常信息则默认为null。
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.在springmvc.xml中配置拦截器拦截哪些控制单元
<mvc:interceptors>
<!-- 为所有控制单元配置拦截器 -->
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
<mvc:interceptor>
<!-- 为所有控制单元配置拦截器 -->
<mvc:mapping path="/**"/>
<!-- 不为某个控制单元配置拦截器 -->
<mvc:exclude-mapping path="/test"/>
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
(3)拦截器栈
拦截器栈指多个拦截器。当一个控制单元被多个拦截器拦截时,就形成了拦截器栈。拦截器栈中拦截器有着严格的执行顺序。执行顺序按照配置顺序执行。先配置的优先级更高,执行完控制单元,是先配置的后执行。
十三、Spring MVC异常处理
(1)局部配置
每个控制器类中可以有多个处理异常的方法。每个方法上面只需要有@ExceptionHandler,千万别添加了@RequestMapping注解。参数:value设置出现什么异常类型。
注意:只在当前类生效。
@Controller
public class DemoController {
@RequestMapping("/demo")
@ResponseBody
public String demo2(){
Object obj = null;
obj.toString();
return "demo2";
}
@ExceptionHandler(value = ArithmeticException.class)
public String myexception(){
System.out.println("Demo-1");
return "forward:/exception.jsp";
}
@ExceptionHandler(value = Exception.class)
public String myexception2(){
System.out.println("Demo-2");
return "forward:/exception2.jsp";
}
}
(2)全局配置
创建一个类在类上加@ControllerAdvice注解,在该类中的异常处理方法就会在所有控制类中生效。
@ControllerAdvice
public class MyExceptionController {
@ExceptionHandler(value = ArithmeticException.class)
public String myexception(){
System.out.println("MyException-1");
return "forward:/exception.jsp";
}
@ExceptionHandler(value = Exception.class)
public String myexception2(){
System.out.println("MyException-2");
return "forward:/exception2.jsp";
}
}
(3)使用配置文件配置
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.NullPointerException">/error1.jsp</prop>
<prop key="java.lang.Exception">/error2.jsp</prop>
</props>
</property>
</bean>
(4)在web.xml中配置
上面的方式只有在Spring MVC中出现异常时才会触发,也可以使用Java EE中的配置方式。在web.xml中配置error-page即可。
这种在web.xml配置的方式是针对整个项目出现的异常。而在springmvc.xml配置文件的配置方式只是针对Spring MVC框架出现的异常。
<error-page>
<!-- 根据异常类型控制 -->
<exception-type>java.lang.NullPointerException</exception-type><!-- 根据状态码控制 -->
<error-code>500</error-code>
<location>/error3.jsp</location>
</error-page>
十四、数据校验
注解 | 含义 |
---|---|
@AssertFalse | 类型必须是布尔,取值必须为false |
@AssertTrue | 类型必须是布尔,取值必须为true |
@DecimalMax("3") | 最大值为3,value属性是String类型。 |
@DecimalMin("1") | 最小值为1,value属性是String类型。 |
@Digits(integer = 10,fraction = 3) | integer:整数位最大长度,fraction小数位最大长度 |
必须是邮箱地址。只要中间包含@,且@前后都具有超过1位的字符就能通过校验。字符可以是数字字母下划线 | |
@Future | 类型必须是时间类型,允许为null,如果设置值必须是一个将来的时间 |
@FutureOrPresent | 类型必须是时间类型,允许为null,如果设置值必须是一个一个将来或现在的时间(精确到秒) |
@Max(5) | 最大值为5,value属性是long类型 |
@Min(1) | 最小值为1,value属性是long类型。 |
@Negative | 必须是负数,对数据类型没有要求。 |
@NegativeOrZero | 必须是负数或零,对数据类型没有要求。 |
@NotBlank | 用在String类型。不能是空白(null和"") |
@NotEmpty | 用在String类型。不能是空白(null和"") |
@NotNull | 不能为null,可以是""。可以用在所有类型中。对于八大基本数据类型来说,永远不为null。 |
@Null | 必须为Null。可以用在所有类型中。对于八大基本数据类型来说,永远不可能为null。 |
@Past | 类型必须是时间类型,必须是一个过去的时间。精确到秒。 |
@PastOrPresent | 类型必须是时间类型,必须是一个过去的时间或现在的时间。精确到秒。· |
@Pattern(regexp = "\w{1,6}") | 必须满足正则表达式。regexp是必有属性。 |
@Positive | 必须是正数,对数据类型没有要求。 |
@PositiveOrZero | 必须是正数或零,对数据类型没有要求。 |
@Size(min = 1,max = 10) | 用在String类型。个数必须在1和10之间 |
注解 | 含义 |
---|---|
@Length(min = 1,max = 10) | 用在String类型。长度需要在1和10之间 |
@Range(min = 1,max = 10) | 数据类型没有要求。取值范围需要在1和10之间 |
@URL(port = 8080,host = "127.0.0.1",protocol = "https") | 需要是一个合法的URL。默认情况下只要是以http:开头即可。可以通过port限制端口、host限制主机名、protocol限制协议 |
(1)导入hibernate-validator依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.7.Final</version>
</dependency>
(2)在实体类属性上添加注解
public class People {
@NotNull(message = "姓名不能是null")
@Length(min = 2,max = 6,message = "长度应该是2-6位")
private String name;
private String age;
// 省略Getter和Setter
}
(3)控制单元中添加@Valid注解,否则校验不生效
@RequestMapping("/test1")
@ResponseBody
public People test1(@Valid People people,BindingResult result) {
// 如果有异常信息
if (result.hasErrors()) {
// 获取异常信息对象
List<ObjectError> errors = result.getAllErrors();
// 将异常信息输出
for (ObjectError error : errors) {
System.out.println(error.getDefaultMessage());
}
}
return people;
}
十五、跨域
(1)跨域介绍
跨域:当前项目的协议、ip、端口和访问的URL的协议、IP、端口中有一个不同,这种访问就叫跨域。
跨域只发生在Ajax请求中。
Ajax研发之初为了保证安全性,设置默认情况下不允许跨域访问。
(2)实现跨域原理
只要在控制单元方法上添加了@CrossOrigin注解后,会在响应头中添加Access-Control-Allow-Origin:*
Access-Control-Allow-Origin是HTTP协议中允许哪些IP的项目跨域访问,*表示所有IP
十六、国际化
(1)新建配置文件:属性文件语法:任意名_语言_国家.properties
。例如:中文是zh、英文是en。如果为了更加精确是哪国使用的这个语言,可以在后面添加国家,因为美式英语和英式英语是不一样的。中国:CN、美国是US,国家缩写都是大写的。
新建suiyi_zh_CN.properties
bjsxt.username=用户名
bjsxt.password=密码
bjsxt.submit=登录
新建suiyi_en_US.properties
bjsxt.username=username
bjsxt.password=password
bjsxt.submit=login
(2)在springmvc.xml中添加额外配置。
<!-- 加载属性文件 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="suiyi"></property>
</bean>
<!-- 默认也是AcceptHeaderLocaleResolver,所以可以不配置-->
<bean id="localeResovler" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>
(3)新建控制单元
@RequestMapping("/showForm")
public String showForm(){
return "/form.jsp";
}
(4)新建页面:
在上面使用taglib执行引入标签库
<spring:message code="属性文件的key"></spring:message>
根据语言环境负责加载属性文件中key的值。中文环境就加载suiyi_zh_CN.properties文件内容,英文环境就加载suiyi_en_US.properties文件内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="" method="post">
<spring:message code="bjsxt.username"></spring:message> <input type="text" name="username"/><br/>
<spring:message code="bjsxt.password"></spring:message> <input type="text" name="password"/><br/>
<input type="submit" value="<spring:message code="bjsxt.submit"></spring:message>"/>
</form>
</body>
</html>
十七、SpringMVC常见组件
DispatcherServlet:前端控制器。Spring MVC的入口,也是整个流程控制中心。其他组件由DispatcherServlet统一调度,降低了组件和组件之间的耦合度。
MultipartResovler:多部分处理器。文件上传时需要使用。
LocaleResolver:解决客户端的区域和时区问题。
ThemeResolver:主题解析器。刻提供自定义布局。
HandlerMapping: 映射处理器。主要负责处理URL,并找到对应的HandlerMethod。简单说就是找@RequestMapping注解中映射路径是否有和URL匹配的。
HandlerAdapter:适配器。负责调用具体的HandlerMethod。
HandlerExceptionResovler:异常处理器。异常处理,根据不同异常返回视图。
RequestToViewNameTranslator:从请求中获取到视图名称。
ViewResovler:视图解析器,负责解析字符串视图名和物理视图文件的。
FlashMapManager:主要用于存储属性的,本质是一个Map。多用在重定向时。FlashMap在重定向之前存储,重定向之后删除。
ModelAndView:模型和视图。Spring MVC中提供的模型和视图接口。
HandlerInterceptor:拦截器。拦截控制器资源的。
十八、SpringMVC运行原理
1.客户端向服务端发起请求,Spring MVC总体入口DispatcherServlet进行请求分发。
2.DispatcherServlet把URL交给映射处理器HandlerMapping进行解析URL。并寻找是否具有对应的控制单元。
3.如果存在控制单元,执行拦截器Interceptor的预处理方法preHandle进行处理。如果不存在对应控制单元,拦截器不执行。
4.如果拦截器允许放行,返回给Servlet分发器DispatcherServlet,调用后续组件。
5.Servlet分发器DispatcherServlet使用适配处理器HandlerAdapter,调用具体的控制单元方法。
6.执行单元方法HandlerMethod。
7.控制单元HandlerMethod执行完成后产生模型和视图组件ModelAndView。
8.返回模型和视图组件给Servlet分发器DispatcherServlet。如果有拦截器Interceptor,执行postHandle
9.Servlet分发器DispatcherServlet调用视图解析器ViewResolver解析视图
10.产生视图View对象,执行了视图对应的资源。
11.把结果返回给Servlet分发器DispatcherServlet。如果有拦截器Interceptor,执行拦截器的afterCompletion
12.Servlet分发器把最终视图执行结果响应给客户端浏览器。