目录
MVC
含义:MVC是一种架构思想,将软件按照模型、视图、控制器来划分
- M:Model、模型层,指工程中的javaBean,作用是处理数据
- V:View、视图层,指工程中html或jsp页面,作用是与用户进行交互,展示数据
- C:Controller、控制层,指工程中的servlet,作用是接收请求和响应浏览器
javaBean分类
- 实体类Bean:专门储存业务数据,如Student、User等
- 业务处理Bean:专门处理业务逻辑和数据访问,如Service、Dao
MVC工作流程
用户通过视图层发起请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕后将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器。
SpringMVC
含义:SpringMVC是spring为表述层开发提供的一套完备的解决方案
注意:三层架构分为表述层(表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet
SpringMVC的特点
- spring家族原生产品,与ioc容器等基础设施无缝对接
- 基于原生的servlet,通过功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理(springmvc就把之前通过servlet所实现的功能进行了封装,封装成了DispatcherServlet)
- 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
- 代码清新简洁,大幅度提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应的组件即可
- 性能卓越,尤其适合现代大型、超大型互联网项目要求
SpringMVC的基本组件
- DispatcherServlet:前端控制器,用于统一处理请求和响应,整个流程的控制中心,由它调用其他组件处理用户的请求
- HandlerMapping:处理器映射器,其会根据请求的URL,method等信息查找Handler,即控制器方法
- Handler:处理器,需要工程师开发;其作用是在DispatcherServlet的控制下Handler对具体的用户请求进行处理
- HandlerAdapter:处理器适配器,他会通过处理器适配器对控制方法进行执行(就是调用处理器)
- ViewResolver:视图解析器,主要功能是进行视图解析,得到相应的视图
- View:视图,作用是将模型数据通过页面展示给用户
SpringMVC执行流程
用户发起请求到前端控制器,前端控制器捕获请求并对url进行解析,得到请求资源标识符,判断请求uri对应的映射,若映射不存在则看是否配置默认servlet处理器,没配置或找不到资源则报404,配置了并且找到资源则访问目标,根据该uri调用处理器映射器获得该handler配置的所有相关对象(包括Handler对象以及handler对象对应的拦截器),最后以HandlerExecutionChain执行链的对象形式返回给处理器控制器,处理器控制器根据获得的Handler选择一个合适的处理器适配器开始执行拦截器或方法;执行完毕后返回ModelAndView给前端控制器;前端控制器根据返回的ModelAndView选择一个合适的ViewResolver进行视图解析,根据Model和View来渲染视图,之后将渲染的结果返回给客户端。
SpringMVC的使用
- 创建maven的web工程并导入对应的依赖
- 在web.xml文件中配置springmvc的前端控制器DispatcherServlet以及springmvc配置文件
- 创建请求控制器
<dependencies>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!--日志依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<!--为SLF4J是日志的门面,就是里面全是接口,而logback就为日志SLF4J门面的实现-->
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--servletAPI-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--spring整合thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
<?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">
<!--配置springmvc的前端控制器DispatcherServlet-->
<!--
springmvc的配置文件默认的位置和名称
位置:WEB.INF下
名称:servlet-name标签值-servlet.xml
-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定springmvc的配置文件位置及名称-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--将servlet的初始化时间提前到服务器启动时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--注意:servlet-name里的default属性是tomcat的web.xml中所配置的一个servlet,为默认的servlet-->
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<!--springmvc配置文件(springmvc.xml)-->
<beans xmlns="http://www.springframework.org/schema/beans"
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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描控制层组件-->
<context:component-scan base-package="cn.tedu.controller"/>
<!--配置Thymeleaf视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"></property>
<property name="characterEncoding" value="UTF-8"></property>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--注意:视图前缀+逻辑视图+视图后缀就是我们的物理视图-->
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
//请求控制器
@Controller
public class HelloController {
@RequestMapping("/")
public String protal(){
//将逻辑视图返回
return "success";
}
}
注意:这里返回页面名称即可,因为页面前缀与页面后缀已经在springmvc配置文件中设置
总结:浏览器发送请求,若请求地址符合前端控制器url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取springmvc的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中的@requestMapping注解的value值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应的页面
@RequestMapping注解
前言:从注解名字上我们可以看出,@RequestMapping注解的作用就是将请求和处理请求的控制器方法进行关联,建立起映射联系;当springmvc接收到这个请求,那么就会来找到在映射关系中对应的控制器方法来处理这个请求。
@RequestMapping注解标识的位置
- @RequestMapping注解标识一个类,表示类中所有方法的初始请求路径
- @RequestMapping注解标识一个方法,表示类中方法的具体请求路径
@RequestMapping注解属性
value属性:value属性值是一个String类型数组形式,表示可以根据多个路径来匹配对应的方法
method属性:该属性类型是RequestMethod[],也为一个数组;表示当前控制器方法所允许接受的请求类型
注意:由于日常只使用支持单个请求的方法,所以在@RequestMapping注解的基础上结合请求方式诞生出一堆派生注解(@GetMapping、@PostMapping、@DeleteMapping、@PutMapping)
params属性:该属性类型为String[],其作用为通过请求参数匹配请求,即浏览器发送的请求参数必须满足以下设置
- "param":表示当前匹配的请求的请求参数中必须携带特定的param参数
- "!param":表示当前匹配的请求的请求参数中一定不能携带param参数
- "param=value":表示当前匹配的请求请求参数中必须携带param参数,并且值必须为value
- "param!=value":表示当前匹配的请求请求参数中可以不携带param参数,若携带,则值一定不为value
headers属性:其类型为String[],其作用是通过请求报文匹配请求,即浏览器的请求头必须满足如下设置
- "key":要求映射匹配的请求头必须包含key请求头信息
- "!key":要求映射匹配的请求头不能包含key请求头信息
- "key=value":要求请求映射所匹配的请求必须携带key请求头信息,并且值为value
- “key!=value":要求请求映射所匹配的请求可以不携带key请求头,若携带则值不为value
注意:请求头和响应头的键不区分大小写
SpringMVC支持ant风格的路径
路径中的占位符(在@RequestMapping的value属性内)
- ?:表示任意的单个字符,但不包括?本身
- *:表示任意的0个或多个字符,但不包括?和/
- **:表示任意层数的任意目录,只能将**写在双斜线中,前后以及中间不能有任意其他字符
restful风格
rest(representational state transfer):表现层资源状态转移
restful风格提倡url地址使用统一的风格设计,从前到后各个单词使用/分开,不使用问号键值对等方式携带请求参数,而是将要发送给服务器的数据作为URL地址的一部分,以保证整体风格的一致性。
- 原始方式:/getUser?username=lili&password=123
- 现在方式:/getUser/lili/123
使用方法
需要在@RequestMapping注解的value属性中所设置的路径中,使用{xxx}的方式表示路径中的数据,在通过@PathVariable注解,将占位符所表示的值和控制器方法的形参进行绑定
注意:此种方式对于post请求或get请求都适用
占位符方式获取参数值
@Controller
@RequestMapping("/test")
public class ProtalController {
@RequestMapping(value= "/get/{username}/{password}")
public String getParam(@PathVariable("username") String username,@PathVariable("password") Integer password){
System.out.println("名字为:"+username+",密码为:"+password);
return "success";
}
}
注意:
- @PathVariable用于接收请求路径中的参数
- @PathVariable内的名字与路径中的占位符参数映射
- 当@PathVariable内的名字与传参的参数名一致,则@PathVariable内的名字可以省略
用实体类形参获取请求参数
@RequestMapping("/pojo/{username}/{password}")
public String getParamByPojo(User user){
System.out.println(user);
return "success";
}
public class User {
private Integer id;
private String username;
private String password;
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
注意:
- 用实体类进行封装就不需要设置@PathVariable注解
- 用实体类封装属性,那么实体类必须有set该属性的方法
SpringMVC获取请求参数
通过servletAPI获取
@Controller
public class TestParam {
@RequestMapping("/servletAPI")
public String getParams(HttpServletRequest httpServletRequest){
//对于这种获取参数方式get或post均可
String username = httpServletRequest.getParameter("username");
String password = httpServletRequest.getParameter("password");
System.out.println("名字为:"+username+",密码为:"+password);
return "success";
}
}
请求路径:http://localhost:8080/springmvc/servletAPI?username=lili&password=123
通过控制器方法形参注解获取参数
@RequestParam注解
@RequestMapping("/param")
public String getParam(@RequestParam(value = "username",required = false,defaultValue = "lili") String u,@RequestParam("password") Integer p){
//get或post请求参数都可以获取
System.out.println("名字为:"+u+",密码为:"+p);
return "success";
}
请求路径:http://localhost:8080/springmvc/param?username=lili&password=234
注意:
- @RequestParam用于接收请求路径中的参数
- 当@RequestParam内的value值与传参的参数名一致,则@RequestParam内的value值可以省略
- @RequestParam有3个属性,第一个value属性,用来绑定参数名字;第二个为require属性,默认值为true,表示该属性值必须传输,不传输则报错;第三是defaultValue属性,此属性代表不传参数时的默认值
@RequestHeader注解
作用:将请求头信息与请求方法的形参进行绑定(里面的值添请求头的key)
@RequestMapping("/param")
public String getParam(@RequestHeader("accept") String accept){
System.out.println(accept);
return "success";
}
注意:这里可以得到请求头的accept的值,该注解的使用方法和@RequestParam使用方法类似
@CookieValue注解
作用:将cookie信息与请求方法的形参进行绑定(里面的值添cookie的key)
利用pojo获取请求参数
public class User {
private Integer id;
private String username;
private String password;
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
@RequestMapping("/pojo")
public String getParamByPojo(User user){
System.out.println(user);
return "success";
}
注意:
- User类必须有要封装的属性以及该属性的set方法才能对传入参数的属性进行封装
- 请求的参数也可以仅有User类的几个属性,那么这样用实体类接收,其他属性为默认值
请求路径:http://localhost:8080/springmvc/pojo?username=lili&password=123
总结:需要在控制器方法的形参位置设置实体类的形参,要保证实体类中的属性的属性名和请求参数的名字一致,就可以通过实体类的形参获取请求参数(实体类必须有set该属性的方法)
同名参数提交问题
对象封装
public class User {
private String[] hobbys;
public void setHobbys(String[] hobbys) {
this.hobbys = hobbys;
}
@Override
public String toString() {
return "User{" +
"hobbys=" + Arrays.toString(hobbys) +
'}';
}
}
@RequestMapping("/like")
public String getParams(User user){
System.out.println(user);
return "success";
}
restful形式
@RequestMapping("/like/{hobbys}")
public String getParams(@PathVariable("hobbys") String[] hobbys){
System.out.println(Arrays.toString(hobbys));
return "success";
}
其他类型都差不多,不多写了
解决获取请求参数的乱码问题
<!--设置spring处理编码的过滤器-->
<filter>
<!--名字可以随便写-->
<filter-name>CharacterEncodingFilter</filter-name>
<!--spring内的过滤器-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置请求的自定义编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--设置响应的自定义编码-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--对所有的请求进行过滤-->
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效
域对象共享数据
使用servletAPI向request域对象中共享数据
@RequestMapping("/request")
public String haveAll(HttpServletRequest request){
request.setAttribute("message", 23);
Object message = request.getAttribute("message");
System.out.println(message);
return "success";
}
使用ModelAndView向request域中共享对象
注意:
- ModelAndView作用:向域对象中共享数据,并实现页面跳转
- 使用ModelAndView时,可以使用model功能向请求域中共享数据;使用view功能设置逻辑视图
- 使用ModelAndView,那么返回值一定要为ModelAndView类型
@RequestMapping("/request")
public ModelAndView haveAll(String username){
//ModelAndView包含model和view的功能
ModelAndView modelAndView = new ModelAndView();
//向请求域中共享数据
modelAndView.addObject("message",88);
//获取数据
Map<String, Object> model = modelAndView.getModel();
Object message = model.get("message");
System.out.println(message);
//设置逻辑视图
modelAndView.setViewName("success");
System.out.println(username);
//使用ModelAndView那么返回值必须为ModelAndView
return modelAndView;
}
使用model向请求域中共享数据
@RequestMapping("/model")
public String testModel(Model model){
model.addAttribute("message", "hello world");
Object message = model.getAttribute("message");
System.out.println(message);
return "success";
}
使用modelmap向请求域中共享数据
@RequestMapping("/modelMap")
public String testModel(ModelMap modelMap){
modelMap.addAttribute("message","我是信息");
Object message = modelMap.getAttribute("message");
System.out.println(message);
return "success";
}
使用Map向请求域中共享数据
@RequestMapping("/map")
public String testMap(Map<String,Object> map){
map.put("message", 67);
Object message = map.get("message");
System.out.println(message);
return "success";
}
向session域中共享数据
@RequestMapping("/session")
public String testSession(HttpSession session){
session.setAttribute("message", "hello");
Object message = session.getAttribute("message");
System.out.println(message);
return "success";
}
向application域中共享数据
@RequestMapping("/application")
public String testApplication(HttpSession session){
//最大域对象
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("message", 67);
Object message = servletContext.getAttribute("message");
System.out.println(message);
return "success";
}
SpringMVC的视图
前言:springmvc中的视图是view接口,视图的作用是渲染数据,将模型model中的数据展示给用户
springmvc视图的种类很多,默认有转发视图和重定向视图
注意:当工程引入jstl依赖时,转发视图会自动转换为jstlView
ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。
@RequestMapping("/thymeleaf")
public String testThymeleafView(){
return "success";
}
配置视图解析器
<!--配置Thymeleaf视图解析器-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"></property>
<property name="characterEncoding" value="UTF-8"></property>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--注意:视图前缀+逻辑视图+视图后缀就是我们的物理视图-->
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
注意:在SpringMVC的配置文件中配置了该视图解析器,由此视图解析器解析之后所得到的视图为ThymeleafView
转发视图(internalResourceView)
@RequestMapping("/thymeleaf")
public String testThymeleafView(String username){
System.out.println(username+"2");
System.out.println("转发视图成功");
return "success";
}
@RequestMapping("/forward")
public String testForward(String username){
System.out.println(username+"1");
return "forward:/thymeleaf";
}
注意:转发视图也可以实现请求的转发,但不能实现视图的渲染,渲染还需要thymeleaf
重定向视图(RdirectView)
@RequestMapping("/thymeleaf")
public String testThymeleafView(String username,Integer password){
System.out.println(username+password);
return "success";
}
@RequestMapping("/redirect")
public String testRedirect(Integer password){
return "redirect:http://localhost:8080/springmvc/thymeleaf?username=lili"+"&password="+password;
}
注意:如果我们写的路径是绝对路径,那么在重定向实现页面跳转的时候,他会自动的在我们设置的绝对路径前面加上上下文路径的
视图控制器
在springmvc配置文件中配置
<!--开启mvc注解驱动-->
<mvc:annotation-driven/>
<!--视图控制器(可写多个):为当前请求设置视图名称来实现页面跳转-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
注意:设置完视图控制器标签后,那么当前请求只有视图控制器的请求被前端控制器处理,其他的请求全部404;若要解决问题,则必须开启mvc注解驱动
用表单发送put或delete请求
注意:浏览器目前只能发送get或post请求,若要发送put或delete请求,需要在web.xml中配置一个过滤器,配置了过滤器之后,发送的请求要满足两个条件才能将请求方式转换为put或delete
- 请求必须为post请求
- 当前请求必须传输请求参数_method,_method的值才为请求方式
<!--设置处理请求方式的过滤器-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--发送put请求-->
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改用户信息">
</form>
配置默认servlet处理静态资源
<!--在springmvc配置文件中配置默认servlet处理静态资源-->
<mvc:default-servlet-handler/>
<!--开启mvc注解驱动-->
<mvc:annotation-driven/>
注意:配置默认servlet处理静态资源标签的同时也应该开启mvc注解驱动,两个标签一起配置作用:当前的请求先由前端控制器处理,若处理不了,则由默认的servlet(tomcat)来处理
静态资源路径映射
xml文件式
<!--将路径中有backend的请求映射到resources目录下的backend路径后面-->
<mvc:resources location="/backend/" mapping="/backend/**"/>
<mvc:resources location="/front/" mapping="/front/**"/>
使用配置类
//说明其为一个配置类
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//设置静态资源映射
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//将路径中有backend的请求映射到classpath下的backend路径后面
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
}
@RequestBody注解
作用:将请求体中的内容与控制器方法的形参进行绑定(将json格式数据转换为java对象)
使用过程
- 导入jackson依赖(底层用到)
- 在springmvc的配置文件中设置mvc注解驱动<mvc:annotation-driven/>
- 在处理请求控制器方法的形参位置用对象或map集合将json中的属性进行封装,然后再用该注解标识
- 封装属性的实体类必须有set(封装)方法
<!--json对象互转依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
axios.post("/mvc_axios/test2",{username:"admin",password:456,age:18,gender:"女"})
.then(res=>{console.log(res.data)})
用实体类进行封装
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String gender;
}
@RequestMapping("/test2")
public void testJson(@RequestBody User user, HttpServletResponse response) throws Exception {
System.out.println(user);
response.getWriter().write("hello"+user);
}
用map集合进行封装
@RequestMapping("/test2")
public void testMap(@RequestBody Map<String,Object> map, HttpServletResponse response) throws Exception {
System.out.println(map);
response.getWriter().write("hello"+map);
}
@ResponseBody注解
作用:将所标识的控制器方法的返回值作为响应报文的响应体相应到浏览器(响应json格式数据)
使用步骤
- 导入jackson依赖
- 在springmvc的配置文件中设置mvc注解驱动<mvc:annotation-driven/>
- 将需要转换为json的java对象直接作为控制器方法的返回值,再使用该注解标识咱们的控制器方法
- 需要转换的java对象必须有get(获取)方法
注意:@ResponseBody可以放在类上或方法上,若放在类上则所有的方法的返回值都会转化为json串,若放在方法上,则只有特定的方法返回值会转化为json串
@ResponseBody
@RequestMapping("/ResponseBody")
public User testResponseBody(){
User user = new User();
user.setPassword("123");
return user;
}
@RestController注解
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且同时为类中的每个方法添加了@ResponseBody注解
SpringMVC实现下载功能
@RequestMapping("/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws Exception{
//获取servletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件真实路径
String realPath = servletContext.getRealPath("/img/hot.png");
//创建输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
//串讲字节数组(fileInputStream.available():获取当前字节输入流所对应的文件所有的字节数;为的就是将文件全部字节都读到字节数组中)
byte[] bytes = new byte[fileInputStream.available()];
//将流读取到字节数组中
fileInputStream.read(bytes);
//创建HttpHeader对象设置响应头信息
MultiValueMap<String,String> httpHeaders = new HttpHeaders();
//设置需要下载方式以及下载文件的名称
httpHeaders.add("Content-Disposition", "attachment;filename=hot.png");
//设置响应状态码
HttpStatus httpStatus = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders, httpStatus);
//关闭输入流
fileInputStream.close();
return responseEntity;
}
SpringMVC实现上传功能
文件上传的要求
- form表单的请求方式必须为post
- form表单必须来设置enctype属性指定以二进制的形式传输数据
<form th:action="@{/up}" method="post" enctype="multipart/form-data">
头像:<input type="file" name="photo"><br>
<input type="submit" value="上传">
</form>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--配置文件上传解析器,将文件转换为MultipartFile格式-->
<!--注意:springmvc.xml中获取该bean是通过id来获取,id为multipartResolver固定-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件上传编码-->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
@RequestMapping("/up")
public String testUp(MultipartFile photo,HttpSession httpSession) throws Exception {
//获取文件名,有后缀名
String filename = photo.getOriginalFilename();
//获取上传文件的后缀名
String hzName = filename.substring(filename.lastIndexOf("."));
//获取UUID(在相同的系统本次服务中唯一[防止上传相同文件名中的文件出现覆盖现象])
String u = UUID.randomUUID().toString();
//拼接新的文件名
filename=u+hzName;
//过去servletContext对象
ServletContext servletContext = httpSession.getServletContext();
//获取当前工程的真实路径
String path = servletContext.getRealPath("photo");
//创建path所对应的文件对象
File file = new File(path);
if (!file.exists()){
file.mkdir();
}
String finalPath=path+File.separator+filename;
//将photo所对应的文件直接上传到我们指定文件所对应的位置
photo.transferTo(new File(finalPath));
return "success";
}
注意:MultipartFile是用来封装上传的对象(使用前先配置文件上传解析器;注意导入依赖),对象名必须与表单上传的name相对应
拦截器
注意:
- SpringMVC中的拦截器用于拦截控制器方法的执行
- SpringMVC拦截器需要实现HandlerInterceptor
- SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置
使用过程
将自己写的拦截器交给spring容器来管理,并进行容器扫描
@Component
public class FirstInterceptor implements HandlerInterceptor {
//在控制器方法执行之前执行该方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//返回值为true表示放行,返回值为false表示拦截
return true;
}
//在控制器方法执行之后执行的方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
//在渲染视图完毕之后执行该方法
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
<!--在springmvc中配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--配置拦截器拦截的路径-->
<mvc:mapping path="/*"/>
<!--
/*表示拦截上下文路径下的一层目录
/**表示拦截上下文路径下的多层目录
-->
<!--配置拦截器不拦截的路径-->
<mvc:exclude-mapping path="/other"/>
<!--配置引用的拦截器-->
<ref bean="firstInterceptor"></ref>
<!--方法一:不需要将拦截器交给spring容器来管理-->
<!--<bean class="cn.tedu.interceptor.FirstInterceptor"/>-->
<!--方法二:需要将拦截器交给spring容器来管理,此方法不需要将对象交给spring容器来管理-->
<!--<ref bean="firstInterceptor"></ref>-->
</mvc:interceptor>
</mvc:interceptors>
多个拦截器的执行顺序
<mvc:interceptors>
<ref bean="firstInterceptor"/>
<ref bean="secondInterceptor"/>
</mvc:interceptors>
多个拦截器的执行顺序和在springMVC的配置文件中的配置有关,但每个方法的执行顺序不一样
- preHandle方法会按照配置拦截器的顺序执行
- postHandle和afterCompletion会按照配置拦截器顺序的反向执行
注意:若拦截器中有某个拦截器的preHandle方法返回了false,则该拦截器之前的preHandle方法都会执行,所有拦截器的postHandle方法都不执行,拦截器的preHandle方法返回false之前的拦截器的afterCompletion()会执行
异常处理器
SpringMVC提供了处理控制器方法执行过程中出现的异常的接口:HandlerExceptionResolver、HandlerExceptionResolver接口的实现类:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver,其中自定义异常处理器:SimpleMappingExceptionResolver,如果不配置自定义异常解析器,那么就会使用默认的异常解析器
xml文件内配置异常处理
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--key:设置要处理的异常,value设置出现该异常时要跳转页面所对应的逻辑视图-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--设置共享在请求域中的异常信息的属性名,设置之后就可以根据异常信息的属性名来访问异常信息的属性值-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
使用注解配置异常处理
//全局异常处理
//里面的参数表示不管哪个类上只要加了里面的两个注解,那么都会被全局异常处理器来进行异常处理
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class ExceptionController {
//若当前控制器方法执行的过程中出现了下面异常,那么它就会执行下面方法来处理异常
@ExceptionHandler(ArithmeticException.class)
public String handleException(Throwable ex,Model model){
//用ex代表该类异常属性在请求域中共享
model.addAttribute("ex", ex);
return "error";
}
}
注解配置SpringMVC
前言:使用注解和配置类的方式代替web.xml和springMVC配置文件的功能
//用来代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
//设置一个配置类,代替spring的配置文件
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//设置一个配置类,代替springmvc的配置文件
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
//设置springmvc前端控制器的url-pattern
protected String[] getServletMappings() {
return new String[]{"/"};
}
//设置当前的过滤器
@Override
protected Filter[] getServletFilters() {
//创建编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
//创建处理请求方式过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
}
}
//将该类标识为配置类
@Configuration
//扫瞄具体包下的组件
@ComponentScan("cn.tedu.controller")
//开启mvc的注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//默认的servlet处理静态资源
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置视图控制器
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Bean
//@Bean注解可以将标识的方法的返回值交给spring容器保存,bean的id为方法的方法名,也可以改@Bean("a"):id就变成a了
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
//配置拦截器
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**").excludePathPatterns("/a");
}
//配置异常解析器
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ArithmeticException", "error");
simpleMappingExceptionResolver.setExceptionMappings(properties);
//共享异常信息到请求域
simpleMappingExceptionResolver.setExceptionAttribute("ex");
resolvers.add(simpleMappingExceptionResolver);
}
//配置生成模板解析器
@Bean
public ITemplateResolver templateResolver(){
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//生成模板引擎,并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器,并为视图解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}