SpringMVC
异步请求
异步请求传参和响应
@Controller
public class AjaxController {
// @RequestMapping("/ajaxController")
// public String ajaxController(){
// System.out.println("ajax request is running...");
// return "page.jsp";
// }
@RequestMapping("/ajaxController")
//使用@RequestBody注解,可以将请求体内容封装到指定参数中
public String ajaxController(@RequestBody String message){
System.out.println("ajax request is running..."+message);
return "page.jsp";
}
@RequestMapping("/ajaxPojoToController")
//如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
//注意:POJO中的属性如果请求数据中没有,属性值为null,POJO中没有的属性如果请求数据中有,不进行映射
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
//如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的对象数组,数据将自动映射到集合参数中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
//使用注解@ResponseBody可以将返回的页面不进行解析,直接返回字符串,该注解可以添加到方法上方或返回值前面
@RequestMapping("/ajaxReturnString")
// @ResponseBody
public @ResponseBody String ajaxReturnString(){
System.out.println("controller return string ...");
return "page.jsp";
}
@RequestMapping("/ajaxReturnJson")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的POJO对象转成json格式数据
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的保存POJO对象的集合转成json数组格式数据
public List ajaxReturnJsonList(){
System.out.println("controller return json list...");
User user1 = new User();
user1.setName("Tom");
user1.setAge(3);
User user2 = new User();
user2.setName("Jerry");
user2.setAge(5);
ArrayList al = new ArrayList();
al.add(user1);
al.add(user2);
return al;
}
@RequestMapping("/cross")
@ResponseBody
//使用@CrossOrigin开启跨域访问
//标注在处理器方法上方表示该方法支持跨域访问
//标注在处理器类上方表示该处理器类中的所有处理器方法均支持跨域访问
@CrossOrigin
public User cross(HttpServletRequest request){
System.out.println("controller cross..."+request.getRequestURL());
User user = new User();
user.setName("Jockme");
user.setAge(39);
return user;
}
}
- 名称 : @RequestBody
- 类型 : 形参注解
- 位置 : 处理器类(controller)中的方法形参前方
- 作用 : 将异步提交数据组织成标准请求参数格式,并赋值给形参
- 范例
- 普通文本
@RequestMapping("/ajaxController")
public String ajaxController(@RequestBody String message){
System.out.println(message);
return "page.jsp";
}
- pojo
@RequestMapping("/ajaxPojoToController")
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
- 集合
@RequestMapping("/ajaxListToController")
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
跨域访问
-
当通过域名A下的操作去访问域名B下的资源时,称为跨域访问
-
名称 : @CrossOrigin
-
类型 : 方法注解,类注解
-
位置 : 处理器类中的方法上方 或 类上方
-
作用 : 设置当前处理器方法/处理器类中所有方法支持跨域访问
-
范例
@RequestMapping("/ajaxReturnJson")
@ResponseBody
@CrossOrigin
public User ajaxReturnJson(){
User user = new User();
user.setName("Jockme");
user.setAge(40);
return user;
}
- 跨域访问的概念
- 协议
- IP地址
- 端口
- 域名
- 谁导致的跨域问题 : 浏览器 后端代码直接网络调用不会出现跨域
- 解决什么问题 : 多项目之间的相互调用会存在跨域问题
拦截器
拦截器简介
- 拦截器(Interceptor)是一种动态拦截方法调用的机制
- 作用:
- 在指定的方法调用钱后执行预先设定后的代码
- 阻止原始方法的执行
- 增强
- 核心原理 : AOP思想
- 拦截器链 : 多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器VS过滤器
- 归属不同 : Filter属于Servlet技术 , Interceptor属于SpringMVC技术
- 拦截内容不同 : Filter对所有访问进行增强 , Interceptor仅针对SpringMVC(拦截Spring的Bean)的访问进行增强
拦截器开发
步骤 :
-
制作拦截器功能类 (通知)
-
配置拦截器的执行位置 (切入点)
-
实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("preHandle");
return true;
//如果是false则不会向下放行
}
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");
}
}
- 配置拦截器
- 配置执行位置
- 配置拦截器执行类
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/showPage"/>
<bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 注意 : 配置顺序为先配置执行位置 , 后配置执行类
拦截器执行流程
拦截器参数
- handler : 被调用的处理器对象,本质上是一个方法对象(method对象),对反射中的Method对象进行了在包装
- 返回值为false,被拦截的处理器将不执行
- moderlAndView : 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
- ex : 如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
拦截器配置项
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--设置拦截器的拦截路径,支持*通配-->
<!--/** 表示拦截所有映射-->
<!--/* 表示拦截所有/开头的映射-->
<!--/user/* 表示拦截所有/user/开头的映射-->
<!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射-->
<!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射-->
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的-->
<mvc:exclude-mapping path="/b*"/>
<!--指定具体的拦截器类-->
<bean class="MyInterceptor"/>
</mvc:interceptor>
<!--配置多个拦截器,配置顺序即为最终运行顺序-->
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="MyInterceptor2"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="MyInterceptor3"/>
</mvc:interceptor>
</mvc:interceptors>
多拦截
责任链模式
统一异常处理器
1.实现HandlerExceptionResolver接口
- 根据异常的种类不同,进行分门别类的管理,返回不同的信息
/*
全局代码只要有异常都会到这里来处理,前提要是Spring管理的bean
*/
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("my exception is running ...."+ex);
ModelAndView modelAndView = new ModelAndView();
if( ex instanceof NullPointerException){
modelAndView.addObject("msg","空指针异常");
}else if ( ex instanceof ArithmeticException){
modelAndView.addObject("msg","算数运算异常");
}else{
modelAndView.addObject("msg","未知的异常");
}
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
2.使用注解
@Component
//使用注解开发异常处理器
//声明该类是一个Controller的通知类,声明后该类就会被加载成异常处理器
@ControllerAdvice
public class ExceptionAdvice {
//类中定义的方法携带@ExceptionHandler注解的会被作为异常处理器,后面添加实际处理的异常类型
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public String doNullException(Exception ex){
return "空指针异常";
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public String doArithmeticException(Exception ex){
return "ArithmeticException";
}
@ExceptionHandler(Exception.class)
@ResponseBody
public String doException(Exception ex){
return "all";
}
}
两种异常处理方式对比
- 注解处理器可以拦截到入参类型转换异常
- 非注解处理器无法拦截到入参类型转换异常
文件上传
- 导包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 页面表单
<form action="/fileupload" method="post" enctype="multipart/form-data">
上传LOGO:<input type="file" name="file"/><br/>
<input type="submit" value="上传"/>
</form>
- SpringMVC配置
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
- 控制器
@RequestMapping(value = "/fileupload")
public void fileupload(MultipartFile file){
file.transferTo(new File("file.png"));
}
- 上传文件的注意事项
if(!file.isEmpty()){
//如果大小在范围要求内正常处理,否则抛出自定义异常告知用户(未实现)
//获取原始上传的文件名,可以作为当前文件的真实名称保存到数据库中备用
String fileName = file.getOriginalFilename();
//设置保存的路径
String realPath = request.getServletContext().getRealPath("/images");
//保存文件的方法,指定保存的位置和文件名即可,通常文件名使用随机生成策略产生,避免文件名冲突问题
file.transferTo(new File(realPath,file.getOriginalFilename()));
}
//测试一次性上传多个文件
if(!file1.isEmpty()){
String fileName = file1.getOriginalFilename();
//可以根据需要,对不同种类的文件做不同的存储路径的区分,修改对应的保存位置即可
String realPath = request.getServletContext().getRealPath("/images");
file1.transferTo(new File(realPath,file1.getOriginalFilename()));
}
if(!file2.isEmpty()){
String fileName = file2.getOriginalFilename();
String realPath = request.getServletContext().getRealPath("/images");
file2.transferTo(new File(realPath,file2.getOriginalFilename()));
}
//重名问题
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
Restful
简介
- Rest (REpresentational State Transfer) 一种网络资源的访问风格,定义了网络资源的访问方式
- 范例
//传统风格
http://localhost/user/get?id=1
http://localhost/deleteUser?id=1
//Rest风格
http://localhost/user/1
-
Restful是按照Rest风格访问网络资源
-
优点
- 隐藏资源的访问行为,通过地址无法得知做的是何种操作
- 书写简化
-
不同请求方式代表不同的操作
-
url: /user/1
- GET: 查询
- POST: 保存
- DELETE:删除
- PUT:修改
-
老版使用方式
- /user/findById
- /user/save
- /user/deleteById
- /user/updateById
Restful开发入门
//@Controller
//@ResponseBody
//设置rest风格的控制器
@RestController
//设置公共访问路径,配合下方访问路径使用
@RequestMapping("/user/")
public class UserController {
//rest风格访问路径完整书写方式
@RequestMapping("/user/{id}")
//使用@PathVariable注解获取路径上配置的具名变量,该配置可以使用多次
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running ....");
return "success.jsp";
}
//rest风格访问路径简化书写方式,配合类注解@RequestMapping使用
@RequestMapping("{id}")
public String restLocation2(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收GET请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.GET)
//接收GET请求简化配置方式
@GetMapping("{id}")
public String get(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
//接收POST请求配置方式
@RequestMapping(value = "{id}",method = RequestMethod.POST)
//接收POST请求简化配置方式
@PostMapping("{id}")
public String post(@PathVariable Integer id){
System.out.println("restful is running ....post:"+id);
return "success.jsp";
}
//接收PUT请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.PUT)
//接收PUT请求简化配置方式
@PutMapping("{id}")
public String put(@PathVariable Integer id){
System.out.println("restful is running ....put:"+id);
return "success.jsp";
}
//接收DELETE请求简化配置方式
@RequestMapping(value = "{id}",method = RequestMethod.DELETE)
//接收DELETE请求简化配置方式
@DeleteMapping("{id}")
public String delete(@PathVariable Integer id){
System.out.println("restful is running ....delete:"+id);
return "success.jsp";
}
}
- 开启SpringMVC对Restful风格的访问支持过滤器,即可通过页面表单提交PUT和DELETE请求
<!--配置拦截器,解析请求中的参数_method,否则无法发起PUT请求与DELETE请求,配合页面表单使用-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
- 页面表单使用隐藏域提交请求类型,参数名称固定为_method,必须配合提交类型method=post使用
<form action="/user/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit"/>
</form>