静态资源处理
- 明明代码没问题, 为什么静态资源如jQuery库文件起不了作用呢?
- 原因如下
-
优雅的REST风格的资源URL不希望带 .html或 .do等后缀, 若将DispatcherServlet请求映射配置为 /, 则SpringMVC将捕获WEB容器的所有请求, 包括静态资源的请求, SpringMVC会将他们当成一个普通请求处理, 因找不到对应处理器将导致错误.
-
- 解决办法就是在springMVC的配置文件中加
- <mvc:default-servlet-handler/>
- <mvc:annotation-driven/>
-
<mvc:default-servlet-handler />
-
Tomcat会配置其默认的servlet, DefaultServlet
-
注意: DefaultServlet所设置的<url-pattern>的值和开发人员所配置的servlt<url-pattern>相同, 以自己所配置的servlet优先.
-
- 作用
-
当客户端发送请求, 由于DefaultServlet所设置的<url-pattern>的值和开发人员所配置的DispatcherServlet<url-pattern>都是/, 因此先通过DispatcherServlet处理请求, 找该请求是否有相应的处理器, 有则处理, 无则交给DefaultServlet处理.
-
-
这里要求WEB应用服务器默认的Servlet名称为"default"
- 若不是, 需要手动指定: <mvc:default-servlet-handler default-servlet-name="default"/>
-
<mvc:annotation-driven />
- 当配置了mvc:annotation-driven/后, Spring就知道了我们启用注解驱动. 然后Spring通过context:component-scan/标签的配置,会自动为我们将扫描到的@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来处理我们的请求.
SpringMVC处理JSON
- 先回顾一下Json
-
Json是作为一种数据交互格式存在的.
-
与xml相比的优点
-
描述相同数据, xml代码量太大.
-
解析代码上, Json也比xml容易.
-
-
Json有两种格式
- Json对象: {key:value, key:value...}
- Json数组: [value1, value2...]
- Json对象解析方式: 对象.key;
-
Json数组的解析方式: for循环遍历
- Java对象转换Json
-
bean和map -> Json对象
- List -> Json数组
-
- SpringMVC处理Json
-
使用jackson, 可以将Java对象自动转为Json对象.
- 四步
- 引入如下依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency>
- 在springMVC的配置文件中开启mvc驱动.
- <mvc:annotation-driven />
- 在处理ajax请求的方法上加注解@ResponseBody.
- 将要转换为json且响应到客户端的数据, 直接作为该方法的返回值返回.
- 引入如下依赖
- 示例
- index.html
<a href="testJson">测试Json</a>
- TestJsonController.java
@Controller public class TestJsonController { @ResponseBody @RequestMapping("/testJson") public Collection<String> testJson() { Collection<String> all = new ArrayList<>(); all.add("AAA"); all.add("BBB"); all.add("CCC"); return all; } }
- index.html
- @ResponseBody
-
@ResponseBody的作用其实是将java对象转为json格式的数据.
-
加上该注解之后, 就不会实现跳转了, 而是作为响应体, 将数据响应到客户端.
-
文件的下载
-
HttpMessageConverter
-
HttpMessageConverter<T>是Spring3.0新添加的一个接口, 负责将请求信息转换为一个对象, 并将对象输出为响应信息.
-
HttpMessageConverter<T>接口定义的方法
- Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)
- Boolean canWrite(Class<?> clazz,MediaType mediaType): 指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义.
- List<MediaType> getSupportMediaTypes(): 该转换器支持的媒体类型.
- T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为T类型的对象.
- void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage): 将T类型的对象写到响应流中, 同时指定相应的媒体类型为contentType.
- 流程图解
- HttpInputMessage
public interface HttpInputMessage extends HttpMessage { /** * Return the body of the message as an input stream. * @return the input stream body (never {@code null}) * @throws IOException in case of I/O errors */ InputStream getBody() throws IOException; }
- HttpOutputMessage
public interface HttpOutputMessage extends HttpMessage { /** * Return the body of the message as an output stream. * @return the output stream body (never {@code null}) * @throws IOException in case of I/O errors */ OutputStream getBody() throws IOException; }
- HttpInputMessage
- HttpMessageConverter的实现类
-
DispatcherServlet默认装配了RequestMappingHandlerAdapter, 而RequestMappingHandlerAdapter默认装配了HttpMessageConverter.(加jackson包)
-
-
使用HttpMessageConverter
-
使用HttpMessageConverter<T>将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息, 有两种途径
-
使用@RequestBody/@ResponseBody对处理方法进行标注.
-
使用HttpEntity<T>/ResponseEntity<T>作为处理方法的入参或返回值.
-
-
当控制器处理方法使用到这两种途径时, Spring首先根据请求头或响应头的Accept属性选择匹配的HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的HttpMessageConverter, 若找不到可用的则报错.
- ResponseEntity<T>实现文件的下载.
- file.html
<a href="down">下载图片</a>
- TestController.java
@GetMapping("/down") public ResponseEntity<byte[]> down(HttpSession session) throws IOException { //获取下载文件的路径 //getRealPath()里什么也不填, 获取项目路径, 填写内容则获取项目下文件夹路径. String realPath = session.getServletContext().getRealPath("img"); String finalPath = realPath + File.separator + "1.jpg"; InputStream is = new FileInputStream(finalPath); //available()获取输入流所读取的文件的最大字节数 byte[] b = new byte[is.available()]; //把字节数字加载进输入流. is.read(b); //设置请求头 HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment;filename=zzz.jpg"); //设置响应状态 HttpStatus statusCode = HttpStatus.OK; ResponseEntity<byte[]> entity = new ResponseEntity<>(b, headers, statusCode); return entity; }
-
Content-Disposition: attachment; filename=abc.jpg
- Content-Disposition: 是属性名
- attachment表示以附件形式下载.
- file.html
文件的上传
-
文件上传
-
SpringMVC为文件上传提供了直接的支持, 这种支持是通过即插即用的MultipartResolver实现的.
-
Spring用Jakarta Commons FileUpload技术实现了一个 MultipartResolver实现类: CommonsMultipartResolver
-
SpringMVC上下文中默认没有装配MultipartResovler, 因此默认情况下不能处理文件的上传工作, 如 果想使用Spring的文件上传功能, 需在上下文中配置MultipartResolver
-
配置文件上传解析器: MultipartResolver
- 导入依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
- 作用: 处理文件, 将客户端上传的File文件, 处理为MultipartFile.
<!-- id只能为multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置文件解析的编码, 一定要和页面的pageEncoding一致 --> <property name="defaultEncoding" value="UTF-8"></property> <!-- 设置最大上传文件大小 --> <property name="maxUploadSize" value="88888"></property> </bean>
- 导入依赖
- 上传页面
<form action="up" method="post" enctype="multipart/form-data"> 头像: <input type="file" name="uploadFile"> 描述: <input typee="text" name="desc"> <input type="submit" value="上传"> </form>
-
enctype="multipart/form-data"
-
enctype: 编码类型
-
multipart/form-data: 指表单数据有多部分构成, 既有文本数据, 又有文件等二进制数据的意思.
-
只有multipart/form-data类型才能实现文件上传.
-
-
- 控制器方法
@PostMapping("up") public String up_new(String desc, MultipartFile uploadFile, HttpSession session) throws IOException { //获取上传文件名称 String filename = uploadFile.getOriginalFilename(); String finalFileName = UUID.randomUUID() + filename.substring(filename.lastIndexOf(".")); String path = session.getServletContext().getRealPath("photo") + File.separator + finalFileName; File file = new File(path); //把uploadFile中的内容转换到file中, 即上传. uploadFile.transferTo(file); return "success"; }
拦截器
- 概述
-
SpringMVC也可以使用拦截器对请求进行拦截处理, 用户可以自定义拦截器来实现特定的功能, 自定义的拦截器可以实现HandlerInterceptor接口, 也可以继承HandlerInterceptorAdapter适配器类.
- preHandle()
-
这个方法在业务处理器处理请求之前被调用, 在该方法中对用户请求request进行处理. 如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器, 或者是业务处理器去进行处理, 则返回true, 如果程序员决定不需要再调用其他的组件去处理请求, 则返回false.
-
- postHandle()
-
这个方法在业务处理器处理完请求后, 但是DispatcherServlet向客户端返回响应前被调用, 在该方法中对用户请求request进行处理.
-
- afterCompletion()
-
这个方法在DispatcherServlet完全处理完请求后被调用.
-
可以在该方法中进行一些资源清理的操作.
-
- preHandle()
- 拦截器和过滤器的区别
- 单个拦截器
- 先配置拦截器
- 默认拦截所有请求
- 方式一
<mvc:interceptors> <!-- 方式一 --> <bean class="test.interceptor.FirstInterceptor"></bean> </mvc:interceptors>
- 方式二
<mvc:interceptors> <!-- 方式二: 必须在拦截器上加@Component注解 --> <ref bean="firstInterceptor"/> </mvc:interceptors>
- 方式一
- 设置自定义拦截方式
<mvc:interceptors> <ref bean="firstInterceptor"/> <mvc:interceptor> <mvc:mapping path=""/> <!-- <mvc:exclude-mapping path=""/> --> </mvc:interceptor> </mvc:interceptors>
- 默认拦截所有请求
- 写拦截器
@Component public class FirstInterceptor implements HandlerInterceptor { /** * 返回true代表放行, 返回false代表拦截. */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("First: preHandle"); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("First: postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("First: afterCompletion"); } }
- 多个拦截器执行顺序
- 当有多个拦截器时
-
preHandle: 按照拦截器数组的正向顺序执行.
-
postHandle: 按照拦截器数组的反向顺序执行.
-
afterCompletion: 按照拦截器数组的反向顺序执行.
-
- 当有多个拦截器的preHandle有不同值时
-
第一个返回false, 第二个返回false, 只有第一个的preHandle会执行.
-
第一个返回true, 第二个返回false, 两个(全部)拦截器的preHandle都会执行
-
但是(全部)postHandle都不会执行, 而afterCompletion只有第一个(返回false的拦截器之前的所有afterCompletion)会执行.
-
-
第一个返回false, 第二个返回true, 只有第一个的preHandle会执行.
-