三层架构
-
表现层:负责数据展示
-
业务层:负责业务处理
-
数据层:负责从数据库中获取数据
MVC 简介
MVC(Model View Controller):一种用于设计Web应用程序表现层的模式。
-
Model(模型):数据模型,用于封装数据
-
View(视图):页面视图,用于展示数据(jsp, html)
-
Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑
SpringMVC
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
优点
自动封装参数
灵活性强
性能突出(相比现有的表现层框架:Struts2)
入门案例制作
①导入SpringMVC相关坐标
<dependencies>
<!-- servlet3.1规范的坐标 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!--springmvc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
在pom.xml中添加tomcat7-maven-plugin插件,配置端口80、访问路径/、编码格式UTF-8
<build>
<!--设置插件-->
<plugins>
<!--具体的插件配置-->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
<configuration>
<port>80</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
配置打包方式和编译版本:
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
②定义表现层业务处理器Controller,并配置成spring的bean(等同于Servlet):@Controller
在Spring MVC配置文件srcmain esourcesspring-mvc.xml中添加包扫描
③web.xml中配置SpringMVC核心控制器,用于将请求转发到对应的具体业务处理器Controller中
④设定具体Controller中指定方法的访问路径@RequestMapping("/save")
//设定当前方法的访问映射地址
⑤设置返回页面,默认路径:srcmainwebapp
JSP页面头信息:设置编码格式
<%
//设定当前方法的访问映射地址
SpringMVC六大核心组件
-
DispatcherServlet:前端控制器, 是整体流程控制的中心,由其调用其它组件处理用户的请求, 有 效的降低了组件间的耦合性
-
HandlerMapping:处理器映射器, 负责根据用户请求找到对应具体的Handler处理器
-
Handler:处理器,业务处理的核心类,通常由开发者编写,描述具体的业务
-
HandlrAdapter:处理器适配器,通过它对处理器进行执行
-
View Resolver:视图解析器, 将处理结果生成View视图
-
View:视图,最终产出结果, 常用视图如jsp、html
Controller加载控制
SpringMVC的处理器对应的bean必须按照规范格式开发,为避免加入无效的bean可通过bean加载过滤器进行包含设定或排除设定,表现层bean标注通常设定为@Controller
在spring-mvc.xml中添加包扫描过滤配置:
<context:component-scan base-package="com.itheima">
<context:include-filter
type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
静态资源加载
简写方式:在spring-mvc中添加如下配置
<!--SpringMVC提供的通用资源放行方式-->
<mvc:default-servlet-handler/>
<!--必须组合使用-->
<mvc:annotation-driven/>
字符集过滤器-中文乱码
SpringMVC提供专用的中文字符过滤器: CharacterEncodingFilter,用于处理中文乱码问题
配置在 web.xml 里面
<!--乱码处理过滤器,与Servlet中使用的完全相同,差异之处在于处理器的类由Spring提供-->
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
请求
普通类型参数传参
在UserController中添加如下方法:
1.参数名与Controller中的方法参数名保持一致 http://localhost/requestParam1?name=itheima&age=14
2.@RequestParam 的使用 类型:形参注解 位置:方法形参前方 作用:绑定请求参数与对应处理方法形参间的关系
http://localhost/requestParam2?userName=Jock
POJO类型参数传参
1.当POJO中使用简单类型属性时, 参数名称与POJO类属性名保持一致,SpringMVC自动封装参数
访问URL: http://localhost/requestParam3?name=itheima&age=14
Controller
2.参数冲突 当POJO类型属性与其他形参出现同名问题时,将被同时赋值 访问URL: http://localhost/requestParam4?name=itheima&age=14&minAge=12
使用@RequestParam注解进行区分
3.POJO中的对象属性
当POJO中出现对象属性时,参数名称与对象层次结构名称保持一致 访问URL: http://localhost/requestParam5?address.province=beijing
public class User {
private String name;
private Integer age;
private Address address;
//生成get,set方法
}
public class Address {
private String province;
private String city;
private String address;
//生成get,set方法
}
4.POJO中有简单的集合类型参数时,属性名写多个即可 访问URL:http://localhost/requestParam6?nick=Jock1&nick=Jockme&nick=zahc
private List<String> nick;
//http://localhost/requestParam6?nick=Jock1&nick=Jockme&nick=zahc
5.当POJO中有复杂的集合类型参数时,参数名称与对象层次结构保持一致,使用数组格式描述集合中对象的位置
访问URL:
private List<Address> addresses;
//http://localhost/requestParam7?addresses[0].city=beijing&addresses[1].province=hebei
6.当POJO中出现Map时,参数名称与对象层次结构名称保持一致,使用映射格式描述集合中对象的位置
访问URL:
private Map<String,Address> addressMap;
数组与集合类型参数传参
1.数组类型参数
请求参数名与处理器方法形参名保持一致
访问URL: http://localhost/requestParam9?nick=Jockme&nick=zahc
2.集合类型参数
需要使用@RequestParam("name")定义参数名 访问URL: http://localhost/requestParam10?nick=Jockme&nick=zahc
注意:
-
使用@RequestParam注解
-
url中的参数名和@RequestParam中的保持一致
日期类型格式转换
第二种方式日期类型格式转换(常用) 名称: @DateTimeFormat 类型: 形参注解、成员变量注解 位置:形参前面 或 成员变量上方 作用:为当前参数或变量指定类型转换规则 范例: http://localhost/requestParam12?date=1999-09-09
配置注解驱动支持
<mvc:annotation-driven />
如果POJO中有Date类型:http://localhost/requestParam11?name=zhangsan&birthday=1999-09-09
请求映射
方法注解
名称: @RequestMapping 类型: 方法注解 位置:处理器类中的方法定义上方 作用:绑定请求地址与对应处理方法间的关系 范例: 访问路径:http://localhost/requestURL1
类注解
名称: @RequestMapping 类型: 类注解 位置:处理器类定义上方 作用:为当前处理器中所有方法设定公共的访问路径前缀 范例: 访问路径: http://localhost/user/requestURL2
响应
页面跳转
两种跳转方式:转发和重定向,区别在于:转发forward后地址栏不会变化
-
转发(默认),服务器跳转
-
重定向,客户端跳转
页面访问快捷设定
InternalResourceViewResolver:展示页面的保存位置通常固定且结构相似的,可以设定通用的访问路径,简化页面配置格式
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
public String showPage3() {
return "page";
}
设置之后,默认使用forward,不能再添加forward和redirect
注意:如果未设定了返回值,使用void类型,则默认使用访问路径作页面地址的前缀和后缀
//最简页面配置方式,使用访问路径作为页面名称,省略返回值
##
带数据页面跳转
-
方式一:使用HttpServletRequest类型形参进行数据传递
http://localhost/showPageAndData1
-
方式二:使用Model类型形参进行数据传递
http://localhost/showPageAndData2
-
最佳实践方式三:使用ModelAndView类型形参进行数据传递,将该对象作为返回值传递给调用者
http://localhost/showPageAndData3
//使用ModelAndView形参传递参数,该对象还封装了页面信息
##
返回JSON数据
添加jackson依赖:
<!--json相关坐标3个-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
-
方法二:使用SpringMVC提供的消息类型转换器将对象与集合数据自动转换为JSON数据:
@ResponseBody
//使用SpringMVC注解驱动,对标注@ResponseBody注解的控制器方法进行结果转换,由于返回值为引用类型,自动调用jackson提供的类型转换器进行格式转换
开启注解驱动
<!--开启springmvc注解驱动,对@ResponseBody的注解进行格式增强,追加其类型转换的功能,具体实现由MappingJackson2HttpMessageConverter进行-->
<mvc:annotation-driven/>
返回中文字符串
在spring-mvc.xml中配置编码格式
<!--开启springmvc注解驱动,对@ResponseBody的注解进行格式增强,
追加其类型转换的功能,具体实现由MappingJackson2HttpMessageConverter进行-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
异步调用(重点)
接受异步请求参数(重点)
名称: @RequestBody 类型: 形参注解 位置:处理器类中的方法形参前方 作用:将异步提交数据组织成标准请求参数格式,并赋值给形参 范例:
检查pom.xml是否导入jackson的坐标
<!--json相关坐标3个-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
*注意:请求数据格式与POJO中的属性对应*
public class User {
private String name;
private Integer age;
//添加get,set方法
}
{"name":"Jock","age":39}
//为id="testAjaxPojo"的组件绑定点击事件
$("#testAjaxPojo").click(function(){
$.ajax({
type:"POST",
url:"ajaxPojoToController",
data:'{"name":"Jock","age":39}',
dataType:"text",
contentType:"application/json",
});
});
将@RequestBody注解添加到Pojo参数前方
{"name":"Jock","age":39}
-
注解添加到集合参数前方时,封装的异步提交数据按照集合的存储结构进行关系映射 注意:页面发送的数据是JSON格式的对象数组
[{"name":"Jock","age":39},{"name":"Jockme","age":40}]
//为id="testAjaxList"的组件绑定点击事件
$("#testAjaxList").click(function(){
$.ajax({
type:"POST",
url:"ajaxListToController",
data:'[{"name":"Jock","age":39},{"name":"Jockme","age":40}]',
dataType:"text",
contentType:"application/json",
});
});
##
自定义拦截器
-
制作拦截功能类(通知):实现HandlerInterceptor接口
//自定义拦截器需要实现HandleInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
//处理器运行之前执行
-
编写Controller
-
配置拦截器的执行位置(类似切入点)
<!--开启拦截器使用-->
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--设置拦截器的拦截路径-->
<mvc:mapping path="/handleRun"/>
<!--指定具体的拦截器类-->
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
异常处理
异常处理器-视频
1.实现HandlerExceptionResolver接口(异常处理器)
2.根据异常的种类不同,进行分门别类的管理,返回不同的信息
3.在Controller中模拟不同的异常进行测试
//http://localhost/save
注解开发异常处理器(重点)
使用注解实现异常分类管理 名称: @ControllerAdvice 类型: 类注解 位置:异常处理器类上方 作用:设置当前类为异常处理器类 范例:
-
使用注解实现异常分类管理 名称: @ExceptionHandler 类型: 方法注解 位置:异常处理器类中针对指定异常进行处理的方法上方 作用:设置指定异常的处理方式 说明:处理器方法可以设定多个 范例:
扩展知识
1.如果标记了@ControllerAdvice类中的每个方法都使用了@ResponseBody,可以采用如下的简写方式:
//@ResponseBody
//@ControllerAdvice
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
2.@ResponseBody返回中文字符串乱码,在spring-mvc.xml配置转换器编码:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
项目异常处理方案
-
异常处理方案
-
业务异常: 发送对应消息传递给用户,提醒规范操作
-
系统异常: 发送固定消息传递给用户,安抚用户 发送特定消息给运维人员,提醒维护 记录日志
-
其他异常: 发送固定消息传递给用户,安抚用户 发送特定消息给编程人员,提醒维护
-
纳入预期范围内
记录日志
-
-
自定义异常
-
自定义BusinessException
//自定义异常继承RuntimeException,覆盖父类所有的构造方法
public class BusinessException extends RuntimeException {
public BusinessException() {
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
} -
自定义SystemException
//自定义异常继承RuntimeException,覆盖父类所有的构造方法
public class SystemException extends RuntimeException {
public SystemException() {
}
public SystemException(String message) {
super(message);
}
public SystemException(String message, Throwable cause) {
super(message, cause);
}
public SystemException(Throwable cause) {
super(cause);
}
public SystemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
项目异常处理实现
-
在UserControll中模拟触发异常
//http://localhost/ajax.jsp
-
通过自定义异常将所有的异常现象进行分类管理,以统一的格式对外呈现异常消息
实用技术
文件上传
-
第一步:引入commons-fileupload坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
同时上传多个文件:
<form action="/fileupload" method="post" enctype="multipart/form-data">
<%--文件上传表单的name属性值一定要与controller处理器中方法的参数对应,否则无法实现文件上传--%>
上传LOGO:<input type="file" name="file"/><br/>
上传照片:<input type="file" name="file1"/><br/>
上传任意文件:<input type="file" name="file2"/><br/>
<input type="submit" value="上传"/>
</form>
完整代码:
##
RESTful开发风格(重点)
Rest
-
Rest( REpresentational State Transfer) 一种网络资源的访问风格,定义了网络资源的访问方式
-
传统风格访问路径 http://localhost/user/get?id=1
-
Rest风格访问路径 GET: http://localhost/user/1
DELETE: http://localhost/user/1
UPDATE: http://localhost/user/1
-
-
Restful是按照Rest风格访问网络资源
-
优点
隐藏资源的访问行为,通过地址无法得知做的是何种操作
简化书写
HTTP请求的四种方式
GET(查询) http://localhost/user/1 GET POST(保存) http://localhost/user POST PUT(更新) http://localhost/user/1 PUT DELETE(删除) http://localhost/user/1 DELETE
注意:上述行为是约定方式,约定不是规范,可以打破,所以称Rest风格,而不是Rest规范
查询id=100的用户信息两种实现方式:
//@Controller
//@ResponseBody
//设置rest风格的控制器
Restful开发简写方式
-
Restful请求路径简化配置方式 : @RestController = @Controller + @ResponseBody
-
完整的简化后代码,@RestController = @Controller + @Response
//设置rest风格的控制器
校验框架
导入hibernate-validator校验框架的坐标
<!--导入校验的jsr303规范:接口-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!--导入校验框架实现技术-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
1. 开启校验
名称:@Valid 、 @Validated 类型:形参注解 位置:处理器类中的实体类类型的方法形参前方 作用:设定对当前实体类类型参数进行校验 范例:
2.设置校验规则
名称:@NotNull, @NotBlank, @NotEmpty 类型:属性注解 位置:属性上方 作用:设定当前属性校验规则 范例:每个校验规则所携带的参数不同,根据校验规则进行相应的调整
public class Employee{
易错点:必须给name,age等属性编写getter,setter方法
3.获取错误信息
通过形参Errors获取校验结果数据,通过Model接口将数据封装后传递到页面显示
在JSP页面中获取后台封装的校验结果信息
<form action="/addemployee" method="post">
员工姓名:<input type="text" name="name"><span style="color:red">${name}</span><br/>
员工年龄:<input type="text" name="age"><span style="color:red">${age}</span><br/>
<input type="submit" value="提交">
</form>
嵌套校验
引用类型字段如何校验,比如Employee中有address属性记录地址
名称:@Valid 类型:属性注解 位置:实体类中的引用类型属性上方 作用:设定当前应用类型属性中的属性开启校验
1.在address属性上添加@Valid注解,开启嵌套属性校验
public class Employee {
//实体类中的引用类型通过标注@Valid注解,设定开启当前引用类型字段中的属性参与校验
2.开启嵌套校验后,被校验对象内部需要添加对应的校验规则,并添加嵌套属性的get,set方法
//嵌套校验的实体中,对每个属性正常添加校验规则即可
public class Address {
3.在jsp页面中获取嵌套属性中的错误信息
${requestScope['address.cityName']}
<%--注意,引用类型的校验未通过信息不是通过对象进行封装的,直接使用对象名.属性名的格式作为整体属性字符串进行保存的,和使用者的属性传递方式有关,不具有通用性,仅适用于本案例--%>
省:<input type="text" name="address.provinceName"><span style="color:red">${requestScope['address.provinceName']}</span><br/>
市:<input type="text" name="address.cityName"><span
style="color:red">${requestScope['address.cityName']}</span><br/>
分组校验
根据业务不同需要调整是否参与校验,比如同一个模块,新增和修改时校验规则是不一致的
-
新增用户:新增时用户名不能为空
-
修改用户:修改时不能修改用户名,所以不用强制用户名不能为空
解决方案:对不同种类的属性进行分组,在校验时可以指定参与校验的字段所属的组类别
-
根据业务类型使用接口定义分组为Save, Update
public interface Save {
}public interface Update {
} -
在实体类Employee中为属性设置所属组,可以设置多个
姓名不能为空只在新增时校验
年龄校验新增和修改都需要校验
-
在Controller的方法参数上开启分组校验,并指定使用哪一种分组:@Validated({Save.class})
//新增Employee
Spring整合Mybatis(复习)
在pom.xml引入相关坐标
<dependencies>
<!--spring+springmvc环境环境-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--mybatis环境-->
<!--mybatis环境-->
<!--mybatis环境-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--mysql环境-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--spring整合jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!--分页插件坐标-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
<!--jackson-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!--其他组件-->
<!--其他组件-->
<!--其他组件-->
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>