Spring MVC 入门学习
一: 依赖
在Spring依赖基础上添加springMVC依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<spring.version>5.2.5 RELEASE</spring.version>
</properties>
<dependencys>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencys>
二: 配置
-
web.xml
<!-- SpringMVC 思想是有一个前端控制器能拦截所有请求,并且智能派发 在web.xml中配置这个前端控制器是 servlet 来拦截所有请求 --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <!-- 配置初始化参数,用于指定并读取 SpringMVC 的配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:SpringMVC.xml</param-value> </init-param> <!-- 配置 servlet 的对象的创建时间点:应用加载时创建。取值只能是非 0 正整数,表示启动顺序,值越小优先级越高 --> <load-on-startup>1</load-on-startup> </servlet> <!-- 拦截处理 请求 --> <servlet-mapping> <!-- /* 和 / 都是拦截所有请求, /:会拦截所有请求,但是不会拦截*.jsp,能保证jsp访问正常 /* 的范围更大,还会拦截到*.jsp这些请求,一旦拦截jsp页面就不能显示 --> <servlet-name>SpringMVCDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
配置Spring MVC编码过滤器
<!-- 配置 springMVC 编码过滤器 --> <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>
在 springmvc 的配置文件中可以配置,静态资源不过滤:
<!-- location 表示路径,mapping 表示文件,**表示该目录下的文件以及子目录的文件 --> <mvc:resources location="/css/" mapping="/css/**"/> <mvc:resources location="/images/" mapping="/images/**"/> <mvc:resources location="/scripts/" mapping="/javascript/**"/>
-
SpringMVC.xml
<!-- 配置创建spring容器要扫描的包 --> <context:component-scan base-package="com.cccq"></context:component-scan> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean>
-
<mvc:annotation-driven/>
在springmvc的上下文配置文件中使用,即会默认注册一个
RequestMappingHandlerMapping
,一个RequestMappingHandlerAdapter
,以及一个ExceptionHandlerExceptionResolver
,以支持对使用了@RequestMapping
,@ExceptionHandler
以及其他注解的控制器方法的请求处理-
同时拥有的特性
- Spring 3风格的类型转换支持。这是使用一个配置的转换服务ConversionService实例,以及the JavaBeans PropertyEditors used for Data Binding.
- 使用@NumberFormat对数字字段进行格式化,类型转换由ConversionService实现
- 使用@DateTimeFormat注解对Date、Calendar、Long及Joda Time类型的字段进行格式化
- 使用@Valid注解对@Controller输入进行验证——前提是classpath路径下比如提供符合JSR-303规范的验证器
- HTTP消息转换HttpMessageConverter的支持,对注解了@RequestMapping或@ExceptionHandler方法的@RequestBody方法参数或@ResponseBody返回值生效
-
相当于如下配置
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <!-- HandlerAdapter --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA dapter"></bean> <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> <!-- HadnlerExceptionResolvers --> <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean> <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver"></bean> <bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"></bean>
-
-
三: 请求参数绑定
表单中的请求参数都是基于 key=value
SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为控制器中方法参数进行绑定的。
SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求
-
请求参数绑定
- 基本类型参数 : 包括基本类型和String类型
使用要求: 要求参数名称必须和控制器中方法的形参名称保持一致(严格区分大小写)
<a href="user/findUser?userId=2">查询用户</a> @RequestMapping("/findUser") public String findUser(Integer userId) { System.out.println("查询用户。。。。"+userId); return "success"; }
- POJO类型参数 : 包括实体类,以及关联的实体类
使用要求: 要求表单中参数名称和POJO类的属性名称保持一直,并且控制器方法的参数类型是POJO类型
<a href="user/findUser?userId=2&userName=cccq">查询用户</a> @RequestMapping("/findUser") public String findUser(User user) { System.out.println("查询用户。。。。"+user.userId+","+user.userName); return "success"; }
- 数组和集合类型参数 : 包括List结构和Map结构的集合(包括数组)
使用要求: 如果是集合类型,有两种方式
1. 要求集合类型的请求参数必须在POJO中,在表单中请求参数名称要和POJO中集合属性名称相同 * 给 List 集合中的元素赋值,使用下标 * 给 Map 集合中的元素赋值,使用键值对 2. 接收的请求参数是 json 格式数据。需要借助一个注解实现
它还可以实现一些数据类型自动转换。内置转换器全都在: org.springframework.core.convert.support 包下。如果需要特俗类型转换要求,则需要自己编写自定义类型转换器
-
自定义类型转换器
-
概述
- ConversionService 是Spring 类型转换体系的核心接口
- 利用ConversionServiceFactoryBean 在 Spring 的IOC容器中定义一个 ConversionService。Spring将自动识别出 IOC 容器中的 ConversionService , 并在 Bean 属性配置及 Spring MVC 处理方法入参绑定等场合使用它进行数据的转换
- 可以通过 ConversionServiceFactoryBean 的 converters 属性注册自定义的类型转换器
-
例:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <!-- class中是自定义的类型转换器 --> <bean class="com.cccq.springmvc.userConverter"></bean> </list> </property> </bean>
-
Spring 支持的转换器类型
Spring 定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactoryBean 中
- Converter<S,T>:将 S 类型对象转为 T 类型对象
- ConverterFactory:将相同系列多个 “同质” Converter 封装在一起。如果希望将一种类型的对象转换为另一种类型及其子类的对象(例如将 String 转换为 Number 及 Number 子类(Integer、Long、Double 等)对象)可使用该转换器工厂类
- GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换
-
自定义转换器步骤
SpringMVC 中需要实现的接口源码
public interface Converter<S, T> {//S:表示接受的类型,T:表示目标类型 /** * 实现类型转换的方法 */ @Nullable T convert(S source); }
-
定义一个类,实现 Converter 接口,该接口有两个泛型
public class userConverter implements Converter<String, Date> { //用于把String类型转换为日期 @Override public Date convert (String source) { DateFormat = null; try { if(StringUtils.isEmpty(source)) { throw new NullPointerException("请输入要转换的日期"); } format = new SimpleDateFormat("yyyy-MM-dd"); Date date = format.parse(source); return date; } catch (Exception e) { throw new RuntimeException("输入日期有误"); } } }
-
在Spring配置文件中配置类型转换器
<!-- 配置类型转换器工厂 --> <bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- 给工厂注入一个新的类型转换器 --> <property name="converters"> <array> <!-- 配置自定义类型转换器 --> <bean class="com.cccq.converter.userConverter"></bean> </array> </property> </bean>
-
在springmvc.xml中定义
<mvc:annotation-driven conversion-service="userConverter"/>
-
在控制器方法中使用
- String --> Date
-
-
四: 常用注解
Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap, 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类
-
@Controller
- 作用: 使用该注解标记的类就是一个SpringMVC Controller对象,分发处理器将会扫描使用了该注解的类的方法
- 位置: 标注于类上
-
@RequestMapping
-
源码:
package org.springframework.web.bind.annotation; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String[] value() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
-
作用: 用于建立请求URL和处理请求方法之间的对应关系
- 位置:
- 类
- 请求URL的第一级访问目录,不写相当于应用的根目录,写则需要以 / 开头
- 主要是为了方便项目url可以按照模块化进行管理
- 方法
- 请求URL的第二级访问目录
- 类
- 属性:
- vlaue: 用于指定请求的URL,它和path属性的作用是一样的
- method: 用于指定请求的方式
- params: 用于指定限制请求参数的条件。它支持简单的表达式,要求请求参数的key和value必须和配置的一模一样
- 例: params = {"username"} --> 表示请求参数必须有 username
- headers: 用于指定限制请求消息头的条件
-
-
@RequestParam
- 作用: 把请求中指定参数名称打的参数给控制器中的形参赋值
- 属性:
- value: 请求参数中的名称
- required: 请求参数中是否必须提供此参数。默认值: true(表示必须提供,若不提供将报错)
-
示例:
@RequestMapping("/findUser") public String findUser(@RequestParam("name")String username, @RequestParam(value="age", required=false)Integer age) { System.out.println("查询用户。。。。"+username+","+age); return "success"; }
-
@RequestBody
- 作用: 用于获取请求体的内容(直接使用是得到 key=value&key=value... 的结构数据)(GET请求方式不适用)
- 属性:
- required: 是否必须有请求体。默认值: true (当取值为true时,get请求方式会报错,如果取值为false,get请求得到的是null)
-
示例:
post: 结果--> username=cccq&age=22 <form action="springmvc/findUser" method="post"> 用户名称:<input type="text" name="username" ><br/> 用户年龄:<input type="text" name="age" ><br/> <input type="submit" value="保存"> </form> get: 结果--> null <a href="springmvc/findUser?body=test">requestBody 注解 get</a> Controller: @RequestMapping("/findUser") public String findUser(@RequestBody(required=false) String body) { System.out.println("查询用户。。。。"+body); return "success"; }
-
@PathVaribale
- 作用: 用于绑定url中的占位符。例: 在请求url中 /delete/{id} --> 该{id}就是url占位符
url支持占位符是spring3.0之后加入的,是springmvc支持rest风格url的一个标志
- 属性:
- value: 用于指定url中占位符名称
- required: 是否必须提供占位符
-
示例:
html: <a href="springmvc/findUser/100">pathVariable 注解</a> Controller: @RequestMapping("/findUser") public String findUser(@PathVariable("id") Integer id) { System.out.println("查询用户。。。。"+id); return "success"; }
- 作用: 用于绑定url中的占位符。例: 在请求url中 /delete/{id} --> 该{id}就是url占位符
-
@RequestHeader
- 作用: 用于获取请求消息头 (用到的不多)
- 属性:
- value: 提供消息头名称
- required: 是否必须有此消息头
-
示例:
html: <a href="springmvc/useRequestHeader">获取请求消息头</a> Controller: @RequestMapping("/useRequestHeader") public String findUser(@RequestHeader(value="Accepy-Language",required=false) String requestHeader) { System.out.println("请求消息头。。。。"+requestHeader); return "success"; }
-
@CookieValue
- 作用: 用于把指定cookie名称的值传入控制器方法参数
- 属性:
- value: 指定cookie的名称
- required: 是否必须有此cookie
-
示例:
@RequestMapping("/userCookieValue") public String findUser(@CookieValue(value="JSESSIONID",required=false)String cookieValue) { System.out.println("获取指定CookieValue。。。。"+cookieValue); return "success"; }
-
@ModelAttribute
- 作用: 该注解是SpringMVC4.3版本以后新加入的,它可以用于修饰方法和参数
- 出现在方法上,表示当前方法会在控制器的方法执行之前先执行,它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法上
- 出现在参数上,获取指定的数据给参数赋值
- 属性:
- value: 用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key
-
场景举例: 当表单提交数据不是完整实体类数据时,保证没有提交的数据字段使用数据库对象原来的数据
-
示例:
基于POJO属性: html: <a href="springmvc/testModelAttribute?username=test">测试 modelattribute</a> Controller: @ModelAtterbute public void showModel(User user) { System.out.println("提前执行的方法。。。。"+user.getUsername()); } @RequestMapping("/useRequestHeader") public String findUser(User user) { System.out.println("查询用户。。。。"+user.getUsername()); return "success"; } 基于Map,ModelAttribute带返回值 html: <form action="springmvc/updateUser" method="post"> 用户名称:<input type="text" name="username" ><br/> 用户年龄:<input type="text" name="age" ><br/> <input type="submit" value="保存"> </form> Controller: @ModelAtterbute public User showModel(String username) { User user = findUserByName(username); System.out.println("提前执行的方法。。。。"+user); return user; } @RequestMapping("/updateUser") public String findUser(User user) { System.out.println("修改用户。。。。"+user); return "success"; } private User findUserByName(String username) { User user = new User(); user.setUsername(username); user.setAge(19); return user; } 基于Map,ModelAttribute不带返回值 html: <form action="springmvc/updateUser" method="post"> 用户名称:<input type="text" name="username" ><br/> 用户年龄:<input type="text" name="age" ><br/> <input type="submit" value="保存"> </form> Controller: @ModelAtterbute public void showModel(String username, Map<String, User> map) { User user = findUserByName(username); System.out.println("提前执行的方法。。。。"+user); map.put("cccq",user); } @RequestMapping("/updateUser") public String findUser(@ModelAttribute("cccq")User user) { System.out.println("修改用户。。。。"+user); return "success"; } private User findUserByName(String username) { User user = new User(); user.setUsername(username); user.setAge(19); return user; }
- 作用: 该注解是SpringMVC4.3版本以后新加入的,它可以用于修饰方法和参数
-
@SessionAttribute
- 作用: 用于多次执行控制器方法间的参数共享
- 属性:
- value: 用于指定存入的属性名称
- type: 用于指定存入的数据类型
-
示例:
html: <a href="springmvc/testPut">存入 SessionAttribute</a> <br/> <a href="springmvc/testGet">取出 SessionAttribute</a> <br/> <a href="springmvc/testClean">清除 SessionAttribute</a> Controller: @Controller @RequestMapping("/springmvc") @SessionAttributes(value ={"username","password"},types={Integer.class}) public class SessionAttributeController { // 把数据存入 SessionAttribute @RequestMapping("/testPut") public String testPut(Model model){ model.addAttribute("username", "泰斯特"); model.addAttribute("password","123456"); model.addAttribute("age", 31); //跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有 这几个参数 return "success"; } @RequestMapping("/testGet") public String testGet(ModelMap model){ System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("a ge")); return "success"; } @RequestMapping("/testClean") public String complete(SessionStatus sessionStatus){ sessionStatus.setComplete(); return "success"; } }
Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap, 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类
五: 响应数据和结果视图解析
无论控制器返回一个String, MModelAndView都会转换为ModelAndView对象,由视图解析器解析视图,然后进行页面的跳转
( String | ModelAndview | View ) --> ModelAndView --> (ViewRedolver) --> (视图对象)
- 两个重要的接口
- org.springframework.web.servlet.View
- org.springframework.web.servlet.ViewResolver
视图解析器 * 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String ,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView
对象,它包含了逻辑名和模型对象的视图。 * Spring MVC 借助视图解析器(ModelAndView)
得到最终的视图对象(View)
。 * 处理器只关心于生产模型数据的工作,不关心最终视图对象对数据模型的渲染
-
返回值分类
-
字符串
- Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址
-
void
- 如果控制器的方法返回值为 void ,执行程序会报404的异常,默认查找页面没有找到
- 默认会跳转到@RequestMapping(value="/initPage") initPage的页面
- 可以使用请求转发或者重定向跳转到指定的页面
- 如果控制器的方法返回值为 void ,执行程序会报404的异常,默认查找页面没有找到
-
ModelAndView
- ModelAndView对象是Spring提供的一个对象,可以用来调整具体的视图,既包含视图,也包含模型数据信息
-
示例:
@RequestMapping public ModelAndView find User(@RequestParam("id")Integer id) { ModelAndView mv = new ModelAndView(); mv.setViewName("userPage"); User user = getUserById(id); mv.addObject("user",user); return mv; }
-
-
Spring MVC 提供的转发和重定向
-
forward请求转发
-
Controller方法返回String类型,需要请求转发可以写成
@RequestMapping("/delete") public String delete() throws Exception { System.out.println("delete方法执行了..."); return "forward:/user/findAll"; }
-
-
redirect重定向
-
Controller方法返回String类型,需要重定向可以写成
@RequestMapping("/count") public String count() throws Exception { System.out.println("count方法执行了..."); return "redirect:/add.html"; }
-
-
-
自定义视图(前缀)
- 自定义视图解析器
- 在springmvc配置文件中配置视图解析器
-
视图解析器用到的视图对象
Controller:
@RequestMapping("allUser") public String allUser(Model model) { List<String> user = new ArrayList<String>(); user.add("cccq"); model.addAttribute("user",user); return "user:/success"; }
自定义视图解析器类:
-
必须实现接口 :
-
org.springframework.web.servlet.ViewResolver;
public class MyUserViewResolver implements ViewResolver, Ordered { private Integer order = 0; @Override public View resolveViewName(String viewName, Locale locale) throws Exception { //根据视图名返回视图对象 if(viewName.startWith("user")) { return new MyView(); } else { return null; } } @Override public int getOrder() { return order; } @Override public void setOrder() { this.order=order; } }
-
自定义返回的视图:
-
必须实现接口 : org.springframework.web.servlet.View;
public class MyView implements View { //返回的数据的内容类型 @Override public String getContentType() { return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { System.pout.println("在Controller中方法保存的数据:"+model); response.setContentType("text/html"); List<User> lists = (List<String>) model.get("user"); for(String user : lists) { response.getWirte().write(user); } } }
在springmvc.xml配置文件中配置视图解析器:
<!-- 自定义的视图解析器 order的value数字越小,优先级越高 --> <bean class="com.cccq.view.MyUserViewResolver"> <property name="order" value="1"></property> </bean>
-
-
@ResponseBody响应json数据
-
DispatcherServlet会拦截所有的资源,导致静态资源 (img,css,js) 也会被拦截到,从而不能被使用,此时需要配置静态资源不进行拦截,在springmvc.xml中进行配置
<!-- 设置静态资源不过滤 --> <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 --> <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 --> <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
-
使用@RequestBody获取请求体数据
html,js: $(function(){ // 绑定点击事件 $("#btn").click(function(){ $.ajax({ url:"user/testJson", contentType:"application/json;charset=UTF-8", data:'{"addressName":"aa","addressNum":100}', dataType:"json", type:"post", success:function(data){ alert(data); alert(data.addressName); } }); }); }); Controller: @RequestMapping("/testJson") public void testJson(@RequestBody String body) { System.out.println(body); }
-
使用@RequestBody注解把json的字符串转换成JavaBean的对象
html,js:同上 Controller: @RequestMapping("/testJson") public void testJson(@RequestBody Address address) { System.out.println(address); }
-
使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应
-
要求方法返回JavaBean的对象
html,js:同上 Controller: @RequestMapping("/testJson") public @ResponseBody Address testJson(@RequestBody Address address) { System.out.println(address); address.setAddressName("上海"); return address; }
-
-
json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
<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-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency>
-