异步调用
异步请求参数的传递,参数是封装在请求体中。对比在地址栏中使用同步的参数传递,参数是使用字符串拼接在地址栏上。所以异步请求的参数获取 需要使用@ResponseBody注解。
异步请求接收普通参数
前端代码:
//为id="testAjax"的组件绑定点击事件
$("#testAjax").click(function(){
//发送异步调用
$.ajax({
//请求方式:POST请求
type:"POST",
//请求的地址
url:"ajaxController",
//请求参数(也就是请求内容)
data:'ajax message',
//响应正文类型
dataType:"json",
//请求正文的MIME类型
contentType:"application/text",
success:function (data) {
alert(data.code);
}
});
});
后端代码:
@RequestMapping("/ajaxController")
//使用@RequestBody注解,可以将请求体内容封装到指定参数中
public String ajaxController(@RequestBody String message){
System.out.println("ajax request is running..."+message);
int i= 1/0;
return "page.jsp";
}
异步请求接收对象
前端传递的是json格式的数据:
data:'{"name":"Jock","age":39}',
后端处理:
@RequestMapping("/ajaxPojoToController")
//如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
//注意:POJO中的属性如果请求数据中没有,属性值为null,POJO中没有的属性如果请求数据中有,不进行映射
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
异步请求接收对象数组
前端传递的是json格式的数组:
data:'[{"name":"Jock","age":39},{"name":"Jockme","age":40}]',
后端处理:
@RequestMapping("/ajaxListToController")
//如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的对象数组,数据将自动映射到集合参数中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
异步请求参数返回
前后端之间的数据都是通过json传递,所以需要转json的工具
在pom.xml中导入坐标
<!--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>
在返回方法的上面加上@ResponceBody 注解 ,自动解析为json格式的数据。
跨域访问
- 协议
- 地址(ip)
- 端口
- 以上其中一个不同就视为跨域
默认情况下跨域访问是不支持的,如果你的方法或者模块需要跨域访问,只需要在方法上添加@CrossOrigin 注解即可。
拦截器
拦截器简介:是一种动态拦截方法调用的机制
作用:
1. 在指定的方法调用的前后执行预先设定好的代码
2. 阻止原始方法的执行
核心原理:AOP思想
拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器开发入门
自定义拦截器类:
public class Myintercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// handler : 被调用的处理对象,本质上是一个方法对象,对反射中的Method对象进行了再包装
// 返回值为false,被拦截的处理器将不执行
System.out.println("这是前置方法----a1");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
//ModelAndView :如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调账
System.out.println("这是后置方法----b1");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// ex:如果处理器执行过程总出现异常对象,可以针对异常情况进行单独处理
System.out.println("这是完成方法----c1");
}
}
注意事项:自定义拦截器必须实现HandlerInterceptor,三个方法可以写任意个。前置方法中的返回值为true时,才会继续向下执行,为false时, 后置 和 完成都不会执行。执行顺序固定为 前置–>后置–>完成
拦截器值制作完成之后还需要在springMVC.xml文件中进行配置
<mvc:interceptors>
<mvc:interceptor>
<!--指定拦截路径-->
<mvc:mapping path="/ajaxController"/>
<!--指定拦截器-->
<bean class="com.myTest.intercepter.Myintercepter"/>
</mvc:interceptor>
</mvc:interceptors>
可以配置多个拦截器,执行顺序和拦截器的配置顺序相关。
异常处理
异常处理器:异常处理器需要实现HandlerExceptionResolver接口,在实现类上添加@Component注解,将这个类交给spring管理,spring看见实现了HandlerExceptionResolver接口,就知道时异常处理类,那么只要在这个模块中出现了异常那么都会被这个异常处理器捕获,进行处理。
异常处理类:
@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;
//Exception ex 参数:异常发生时,异常就会被捕获,作为参数传递到这个类的这个方法中进行处理
}
}
根据不同的异常进行不同的处理。
使用注解开发异常处理
@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";
}
}
@ControllerAdvice 注解作用:申明这个类时异常通知类
@ExceptionHandler(NullPointerException.class) 注解作用:申明方法是异常处理器,后面添加实际异常处理类型,根据不同的异常执行不同的异常处理器。
@ResponseBody 注解作用:将数据转换为json返回(前提在pom文件中加载坐标),否则会被当作页面跳转。
使用注解开发和异常处理类的差别。注解开发 加载时机比较早,在DispatcherServlet加载完成之后就进行加载,比如说,前端传递的是String类型,后端使用int类型接收,那么注解方式能捕获到这个类型转换异常,使用继承接口的方式则捕获不到这个异常。
如何自定义异常?
实现对应异常接口
将对应异常分装为自定义异常
再异常处理中获取封装的信息,getMessage() 即可获取自己传入的信息
SpringMVC整合文件上传功能
因为是整合别人的功能首先肯定就是导入对应的坐标
pom.xml
<!--文件上传下载-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
在SpringMVC.xml配置文件中,将处理文件上传的bean申明
<!--配置文件上传处理器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件上传文件大小上限,还有其他很多设置-->
<property name="maxUploadSize" value="1024000000"/>
</bean>
在文件中使用,只需要在形参中申明这个类型的参数即可。Spring自动将文件封装到这个对象中。
注意:表单提交的属性名一定要和形参名相同,上传多个文件时,又形参名和name属性名进行对应。
@Controller
public class FileUploadController {
@RequestMapping(value = "/fileupload")
//参数中定义MultipartFile参数,用于接收页面提交的type=file类型的表单,要求表单名称与参数名相同
public String fileupload(MultipartFile file,MultipartFile file1,MultipartFile file2, HttpServletRequest request) throws IOException {
System.out.println("file upload is running ..."+file);
// MultipartFile参数中封装了上传的文件的相关信息
// System.out.println(file.getSize()); 获取文件的字节大小
// System.out.println(file.getBytes()); 获取文件对应字节数组的地址
// System.out.println(file.getContentType()); 获取上传文件的类型
// System.out.println(file.getName()); 获取形参名,就是上面的file
// System.out.println(file.getOriginalFilename()); 获取文件名
// System.out.println(file.isEmpty());
//首先判断是否是空文件,也就是存储空间占用为0的文件
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()));
}
return "page.jsp";
}
}
前端代码:
<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>
ResultFul开发入门
总的来说就是使用数字代替操作名称,在后端解析路径上的数字,根据数字不同进行不同的数据处理。
//rest风格访问路径完整书写方式
@RequestMapping("/user/{id}")
//使用@PathVariable注解获取路径上配置的具名变量,该配置可以使用多次
public String restLocation(@PathVariable Integer id){
System.out.println("restful is running ....");
return "success.jsp";
}
rest风格访问路径简化书写方式,配合类注解@RequestMapping使用,在对应模块的类上指定这个模块访问的名称。
//@Controller
//@ResponseBody
//设置rest风格的控制器
@RestController
//设置公共访问路径,配合下方访问路径使用
@RequestMapping("/user/")
public class UserController {
//rest风格访问路径简化书写方式,配合类注解@RequestMapping使用
@RequestMapping("{id}")
public String restLocation2(@PathVariable Integer id){
System.out.println("restful is running ....get:"+id);
return "success.jsp";
}
}
使用ResultFul访问风格之后,那么跳转的页面就是webapp(根目录下)的user文件夹下的页面。这点需要注意。
在实际工作中,我们都是给前端返回json数据,不会进行页面跳转。返回json数据需要@ResponseBody,这个配置的bean文件又需要交由bean管理,所以使用@Controller注解。@RestController注解,作用就是两者的合二为一。
但是现在我们并没有对请求方式进行识别,只是对访问路径进行了设置。
@RequestMapping("/user/")
public class UserController {
//接收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";
}
}
现在只需要在浏览器端指定访问的方式即可:但是在浏览器端指定访问方式时,需要注意,表单form的method属性只能指定get/post方式,put和delete方式需要进行另外设置
<form action="/user/1" method="post">
<%--当添加了name为_method的隐藏域时,可以通过设置该隐藏域的值,修改请求的提交方式,切换为PUT请求或DELETE请求,但是form表单的提交方式method属性必须填写post--%>
<%--该配置需要配合HiddenHttpMethodFilter过滤器使用,单独使用无效,请注意检查web.xml中是否配置了对应过滤器--%>
<input type="hidden" name="_method" value="PUT"/>
<input type="submit"/>
</form>
另外put方式和delete方式需要进行识别还需要配置过滤器,不然后端无法进行识别
在web.xml文件中进行入下配置
<!--配置拦截器,解析请求中的参数_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>
<!--只对serlvet访问进行拦截-->
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>