SpringMVC
SpringMVC
SpringMVC的几大组件
1、前端总控制器:用于接受、处理请求,对其他各个组件进行调度。
2、处理器映射器(Handler Mapping):根据请求url找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给DispatherServlet(HandlerExecutionChain执行链)
3、处理器适配器(Handler Adapter):DispatherServlet通过HandlerAdapter处理器适配器调用处理器,找到对应的处理器。然后将ModelAndView(Model 数据和view 名)给前端总控制器
4、视图解析器(ViewResolver):将逻辑视图转化为真正的视图,然后在前端控制器处将数据和页面结合返回给请求的用户(返回静态页面)
SpringMvc所需jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.16</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!--jsp相关--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <scope>provided</scope> </dependency> <!--provided有服务器提供,tomcat而且部署的时候同样不会打包--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> </dependency>
DispatcherServlet是一个servlt。servlet在第一次被访问的时候创建的,后面就不会再创建了。
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--服务器启动的时候,初始化springmvc中的DispatherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
上图为handlerMapping拿到执行链,
interceptorList=ArrayList<E>(id=1067) :拦截器的集合
在xml文件中可以不添加映射器和适配器因为在webmvc的代码中,DispatcherServlet.properties配置文件中含有
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter为处理器映射器和处理器适配器。
但是这两个类被淘汰了,可以使用
<mvc:annotation-driven />这行代码可以自动加载替代上面两个类的新类,以及JSON转换器
2、注解开发
由于springmvc注解开发更加简洁而且如果不适用注解,每个类都需要实现Controller接口,但是这样的话注册到spring容器中时,该类只能绑定一个路径,springmvc是方法级别的,
一个方法对应一个请求,这样极大的降低了开发效率。
而使用注解开发只需要在类上添加
@Controller @RequestMapping(value = "hello")
即可标识,已将该类添加到了spring容器,而且可以指定一个路径到该类,而再在类中的各个方法前添加
@RequestMapping(value = "/i18n")即可标识hello/i18n.html的请求会到含有in18n的方法上。
是否添加/无所谓
ANT风格开发,即通配符。
通配符 |
说明 |
? |
匹配任何单字符 |
* |
匹配0或者任意数量的字符 |
** |
匹配0或者更多的目录 |
其中
@RequestMapping("/test/*/show")
*代表可以填写任意字符,但是不可以写/test/show.html
**则既可以使用**代表任意字符,也可以使用/test/show.html
<context:component-scan base-package="com.back.controller"/> //开启注解扫描,以扫描那些类开启了注解
视图配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/> <!-- 视图前缀 -->
<property name="suffix" value=".jsp" /> <!-- 视图后缀 -->
</bean>
可以不用每个路径都添加前缀和后缀
限定请求方式和请求参数
限定只能使用GET方式请求:
@RequestMapping(value = "/show",method = RequestMethod.GET)
即可使用GET也可以使用POST
@RequestMapping(value = "/show",method = {RequestMethod.GET,RequestMethod.POST})
限定请求参数
@RequestMapping(value = "/show",params = "userId")
请求参数必须含有userId
params=“userId”;请求参数必须含有userId
params=“!userId”;请求参数不能含有userId
params=“userId!=1”;请求参数必须包含userId,但是其值不能为1
params={"userId","userName"} 请求参数必须包含userId和userName参数
3、处理方法好参数绑定
3.1 绑定servlet内置对象
@RequestMapping(value = "/hello.do") public ModelAndView welcome(HttpServletRequest request, HttpServletResponse response, HttpSession session) { ModelAndView mv = new ModelAndView("welcome"); mv.addObject("msg","sdfasfsaf"); mv.getModel().put("name","张小娴"); return mv; }
一般常用的有request、response和session
3.2 获取请求连接中的值 @PathVariable(获取路径中的占位符的值)
@RequestMapping(value = "test/{id}/show") public ModelAndView test4(@PathVariable("id")String id){ ModelAndView mv=new ModelAndView("welcome"); mv.addObject("msg","占位符id"+id); System.out.println(id); return mv; }
将请求路径中的{id}传入到id参数。其中@PathVariable("id")的id不可省去,不添加的时候它是由IDE工具自动添加到类中,但是发布的时候并不能保证可以传入class中。
{id} 这里表示可以输入任何字符都可以被@PathVariable("id")获取到
3.3 @RequestParam
@RequestMapping(value = "test/{uri}/show") public ModelAndView test4(@RequestParam(value = "username",required = false,defaultValue = "012")String username){ ModelAndView mv=new ModelAndView("welcome"); System.out.println(username); mv.addObject("username",username); return mv; }
@RequestParam有三个参数1、value:参数名 2、required:是否必须(当有defaultValue时,该参数无效,以defaultValue为准 3、defaultValue默认参数值)
3.4POJO对象绑定参数
即将User user当成参数传入进来,然后在地址栏输入属性例:userName=zhangsan; 会自动匹配到user.userName
3.5Java基本数据类型绑定
Spring对Java的基本数据类型支持非常多,基本满足开发需要。
@RequestMapping(value = "/show") @ResponseStatus(value = HttpStatus.OK) public ModelAndView test4(@RequestParam("name")String name,@RequestParam(value = "age")Integer age){ ModelAndView mv=new ModelAndView("welcome"); mv.addObject("username","李大庄"); System.out.println(age); return mv; }
<form action="/demos/demo1.action" method="post"> <div>姓名:</div> <div><input name="name" value="张三"/></div> <div class="clear"></div>
@RequestParam可以添加页面上的name属性的值给name
@ResponseStatus(value = HttpStatus.OK)直接返回一个结果表示200OK。
3.6 session对象的创建时机
第一次获取session对象,或者访问一个jsp页面,内置对象有session也会创建session。
3.7 集合List绑定
List无法直接作为参数去接受数据,如下代码
@RequestMapping(value = "/show") @ResponseStatus(value = HttpStatus.OK) public ModelAndView test4(List<User> user){ ModelAndView mv=new ModelAndView("welcome"); mv.addObject("username",user); return mv; }
数组如果是POJO类型的数据也是不可以直接接收参数的
一定要将List<User>当做一个包装类的属性才行
public class UserForm { private List<User> users; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } }
例如:
@RequestMapping(value = "/show") @ResponseStatus(value = HttpStatus.OK) public ModelAndView test4(UserForm userForm){ ModelAndView mv=new ModelAndView("welcome"); mv.addObject("username",123); if (userForm.getUsers()!=null){ for (User user:userForm.getUsers()) { System.out.println(user); } } return mv; }
像以上这种方式即可接收参数。
前端页面代码:
<form action="/show.do"> 用户1:<input type="text" name="users[0].username"><br> 用户2:<input type="text" name="users[1].username"><br> 用户3:<input type="text" name="users[2].username"><br> <button type="submit">提交</button> </form>
包括如果不是List<User> user,而是User[]user数组;也需要弄到包装类里。
public class UserForm { private User[] users; public User[] getUsers() { return users; } public void setUsers(User[] users) { this.users = users; } }
Controller代码不变
4、SpringMVC和Struts的区别
4.1、SpringMVC核心是servlet,Struts是Filter
4.2、接收参数模式不一样 springmvc接收参数是在方法上,Struts2是定义成员变量。
4.3、Struts2返回值类型只能是string,springmvc可以返回多种类型
4.4、Struts2是多例的,springmvc是单例的。如果一个类是单例的,那么类中的属性,是所有人都共享的。
springmvc把所有的参数都绑定到方法上,运行的时候会为每一个方法开辟一个新的方法区,变量是自己的。没调用一次方法就会生成一个方法区,都会有一个新的方法区。没有
冲突的地方。
springmvc性能更高,单例的。节省内存空间,单例的。不会开辟多余的内存空间。多个方法之间不能共享。
5、JSP和JSTL视图解析器
JSTL需要导入一个jar包 jstl-version.jar
在jsp页面头上添加:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
用foreach循环
<c:forEach items="${users}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> </tr> </c:forEach>
Controller类
@RequestMapping(value = "/show") @ResponseStatus(value = HttpStatus.OK) public ModelAndView test4(){ ModelAndView mv=new ModelAndView("welcome"); User user=new User(); user.setId(12); user.setUsername("张三"); User user1=new User(); user1.setId(32); user1.setUsername("李四"); List<User> users=new ArrayList<User>(); users.add(user); users.add(user1); mv.addObject("users",users); return mv; }
6、使用ResponseBody输出JSON
在实际开发过程中返回JSON是最为常见的一种方式,所以SpringMVC提供了一种更为简便的方式输出数据。就是使用@ResponseBody注解。
依赖包:jackson-annotation、jackson-core、jackson-databind
@RequestMapping(value = "/show") @ResponseBody public List<User> test4(){ ModelAndView mv=new ModelAndView("welcome"); User user=new User(); user.setId(12); user.setUsername("张三"); User user1=new User(); user1.setId(32); user1.setUsername("李四"); List<User> users=new ArrayList<User>(); users.add(user); users.add(user1); mv.addObject("users",users); return users; }
添加了@ResponseBody就会将返回的结果当成响应体进行输出。springmvc会调用一个messageConvertor去把returnvalue进行数据输出,默认会去调用json去处理数据。
1、springmvc要处理json,需要依赖jackson工具类
2、messageConvertor:消息转换器,把请求中的内容转换成java的数据格式,把java的数据格式转换成响应的内容
3、当方法上面标识了@ResponseBody之后,springmvc使用的消息转换器,默认使用jackson的消息转换器。
4、springmvc有很多的转换器,在注解驱动的类里面,如果它发现了jackson的工具类才会把jackson的massegeConvertor添加到springmvc中。
7、RequestBody请求体,处理接收的json格式的数据
依赖springmvc.xml中的
mvc:annotation-driven />
public ModelAndView test4(@RequestBody User user)
将json格式转换成user对象
@RequestMapping(value = "/show") public ModelAndView test4(@RequestBody List<User> users){ ModelAndView mv=new ModelAndView("welcome"); if(users!=null){ for (User user:users){ System.out.println(user); } } return mv; }
8、转发和重定向
如果Controller的返回值是String,那么它返回的是视图名称。
如果是
ModelAndView mv = new ModelAndView("redirect:/welcome");表示转发到这个路径下
ModelAndView mv = new ModelAndView("forward:/hello/welcome.do"); //重定向该路径
9、拦截器
MappingHandler被调用返回给DispatcherServlet一个HandlerExecutionChain,这其中包含了Handler对象和Intercptor(拦截器)对象。
<!--对拦截器进行配置--> <mvc:interceptors> <mvc:interceptor> <!--定义所有请求都进入拦截器--> <mvc:mapping path="/**"/> <bean class="com.back.interceptor.MyHandlerInterceptor"/> </mvc:interceptor> </mvc:interceptors>
拦截器类实现HandlerInterceptor接口。
public class MyHandlerInterceptor implements HandlerInterceptor { /** *前置方法:从前向后执行 * 在执行Handler前执行 *返回值:true:继续向下执行 * false:终止执行 */ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("前置方法"); return true; } /** * 后置方法,执行完handler之后执行,从后向前执行 * 如果前置方法返回false:整个拦截器中的所有的后置方法都不再执行 * * */ @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("后置方法"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("完成方法"); } }