Spring-MVC
一、MVC 概述
mvc是软件开发中非常常见的分层设计,目的是使程序中的代码根据负责的功能分离,便于维护,当你更新视图时,不用修改其他代码。
M: Model -- JavaBean 模型 V: View -- 视图 C: Controller 控制器
二、SpringMVC
Springmvc 是spring 框架的一个模块,用于构建web应用程序,Springmvc是符合mvc设计的一个框架。
三、入门案例
-
准备依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.14.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
-
配置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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 应用程序前端控制器 --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
编写spring-mvc配置文件 ( spring-mvc.xml )
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 扫描控制器所在包 --> <context:component-scan base-package="com.xingxue.mvc.controller"/> <!-- 开启注解驱动 --> <mvc:annotation-driven/> <!-- 对于静态资源 请求交给容器的默认servlet处理 --> <mvc:default-servlet-handler/> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <!-- --> <property name="suffix" value=".jsp"/> </bean> </beans>
-
编写控制器
package com.xingxue.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/test/") public class HelloController { @RequestMapping("/hello") public String test1( ) { return "hello"; } }
-
测试
四、请求映射( @RequestMapping )
1、基本请求映射
@RequestMapping("/test/") public class HelloController { @RequestMapping("/hello") public String test1( ) { return "hello"; } }
2、精准请求映射 ( value method param header 映射 Http消息 )
@RequestMapping(value="/emp",method=RequestMethod.GET) public void getEmp() { System.out.println("get one resource"+id); } @RequestMapping(value="/emps",method=RequestMethod.GET) public void getEmps() { System.out.println("get all resource"); } @RequestMapping(value="/emp",method=RequestMethod.POST) public void save() { System.out.println("add one resource"); } @RequestMapping(value="/emp",method=RequestMethod.DELETE) public void delete() { System.out.println("delete one resource"); } @RequestMapping(value="/emp",method=RequestMethod.PUT) public void update() { System.out.println("update one resource"); }
3、ant请求映射
@RequestMapping(value="/user/?bc") public void abc() { System.out.println("abc"); } @RequestMapping(value="/user/*d") public void aaa() { System.out.println("aaa"); } @RequestMapping(value="/user/**/sb") public void bbb() { System.out.println("bbb"); } @RequestMapping(value="/user/**/?mvc*") public void ccc() { System.out.println("ccc"); }
Ant 资源地址支持3种统配符
? :资源名字中一个任意字符
* : 资源名字中的任意个字符
** : 匹配多层路径
4、@PathVariable 注解
通过 @PathVariable 可以将 URL 中占位符参数绑定到控 制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过
@PathVariable("xxx") 绑定到操作方法的入参中。
@RequestMapping(value="/emp/{id}",method=RequestMethod.GET) public void getEmp(@PathVariable("id") Integer id) { System.out.println("get one resource"+id); }
五、RestFul风格
REST:即 Representational State Transfer。(资源)表现层状态转化。是目前 最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用
• 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它 可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。 可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要 获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识 别符。 • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格 式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。 • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一 次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器 端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“ 状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “ 表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动 词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获 取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
API 示例:
URL | HTTP Method | 作用 |
---|---|---|
–/order/1 | GET | :得到 id = 1 的 order |
–/order/1 | PUT | :更新id = 1的 order |
–/order | POST | :新增 order |
–/order/1 | DELETE | :删除 id = 1的 order |
-/orders/1 | GET | : 1号用户 全部订单 |
SpringMVC对RestFul支持
HiddenHttpMethodFilter:浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支 持,Spring3.0 添加了一个过滤器,可以将这些请求转换 为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。
配置 HiddenHttpMethodFilter
<!-- HiddenHttpMethodFilter: 支持restful 风格请求 --> <filter> <filter-name>RestFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>RestFilter</filter-name> <servlet-name>SpringMVC</servlet-name> </filter-mapping>
六、获取请求数据
1、简单数据类型
@RequestMapping("t1") public void test1(String name,int age ) { System.out.println("you input is "+name+ "\t" + age ); }
测试:http://localhost;8080/spring-web/params/t1?name=admin&age=11
2、pojo数据类型(支持级联属性注入)
@RequestMapping("t2") public void test2(User u) { System.out.println(u); } ---------------------------------------------------------------------------------------------- public class User { private String username; private int age; private Company company; //省略get set } public class Company{ private String cname; private String address; private String phone; //省略get set方法 }
测试:http://localhost;8080/spring-web/params/t2?username=admin&age=11&company.cname=baidu&company.address=beijing&company.phone=010-666666
3、@RequestParam
(此注解目的是 把请求参数绑定到入参 主要解决前后参数名字不一致的情况)
@RequestMapping("t3") public void test3(@RequestParam(value="fullname",required=true)String name,@RequestParam(value="phone")String tel ) { System.out.println("名字:"+name); System.out.println("电话:"+tel); }
测试:http://localhost:8080/spring-mvc/params/t3?fullname=kangkang&phone=188xxxxxx
4、@RequestHeader(获得请求头信息) @CookieValue(获得Cookie信息)
@RequestMapping("t4") public void test4(@RequestHeader(value="connection",required=false)String header, @CookieValue(value="JSESSIONID",required=false) String sessid ) { System.out.println("Connection:"+header); System.out.println("sessionid:"+sessid); }
5、使用Servlet API作为入参
@RequestMapping("t5") public void test5(HttpServletRequest req,HttpServletResponse resp, HttpSession session) throws IOException { String n=req.getParameter("name"); resp.getWriter().write(n); resp.getWriter().flush(); }
以下是方法支持的 类型
•HttpServletRequest
•HttpServletResponse
•HttpSession
•java.security.Principal
•Locale
•InputStream
•OutputStream
•Reader
•Writer
总结:SpringMVC控制器方法入参非常灵活
七、页面渲染
-
ModelAndView
直接翻译为 模型和数据,其作用是保存模型和数据,最终目的是把模型数据( Model )带到视图(View)中展现
@RequestMapping("t6") public ModelAndView t6() { ModelAndView mv = new ModelAndView(); mv.getModel().put("company", "baidu"); mv.getModel().put("phone", "010-666666"); mv.setViewName("hello"); return mv; }
-
Model
直接翻译为 模型,其作用是提供数据,但是也仅仅提供数据,不参与跳转视图,跳转视图需要 return 确定,此Model不需要开发者手动创建,只需要声明在方法的入参中即可,它由框架创建和注入以及发送到视图
@RequestMapping("t7") public String t7( Model md ) { md.addAttribute("company", "tengxun"); md.addAttribute("phone", "010=888888"); return "hello"; }
-
ModelMap Map
其作用和使用方法 与Model一致
@RequestMapping("t8") public String t8(ModelMap mp) { mp.addAttribute("company", "alibaba"); mp.addAttribute("phone", "010-999999"); return "hello"; } @RequestMapping("t9") public String t9(Map<String,Object> mp) { mp.put("company", "oralce"); mp.put("phone", "010-555555"); return "hello"; }
测试效果: ![1535214218514](C:\Users\ADMINI~1\AppData\Local\Temp\1535214218514.png)
八、转发和重定向
区别:转发一次请求,浏览器上地址不变 重定向两次请求,浏览器地址改变
/*重定向到jsp*/ @RequestMapping("/t10") public String t10(){ return "redirect:/static/jsp/add.jsp"; } /*转发向到jsp*/ @RequestMapping("/t10") public String t10(){ return "forward:/static/jsp/add.jsp"; } /*重定向到controller*/ @RequestMapping("/t11") public String t11(){ return "redirect:/param/t9"; } /*转发到控制器controller*/ @RequestMapping("/t12") public String t12( ){ return "forward:/param/t8"; }
SpringMVC 默认采取的方式是转发
重定向: redirect:/xxx
转发:forward:/xxx
九、处理JSON数据
当前端请求方法后不需要跳转页面,而只是需要json数据,这时可以使用@ResponseBody注解,该注解的作用是把控制器方法的返回值 通过消息转换器 HttpMessageConverter 自动把方法的返回值 添加 到HttpResponse 响应消息的body中。为此我们需要添加 json依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.7</version> </dependency>
还需要开启注解驱动 ,
<mvc:annotation-driven/>
•<mvc:annotation-driven /> 会自动注 册RequestMappingHandlerMapping
、RequestMappingHandlerAdapter 与
ExceptionHandlerExceptionResolver 三个bean。
• 还将提供以下支持:
–支持使用 ConversionService 实例对表单参数进行类型转换
–支持使用 @NumberFormat annotation、@DateTimeFormat
注解完成数据类型的格式化
– 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
–支持使用 @RequestBody 和 @ResponseBody 注解
@RequestMapping("/json") @ResponseBody() public User t11() { User user = new User(); user.setUsername("admin"); user.setAge(10); Address addr= new Address(); addr.setXian("奉节县"); addr.setXiang("石岗乡"); addr.setSheng("重庆市"); user.setAddr(addr ); return user; }
{"username":"admin","age":10,"addr":{"sheng":"重庆市","xian":"奉节县","xiang":"石岗乡"}}
十、文件上传
依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
①准备上传html页面
<form action="/file/upload" method="post" enctype="multipart/form-data"> <input name="file" type="file"> <input type="submit" value="上传" /> </form>
②准备控制器接受文件
@Controller @RequestMapping("/file") public class FileController { @RequestMapping("/upload") public String fileUpoad(MultipartFile file, HttpServletRequest request){ System.out.println(file); String path=request.getServletContext().getRealPath("/")+"upload\\"; File f= new File(path); if(!f.exists()){ f.mkdirs(); } try { //保存文件方法 /d:/abc/mvc/upload/abc.png file.transferTo( new File(f, file.getOriginalFilename() ) ); } catch (IOException e) { return "error"; } System.out.println("ok..................."); return "success"; } }
③sping配置
<!--文件上传配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"/> <property name="maxUploadSize" value="104857600"/> </bean>
十 一、拦截器
1 、什么是拦截器?
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2、实现拦截器
①编写拦截器代码
以实现HandlerInterceptor接口方式为例,自定义拦截器类的代码如下:
public class CustomInterceptor implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception { System.out.println("开始拦截...."); return false; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("结束拦截...."); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("整个拦截过程完成"); } }
preHandle() 方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
② 注册和配置拦截器
<!--配置拦截器--> <mvc:interceptors> <!-- 直接注册 interceptors 下的拦截器为全局的拦截器 --> <!--<bean class="com.ma.interceptor.CustomeInterceptor" />--> <!--拦截器1--> <mvc:interceptor> <!--配置拦截器的作用路径--> <mvc:mapping path="/**"/> <mvc:exclude-mapping path=""/> <!--定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截--> <bean class="com.ma.interceptor.Intercptor1"/> </mvc:interceptor> <!--拦截器2--> <mvc:interceptor> <mvc:mapping path="/hello"/> <bean class="com.ma.interceptor.Interceptor2"/> </mvc:interceptor> </mvc:interceptors>
上面的代码中,mvc:interceptors元素用于配置一组拦截器,基子元素<bean>中定义的是全局拦截器,它会拦截所有的请求;而mvc:interceptor元素中定义的是指定路径的拦截器,它会对指定路径下的请求生效。mvc:interceptor元素的子元素mvc:mapping用于配置拦截器作用的路径,该路径在其属性path 中定义。如上述代码中 path 的属性值“/**” 表示拦截所有路径,“/hello” 表示拦截所有以 “/hello” 结尾的路径。如果在请求路径中包含不需要拦截的内容,还可以通过mvc:exclude-mapping元素进行配置。
③ 测试
3、拦截器 执行流程
请求 --> preHandle(前置处理) --> target(控制器方法) --> postHandle(后置处理) --> dispatcherServlet(渲染页面) --> afterCompletion(结束处理)
十二、异常处理
在实际开发过程中,我们通常会把应用后端分为3层来开发,Controller(控制器) Service(服务层) Dao(数据访问层),这3层都可能会有异常的发生,通常的做法是把异常,由 DAO 抛到 Service 再抛到 Controller,最后再控制其中处理异常。这样可以把异常反馈給最顶层调用者处理。但是一个系统中有若干个Controller ,若干个方法,处理异常就变得十分难受了,Springmvc提供了一种全局异常处理机制,允许我们在一个类中集中处理异常,控制器不需理会异常。
实现异常处理方式一:
@ExceptionHandler({IOException.class,NullPointerException.class } ) public String excpetionHandle(){ return "error"; }
注解中的异常类型可以给 单个 或 多个
这种处理方式,比较局限,只能处理当前 Controller中的异常
实现全局异常处理方式一:
①实现 HandlerExceptionResolver 接口
//确保 AppGolablException 实例加入IOC容器中 可以用xml配置和注解配置 @Component public class AppGolablException implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //这里无论什么异常都到 error 页面去 //当然这里可以根据实际的异常类型做 不同的响应 ModelAndView mv = new ModelAndView("error"); return mv; } }
②抛出异常
@RequestMapping("/ex") public String testex(){ throw new NullPointerException("hehe"); } @RequestMapping("/ex2") public String textex2() throws Exception{ throw new IOException("haha"); }
这时请求 ex 和 ex2 方法都会跳转到 error页面
实现全局异常处理方式二: @ControllerAdvice + @ExceptionHandler
①编写异常处理代码
@ControllerAdvice public class AppException { @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) @ResponseBody public Object handleHttpMessageNotReadableException(Exception e) { System.out.println("------------"); return "50X"; } }
②控制器抛出异常
@RequestMapping("/ex") public String testex(){ throw new NullPointerException("hehe"); } @RequestMapping("/ex2") public String textex2() throws Exception{ throw new IOException("haha"); }