步骤:
spring-aop-4.0.0.RELEASE.jar spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jar commons-logging-1.1.3.jar spring-web-4.0.0.RELEASE.jar spring-webmvc-4.0.0.RELEASE.jar |
|
解释配置文件的名称定义规则:
实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.默认的配置文件为: /WEB-INF/
增加配置
class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
package com.atguigu.springmvc.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller //声明Bean对象,为一个控制器组件 public class HelloWorldController {
/** * 映射请求的名称:用于客户端请求;类似Struts2中action映射配置的action名称 * 1. 使用 @RequestMapping 注解来映射请求的 URL * 2. 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图解析器, * 会做如下的解析: * 通过 prefix + returnVal + suffix 这样的方式得到实际的物理视图, 然后做转发操作. * /WEB-INF/views/success.jsp */ @RequestMapping(value="/helloworld",method=RequestMethod.GET) public String helloworld(){ System.out.println("hello,world"); return "success"; //结果如何跳转呢?需要配置映射解析器 } } |
/WEB-INF/views/success.jsp
Sucess Page |
http://localhost:8080/SpringMVC_01_HelloWorld/index.jsp
@RequestMapping(value="/helloworld",method=RequestMethod.GET) public String helloworld(){ //public String abc123(){ System.out.println("hello,world"); return "success"; } |
public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE } |
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping {…} |
视图负责将结果显示到客户端
作用:DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。
@Controller //声明Bean对象,为一个控制器组件 @RequestMapping("/springmvc") public class HelloWorldController { /** * 映射请求的名称:用于客户端请求;类似Struts2中action映射配置的,action名称 *1 使用@RequestMapping 注解来映射请求的 URL *2 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图解析器, * 会做如下的解析: * 通过 prefix + returnVal + 后缀 这样的方式得到实际的物理视图, 然会做转发操作. * /WEB-INF/views/success.jsp */ @RequestMapping(value="/helloworld") public String helloworld(){ System.out.println("hello,world"); return "success"; //结果如何跳转呢?需要配置映射解析器 } } |
标准的 HTTP 请求报头
@RequestMapping 除了可以使用请求 URL 映射请求外,还可以使用请求方法、请求参数及请求头映射请求
@RequestMapping 的 value【重点】、method【重点】、params【了解】 及 heads【了解】
分别表示请求 URL、请求方法、请求参数及请求头的映射条件,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化。
param1: 表示请求必须包含名为 param1 的请求参数
!param1: 表示请求不能包含名为 param1 的请求参数
param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
{"param1=value1", "param2"}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
@Controller @RequestMapping("/springmvc") public class SpringMVCController { @RequestMapping(value="/testMethord",method=RequestMethod.POST) public String testMethord(){ System.out.println("testMethord..."); return "success"; } } |
//了解: 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式. @RequestMapping(value="/testParamsAndHeaders", params= {"username","age!=10"}, headers = { "Accept-Language=en-US,zh;q=0.8" }) public String testParamsAndHeaders(){ System.out.println("testParamsAndHeaders..."); return "success"; } |
|
四月 11, 2016 4:48:08 下午 org.springframework.web.servlet.PageNotFound handleNoSuchRequestHandlingMethod 警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map[[empty]] |
四月 11, 2016 4:51:01 下午 org.springframework.web.servlet.PageNotFound handleNoSuchRequestHandlingMethod 警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map['username' -> array |
四月 11, 2016 5:05:24 下午 org.springframework.web.servlet.PageNotFound handleNoSuchRequestHandlingMethod 警告: No matching handler method found for servlet request: path '/springmvc/testParamsAndHeaders', method 'GET', parameters map['age' -> array |
?:匹配文件名中的一个字符
*:匹配文件名中的任意字符
**:** 匹配多层路径
/user/*/createUser 匹配 /user/aaa/createUser、/user/bbb/createUser 等 URL /user/**/createUser 匹配 /user/createUser、/user/aaa/bbb/createUser 等 URL /user/createUser?? 匹配 /user/createUseraa、/user/createUserbb 等 URL |
//Ant 风格资源地址支持 3 种匹配符 //@RequestMapping(value="/testAntPath/*/abc") //@RequestMapping(value="/testAntPath/**/abc") @RequestMapping(value="/testAntPath/abc??") public String testAntPath(){ System.out.println("testAntPath..."); return "success"; } |
@PathVariable 映射 URL 绑定的占位符
带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义
通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:
URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。
//@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中 @RequestMapping(value="/testPathVariable/{id}") public String testPathVariable(@PathVariable("id") Integer id){ System.out.println("testPathVariable...id="+id); return "success"; } |
它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。
可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。
获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
示例:
/order/1 HTTP GET :得到 id = 1 的 order
/order/1 HTTP DELETE:删除 id = 1的 order
/order/1 HTTP PUT:更新id = 1的 order
/order HTTP POST:新增 order
浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,
Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。
|
/** * 1.测试REST风格的 GET,POST,PUT,DELETE 操作 * 以CRUD为例: * 新增: /order POST * 修改: /order/1 PUT update?id=1 * 获取: /order/1 GET get?id=1 * 删除: /order/1 DELETE delete?id=1
* 2.如何发送PUT请求或DELETE请求? * ①.配置HiddenHttpMethodFilter * ②.需要发送POST请求 * ③.需要在发送POST请求时携带一个 name="_method"的隐含域,值为PUT或DELETE
* 3.在SpringMVC的目标方法中如何得到id值呢? * 使用@PathVariable注解 */ @RequestMapping(value="/testRESTGet/{id}",method=RequestMethod.GET) public String testRESTGet(@PathVariable(value="id") Integer id){ System.out.println("testRESTGet id="+id); return "success"; }
@RequestMapping(value="/testRESTPost",method=RequestMethod.POST) public String testRESTPost(){ System.out.println("testRESTPost"); return "success"; }
@RequestMapping(value="/testRESTPut/{id}",method=RequestMethod.PUT) public String testRESTPut(@PathVariable("id") Integer id){ System.out.println("testRESTPut id="+id); return "success"; }
@RequestMapping(value="/testRESTDelete/{id}",method=RequestMethod.DELETE) public String testRESTDelete(@PathVariable("id") Integer id){ System.out.println("testRESTDelete id="+id); return "success"; } |
hidden" name="_method" value="PUT">
hidden" name="_method" value="DELETE">
</fieldset> |
/** * @RequestParam 注解用于映射请求参数 * value 用于映射请求参数名称 * required 用于设置请求参数是否必须的 * defaultValue 设置默认值,当没有传递参数时使用该值 */ @RequestMapping(value="/testRequestParam") public String testRequestParam( @RequestParam(value="username") String username, @RequestParam(value="age",required=false,defaultValue="0") int age){ System.out.println("testRequestParam - username="+username +",age="+age); return "success"; } |
//了解: 映射请求头信息 用法同 @RequestParam @RequestMapping(value="/testRequestHeader") public String testRequestHeader(@RequestHeader(value="Accept-Language") String al){ System.out.println("testRequestHeader - Accept-Language:"+al); return "success"; } |
|
//了解:@CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam @RequestMapping("/testCookieValue") public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) { System.out.println("testCookieValue: sessionId: " + sessionId); return "success"; } |
增加页面链接
/** * Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配, 自动为该对象填充属性值。 * 支持级联属性 * 如:dept.deptId、dept.address.tel 等 */ @RequestMapping("/testPOJO") public String testPojo(User user) { System.out.println("testPojo: " + user); return "success"; } |
|
package com.atguigu.springmvc.entities;
public class Address {
private String province; private String city;
//get/set
} |
package com.atguigu.springmvc.entities;
public class User { private Integer id ; private String username; private String password;
private String email; private int age;
private Address address;
//get/set } |
如果中文有乱码,需要配置字符编码过滤器,且配置其他过滤器之前,
如(HiddenHttpMethodFilter),否则不起作用。(思考method=”get”请求的乱码问题怎么解决的)
<filter> <filter-name>encodingFilterfilter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class> <init-param> <param-name>encodingparam-name> <param-value>UTF-8param-value> init-param> <init-param> <param-name>forceEncodingparam-name> <param-value>trueparam-value> init-param> filter> <filter-mapping> <filter-name>encodingFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping> |
@Override protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(parameterType)) { Object nativeRequest = webRequest.getNativeRequest(parameterType); if (nativeRequest == null) { throw new IllegalStateException( "Current request is not of type [" + parameterType.getName() + "]: " + request); } return nativeRequest; } else if (ServletResponse.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; Object nativeResponse = webRequest.getNativeResponse(parameterType); if (nativeResponse == null) { throw new IllegalStateException( "Current response is not of type [" + parameterType.getName() + "]: " + response); } return nativeResponse; } else if (HttpSession.class.isAssignableFrom(parameterType)) { return request.getSession(); } else if (Principal.class.isAssignableFrom(parameterType)) { return request.getUserPrincipal(); } else if (Locale.class.equals(parameterType)) { return RequestContextUtils.getLocale(request); } else if (InputStream.class.isAssignableFrom(parameterType)) { return request.getInputStream(); } else if (Reader.class.isAssignableFrom(parameterType)) { return request.getReader(); } else if (OutputStream.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; eturn response.getOutputStream(); } else if (Writer.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; return response.getWriter(); } return super.resolveStandardArgument(parameterType, webRequest); } |
/** * 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型 * * HttpServletRequest * HttpServletResponse * HttpSession * java.security.Principal * Locale InputStream * OutputStream * Reader * Writer * @throws IOException */ @RequestMapping("/testServletAPI") public void testServletAPI(HttpServletRequest request,HttpServletResponse response, Writer out) throws IOException { System.out.println("testServletAPI, " + request + ", " + response); out.write("hello springmvc"); //return "success"; } |
提供了以下几种途径输出模型数据:
org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
MoelAndView addObject(String attributeName, Object attributeValue)
ModelAndView addAllObject(Map
设置视图:
void setView(View view)
void setViewName(String viewName)
/** * 目标方法的返回类型可以是ModelAndView类型 * 其中包含视图信息和模型数据信息 */ @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){ System.out.println("testModelAndView"); String viewName = "success"; ModelAndView mv = new ModelAndView(viewName ); mv.addObject("time",new Date().toString()); //实质上存放到request域中 return mv; } |
time: ${requestScope.time } |
|
|
|
|
|
|
|
|
|
具体使用步骤
//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型) @RequestMapping("/testMap") public String testMap(Map System.out.println(map.getClass().getName()); //org.springframework.validation.support.BindingAwareModelMap map.put("names", Arrays.asList("Tom","Jerry","Kite")); return "success"; } |
names: ${requestScope.names } |
//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型) @RequestMapping("/testMap2") public String testMap2(Map System.out.println(map.getClass().getName()); map.put("names", Arrays.asList("Tom","Jerry","Kite")); model.addAttribute("model", "org.springframework.ui.Model"); modelMap.put("modelMap", "org.springframework.ui.ModelMap");
System.out.println(map == model); System.out.println(map == modelMap); System.out.println(model == modelMap);
System.out.println(map.getClass().getName()); System.out.println(model.getClass().getName()); System.out.println(modelMap.getClass().getName());
/* true true true org.springframework.validation.support.BindingAwareModelMap org.springframework.validation.support.BindingAwareModelMap org.springframework.validation.support.BindingAwareModelMap */ return "success"; } |
public class BindingAwareModelMap extends ExtendedModelMap {
@Override public Object put(String key, Object value) { removeBindingResultIfNecessary(key, value); return super.put(key, value); }
@Override public void putAll(Map extends String, ?> map) { for (Map.Entry extends String, ?> entry : map.entrySet()) { removeBindingResultIfNecessary(entry.getKey(), entry.getValue()); } super.putAll(map); }
private void removeBindingResultIfNecessary(Object key, Object value) { if (key instanceof String) { String attributeName = (String) key; if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attributeName; BindingResult bindingResult = (BindingResult) get(bindingResultKey); if (bindingResult != null && bindingResult.getTarget() != value) { remove(bindingResultKey); } } } } } |
例如:
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.TYPE}) //说明这个注解只能应用在类型上面 @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface SessionAttributes { String[] value() default {}; //推荐使用 Class>[] types() default {}; //范围太广 } |
@Controller //@SessionAttributes("user") @SessionAttributes(value={"user"},types={String.class}) public class SpringMVCController { /** * @SessionAttributes * 除了可以通过属性名指定需要放到会话中的属性外(实际上是通过value指定key值), * 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上是通过types指定类型) * 注意:只能放在类的上面,不能修饰方法 */ @RequestMapping("/testSessionAttributes") public String testSessionAttributes(Map User user = new User("Tom","123","[email protected]",22); map.put("user", user); map.put("school", "atguigu"); //默认是被存放到request 域,如果设置了@SessionAttribute注解,就同时存放到session域中 return "success"; } } |
request user : ${requestScope.user } session user : ${sessionScope.user } request school : ${requestScope.school } session school : ${sessionScope.school } |
//1. 由 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用! @RequestMapping("/testModelAttribute") public String testModelAttribute(User user){ System.out.println("user="+user); return "success"; }
@ModelAttribute public void getUser(@RequestParam(value="id",required=false) Integer id,Map if(id!=null){ //模拟从数据库中获取到的user对象 User user = new User(1,"Tom","123456","[email protected]",12); System.out.println("从数据库中查询的对象:user="+user ); map.put("user", user); } } |
//org.springframework.web.HttpSessionRequiredException: Session attribute 'user' required - not found in session
//出现这个异常,是@SessionAttributes(value={"user"},types={String.class})导致的,去掉类上的这个注解
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ModelAttribute { /** * The name of the model attribute to bind to. * The default model attribute name is inferred from the declared * attribute type (i.e. the method parameter type or method return type), * based on the non-qualified class name: * e.g. "orderAddress" for class "mypackage.OrderAddress", * or "orderAddressList" for "List<mypackage.OrderAddress>". */ String value() default ""; } |
1). 创建 WebDataBinder 对象:
【1】确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
*注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute 的 value 属性值
【2】确定 target 属性:
> 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
> *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中获取 attrName 所对应的属性值.
若 session 中没有对应的属性值, 则抛出了异常.
> 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key和 attrName 相匹配, 则通过反射创建了 POJO 对象
2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel. 进而传到 request 域对象中.
4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
2). 若使用了@ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 ① 确定的 key 一致, 则会获取到.
@RequestMapping("/testModelAttribute") //public String testModelAttribute(User user){
public String testModelAttribute(@ModelAttribute("abc") User user){
System.out.println("修改 user="+user); return "success"; }
/** * @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用: 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中. 2). SpringMVC 会以 value 为 key, POJO 类型的对象为 value, 存入到 request 中. */ @ModelAttribute public void getUser(@RequestParam(value="id",required=false) Integer id,Map if(id!=null){ //模拟从数据库中获取到的user对象 User user = new User(1,"Tom","123456","[email protected]",12); System.out.println("从数据库中查询的对象:user="+user ); //map.put("user", user); //BindingAwareModelMap
//map.put("abc", user); //BindingAwareModelMap } } |
user user: ${requestScope.user }
abc user: ${requestScope.abc }
|
i18n.properties |
i18n_en_US.properties |
i18n_zh_CN.properties |
i18n.username=username i18n.password=password |
i18n.username=Username i18n.password=Password |
i18n.username=\u7528\u6237\u540D i18n.password=\u5BC6\u7801 |
①增加jstl标签 jar包(断点调试,这时的View对象就是JstlView)
②设置国际化资源文件
|
③控制器代码
package com.atguigu.springmvc.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller @RequestMapping("springmvc") public class SpringMVCController { @RequestMapping("/testViewAndViewResolver") public String testViewAndViewResolver(){ System.out.println("testViewAndViewResolver"); return "success"; } } |
④成功页面(/WEB-INF/views/success.jsp)使用fmt标签库
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> Sucess Page
|
|
http://localhost:8080/SpringMVC_02_View/success |
|
@RequestMapping("/testView") public String testView(){ System.out.println("testView..."); return "helloView"; //与视图Bean 对象的id一致 } |
package com.atguigu.springmvc.view;
import java.util.Date; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component; import org.springframework.web.servlet.View;
@Component public class HelloView implements View {
@Override public String getContentType() { return "text/html"; }
@Override public void render(Map HttpServletResponse response) throws Exception { response.getWriter().println("HelloView - time = " + new Date()); } } |
|
InternalResourceViewResolver默认的优先级:private int order = Integer.MAX_VALUE;
public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
public void setOrder(int order) { this.order = order; }
@Override public int getOrder() { return order; }
@Override public View resolveViewName(String viewName, Locale locale) throws BeansException { ApplicationContext context = getApplicationContext(); if (!context.containsBean(viewName)) { //说明视图组件必须增加到Spring的IOC 容器中,所以需要@Component // Allow for ViewResolver chaining. return null; } return context.getBean(viewName, View.class); } } |
@RequestMapping("/testRedirect") public String testRedirect(){ System.out.println("testRedirect"); return "redirect:/index.jsp"; //return "forward:/index.jsp"; } |