类型转换器
SpringMVC实际上自带了一些简单的类型转换器:
String---->int/float/double
String------>boolean类型
全局类型转换器
全局类型转换器:在springmvc.xml配置一次,在任意controller层都可以使用。
1.在springmvc的配置文件中配置一个FormattingConversionServiceFactoryBean 2.Mvc:annotation-driven标签配置一个converservice-service属性
3.自定义一个类实现converter接口
例子1:通过form表单获取数据
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> "UTF-8">Insert title here "user/test03?birth=1994-11-29">把字符串转为日期
public class User { private String username; private boolean sex; private Integer age; private double salary; private Date birthday; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "User [username=" + username + ", sex=" + sex + ", age=" + age + ", salary=" + salary + ", birthday=" + birthday + "]"; } }
public class DateTimeConverter implements Converter{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); @Override public Date convert(String arg0) { try { return sdf.parse(arg0); } catch (ParseException e) { e.printStackTrace(); } return null; }
public class NumberConverter implements Converter{ DecimalFormat df = new DecimalFormat("###,###.##"); @Override public Double convert(String arg0) { try { Number parse = df.parse(arg0); return parse.doubleValue(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
@RequestMapping("test02") public String test02(User user) { System.out.println("全局数据转换:"+user); return "success"; }
不使用注解:
使用注解:类上加注解
xml文件:
输出结果:
例子2:通过a标签获取数据
@RequestMapping("test04") public String test04(Date birth) { System.out.println("全局数据类型转换"+birth); return "success"; }
输出结果:
局部类型转换器
局部类型转换器:在controller层的方法上每一次使用都需要配置一次。
@DateTimeFormat:时间类型的类型转换器,指定pattern属性值
@NumberFormat:数值类型的类型转换器
例子1:通过form表单获取数据
@DateTimeFormat注解可以放在方法参数上
也可以放在User类的属性上
例子2:通过a标签
输出结果:
文件上传与下载
1 文件上传
前端页面:
必须发送post请求
Form表单的encytype=“multipart/form-data”
type=file
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> "UTF-8">Insert title here
后端要求:
导入文件上传所需要的jar包:commons-fileupload.jar 和commons-io.jar
在springmvc.xml配置一个多媒体解析器:CommonsMultiPartResolver
在controller层的方法上使用MultiPartFile类型的参数接收一个文件
@RequestMapping("/upload") public ModelAndView upload(MultipartFile file,HttpServletRequest request,String username) throws IOException { ServletContext context = request.getServletContext(); String realPath = context.getRealPath("/upload"); File directory = new File(realPath); if(!directory.exists()) { directory.mkdirs(); } String fileName = UUID.randomUUID().toString().replaceAll("-", "")+file.getOriginalFilename(); FileOutputStream fos = new FileOutputStream(realPath+"/"+ fileName); InputStream fis = file.getInputStream(); IOUtils.copy(fis, fos); fis.close(); fos.close(); ModelAndView mv = new ModelAndView(); mv.setViewName("success"); mv.addObject("fileName",fileName); return mv; }
2 文件下载
文件下载要求:
返回值类型必须为ResponseEntity类型
设置两个响应头信息
@RequestMapping("/download") public ResponseEntity<byte[]> download(HttpServletRequest request) throws IOException { String fileName = "风吹麦浪.mp3"; String realPath = request.getServletContext().getRealPath("/WEB-INF/"+fileName); FileInputStream fis = new FileInputStream(new File(realPath)); byte[] body = new byte[fis.available()]; fis.read(body);
fileName = new String(fileName.getBytes("gbk"),"iso8859-1");
MultiValueMapheaders = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename="+fileName);
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(body, headers, statusCode);
return responseEntity;
}
拦截器[Interceptor]
拦截器简介及实现
拦截器类似于javaweb学过的filter【过滤器】,是在请求到达目标方法之前拦截请求,然后进行过滤、权限验证。类似于现实中安检员。
具体实现:
自定义一个类,实现HandlerInterceptor接口
public class FirstInterceptor implements HandlerInterceptor { /** * preHandle:在目标方法执行之前执行 * 返回值:boolean类型,true表示放行该请求,false表示拦截该请求 * 作用:日志、权限验证、获取连接 */ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("FirstInterceptor preHandle"); return true; } /** * postHandle:在目标handler方法执行之后,在视图渲染(render)之前【给页面填充数据】执行 * 作用:修改域中的值,用的不多 */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub System.out.println("FirstInterceptor postHandle"); } /** * afterCompletion:在目标handler方法执行之后,在视图渲染(render)之后【给页面填充数据】执行 * 作用:释放资源,用的不多。 */ @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("FirstInterceptor afterCompletion"); } }
在springmvc的配置文件中通过mvc:interceptors标签内部配置该拦截器对象
执行流程图:
多拦截器的执行顺序问题
问题1:拦截器的顺序实际上是由springmvc.xml文件中配置的顺序决定的。
问题2:拦截器也可以只拦截某些指定的请求。具体配置如下
问题3:拦截器中各个方法的执行顺序,是由springmvc框架源码决定的和前面学过的aop有点区别
SpringMVC的运行流程图及源码(***)
解析1:
1用户发送请求给前端控制器(Dispatcherservlet),前端控制器接收到请求后委托给其他的解析器进行处理,进行全局的流程控制 2.需要根据请求url查找处理器,于是前端控制器找到处理器映射器(HandlerMapper),HandlerMapper会把请求映射为HandlerExecutionChain对象HandlerExecutionChain(包含一个handler对象,多个HanndlerInterceptor对象拦截器)返回给前端控制器 3前端控制器拿到handler之后,找到处理器适配器HandlerAdapter,适配器能支持多种类型的处理器,适配器会根据适配的结果找到真正的处理器完成功能处理。处理器会返回ModelAndView对象给HandlerAdapter(包括模型数据和逻辑视图名),通过HandlerAdapter返回ModelAndView对象给前端控制器 4前端控制器请求视图解析器ViewResolver来解析成具体的视图,解析器生成视图对象返回给前端控制器 5.前端控制器把返回的视图对象传给View,View通过传进来的Model模型数据进行渲染 6.返回控制权给前端控制器,前端控制器返回响应给用户,结束
解析2:
用户发起请求到前端控制器(DispatcherServlet),该控制器会过滤出哪些请求可以访问Servlet、哪些不能访问。就是url-pattern的作用,并且会加载springmvc.xml配置文件。 前端控制器会找到处理器映射器(HandlerMapping),通过HandlerMapping完成url到controller映射的组件,简单来说,就是将在springmvc.xml中配置的或者注解的url与对应的处理类找到并进行存储,用map这样的方式来存储。 HandlerMapping有了映射关系,并且找到url对应的处理器,HandlerMapping就会将其处理器(Handler)返回,在返回前,会加上很多拦截器。 DispatcherServlet拿到Handler后,找到HandlerAdapter(处理器适配器),通过它来访问处理器,并执行处理器。 执行处理器 处理器会返回一个ModelAndView对象给HandlerAdapter 通过HandlerAdapter将ModelAndView对象返回给前端控制器(DispatcherServlet) 前端控制器请求视图解析器(ViewResolver)去进行视图解析,根据逻辑视图名解析成真正的视图(jsp),其实就是将ModelAndView对象中存放视图的名称进行查找,找到对应的页面形成视图对象 返回视图对象到前端控制器。 视图渲染,就是将ModelAndView对象中的数据放到request域中,用来让页面加载数据的。 通过第8步,通过名称找到了对应的页面,通过第10步,request域中有了所需要的数据,那么就能够进行视图渲染了。最后将其返回即可。
解析3:
核心架构的具体流程步骤如下: 1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制; 2、 DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略; 3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器; 4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名); 5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术; 6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术; 7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。