SpringMvc
轻量级web框架,主要针对Contoller层
工作流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器
- 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapter处理器适配器
- HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户
1、搭建springMvc环境
1.1、配置文件
在web工程下配置DispatcherServlet的servlet
<!--注册DispatcherServlet,这是springmvc的核心,就是个servlet-->
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的请求;(不包括.jsp)-->
<!--/* 匹配所有的请求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
在springMvc中,视图都放在WEB-INF中以保证视图安全。客户端必须通过访问servlet才能跳转到WEB-INF
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 处理映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/page/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
编写一个Controller,继承Controller接口
public class FirstController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
//ModelAndView 封装了模型和视图
ModelAndView mv = new ModelAndView();
//模型里封装数据
mv.addObject("HelloMvc","Hello springMVC!");
//封装跳转的视图
mv.setViewName("HelloMvc");
//不是有个视图解析器吗
//这玩意就是为了省事的,自动给你加个前缀后缀
//就成了 /jsp/hellomvc.jsp 不就是拼串吗
return mv;
}
}
spingMvc默认去找WEB-INF下的spring-mvc-servlet.xml ,要创建一个spring-mvc-servlet.xml 文件
<bean id="/hellomvc" class="com.xinzhi.controller.FirstController"/>
1.2、注解
注解就要扫包
<!-- 自动扫包 -->
<context:component-scan base-package="com.xinzhi"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!-- 让springmvc自带的注解生效 -->
<mvc:annotation-driven />
@Controller//说明是Controller层
@RequestMapping("/user/") //路径
public class UserController {
//一个主路径可以有多个子路径,多个功能可以由一个servlet来处理
@RequestMapping("login")
public String login(Model model) {
model.addAttribute("ogin","denlu!");
return "Login";
}
@RequestMapping("register")
public String register(Model model) {
model.addAttribute("egister","zhuce!");
return "Register";
}
}
2、深入学习
2.1、视图模型分离
之前返回的是模型加视图,耦合性太高,有时候不需要跳转视图也会强行跳转
使用Model参数
@RequestMapping(value = "login")
public String login(Model model) {
model.addAttribute("ogin","登录!");
return "Login";
}
2.2、@RequestMapping
这个注解很强大,可以放在方法上也可以放在类上,放在类上所有方法都默认加上
该注解有六个参数
- value:访问路径
- method :指定请求类型,如post、get等
- consumes :指定处理请求后提交的数据类型(Content-Type),如json,text
- produces :指定返回值类型
- params :指定request中必须有哪些参数,有才能让此方法执行
- headers :指定request中必须包含某些指定的header值 ,有才能让此方法执行
2.3、字符集处理
<!--在web.xml里配置-->
<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>
2.4、传参
不需要在一个个的getParameter(),要什么直接拿
@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
System.out.println(user.getUsername());
System.out.println(user.getId());
System.out.println(user.getPassword());l
model.addAttribute("login","登录!");
return "Login";
}
2.5、返回json数据
手动转换
<!--依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
//方法上设置
@RequestMapping(value = "get",produces = "text/json;charset=utf-8")
自动转换
<mvc:annotation-driven >
<mvc:message-converters>
<bean id="fastjson"
class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
@RequestMapping("set")
//直接将返回值以数据的形式返回到响应体
@ResponseBody
public User set(@Validated User user, BindingResult br) {
List<ObjectError> allErrors = br.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
}
System.out.println(user.getId());
return user;
}
2.6、数据转化
@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date birthday;
@NumberFormat(pattern = "#,###,###.#")
private Double salary;
2.7、数据校验
@Max(value = 20L,message = "id不能大于20")
private int id;
@NotNull(message = "名字不能为空")
private String username;
@Null
private String password;
@RequestMapping("set")
@ResponseBody
//用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
public User set(@Validated User user, BindingResult br) {
//拿到错误,不会挡住请求
List<ObjectError> allErrors = br.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
}
System.out.println(user.getId());
return user;
}
2.8、重定向和请求转发
直接返回字符串就是请求转发
return "index"
返回时添加redirect就是重定向
return "redirect:http://www.baidu.com"
return "redirect:/index.jsp"
重定向可以传参
@RequestMapping("reg")
public String reg(RedirectAttributes attributes) {
attributes.addAttribute("name","lin");
return "redirect:/index.jsp"
}
3、restful
请求url | 请求方式 | 操作 |
---|---|---|
/user/1 | get | 获取一个id为1的user |
/user/1 | delete | 删除id为1的对象 |
/user/1 | put | 更新id为1的对象 |
/user/add | post | 新增一个对象 |
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@RequestMapping(value = "/add", method = RequestMethod.POST)
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
//或
@GetMapping("/user/{id}")
@PostMapping("/user/add")
@DeleteMapping("/user/{id}")
@PutMapping("/user/{id}")
//根据id获取一个对象
@GetMapping("/rest/{id}")
@ResponseBody
public User getUser(@PathVariable int id) {
return new User();
}
//根据id获取一个对象
@DeleteMapping("/rest/{id}")
@ResponseBody
public R deleteUser(@PathVariable int id) {
System.out.println(id);
return R.success();
}
//根据id获取一个对象
@PutMapping("/rest/{id}")
@ResponseBody
public R updateUser(@PathVariable int id, User user) {
System.out.println(id);
System.out.println(user);
return R.fail();
}
//添加一个对象
@PostMapping("/rest/add")
@ResponseBody
public R addUser(User user) {
System.out.println(user);
return R.build(304, "插入发生异常")
.put("reason ", "超时了!")
.put("other", "暗藏");
}
ajax
$.ajax( {
type : "GET",
url : "http://localhost:8080/springmvc/user/rest/1",
dataType : "json",
success : function(data) {
console.log("get请求!---------------------")
console.log(data)
}
});
4、拦截器
- SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的任意请求。
- 熟练掌握 SpringMVC 拦截器对于我们开发非常有帮助,在没使用权限框架( shiro,spring security )之前,一般使用拦截器进行认证和授权操作。
- SpringMVC拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。
4.1、自定义拦截器
自定义的 Interceptor类要实现了pring的HandlerInterceptor接口
public class LoginInterceptor implements HandlerInterceptor {
//拦截前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) {
//放行
return true;
}
//拦截后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
//所有拦截完成后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
}
4.2、拦截流程
客户端发出请求,经过preHandle,通过后入controller,controller处理完进入postHandle通过view、afterCompletion响应到用户
4.3、拦截规则
我们可以配置多个拦截器,每个拦截器中都有三个方法。下面将总结多个拦截器中的方法执行规律。
- preHandle:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。
- postHandle:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方
都返回true时才会调用。 - afterCompletion:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true就会
调用
4.4、登录拦截器
public class LoginInterceptor implements HandlerInterceptor {
/**
在执行Controller方法前拦截,判断用户是否已经登录,
登录了就放行,还没登录就重定向到登录页面
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
HttpSession session = request.getSession();
User user = session.getAttribute("user");
if (user == null){
//还没登录,重定向到登录页面
response.sendRedirect("/user/toLogin");
}else {
//已经登录,放行
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) {}
}
编写完SpringMVC拦截器,我们还需要在springmvc.xml配置文件中,配置我们编写的拦截器,配置代码如下:
- 配置需要拦截的路径
- 配置不需要拦截的路径
- 配置我们自定义的拦截器类
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--
mvc:mapping:拦截的路径
/**:是指所有文件夹及其子孙文件夹
/*:是指所有文件夹,但不包含子孙文件夹
/:Web项目的根目录
-->
<mvc:mapping path="/**"/>
<!--mvc:exclude-mapping:不拦截的路径-->
<mvc:exclude-mapping path="/user/toLogin"/>
<mvc:exclude-mapping path="/user/login"/>
<!--class属性就是我们自定义的拦截器-->
<bean id="loginInterceptor"class="cn.zwq.springmvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
5、文件上传
maven依赖
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
配置bean
<!--文件上传配置-->
<!--这个bena的id必须为:multipartResolver-->
<bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1-->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
前端页面
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
controller
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws Exception{
//获取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
System.out.println("上传文件名 : "+uploadFileName);
//上传路径保存设置
String path = "D:/upload";
//如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件保存地址:"+realPath);
file.transferTo(new File(path+"/"+uploadFileName));
return "yes";
}
6、文件下载
ResponseEntity
@GetMapping("/download")
@ResponseBody
public ResponseEntity<byte[]> download2(){
try {
String fileName = "czx.jpg";
byte[] bytes = FileUtils.readFileToByteArray(new File("D:/upload/"+fileName));
HttpHeaders headers=new HttpHeaders();
headers.set("Content-Disposition","attachment;filename="+
URLEncoder.encode(fileName, "UTF-8"));
headers.set("charsetEncoding","utf-8");
headers.set("content-type","multipart/form-data");
ResponseEntity<byte[]> entity=new ResponseEntity<byte[]>(bytes,headers,HttpStatus.OK);
return entity;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
7、全局捕获异常
@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
e.printStackTrace();
//把日志存起来
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
return mv;
}
}