SpringMVC总结
springmvc是表现层的MVC框架,springmvc是spring框架一部分。
springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例);
springmvc的好处?
1、实现了代码分层,有利于组件的重用。
2、有了前端控制器的存在,可以帮我们完成请求参数的封装,json格式支持。
3、springmvc更加安全,更好,更便捷。
1、谈谈你对mvc的理解?
MVC是分层的一个设计思想。分为视图层,业务层,数据层(模型),有利于组件的重用。
细解:
(1)视图view: 视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并接受用户的输入。视图不进行任何业务逻辑处理。
(2)模型Model: 模型表示业务数据和业务处理。相当于JavaBean。一个模型能为多个视图提供数据。这提高了应用程序的重用性
(3)控制器Controller: 当用户单击Web页面中的提交按钮时,控制器接受请求并调用相应的模型去处理请求。然后根据处理的结果调用相应的视图来显示处理的结果。
MVC的处理过程:首先控制器接受用户的请求,调用相应的模型来进行业务处理,并返回数据给控制器。控制器调用相应的视图来显示处理的结果。并通过视图呈现给用户。
2、SpringMVC执行流程:
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 执行处理器(Controller,也叫后端控制器)。
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet响应用户
总结:用户发送请求至前端控制器,然后找处理器映射器拦截,通过适配器将url适配到相应的handler,就是我们的业务代码,然后就是返回一个ModelAndView对象给前端控制器,再通过视图解析器来解析得到view和填充相应数据到view对象中。
-
-
- 组件说明
-
以下组件通常使用框架提供实现:
- DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
- HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- Handler:处理器(就提具体的业务代码)
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。就是service
- HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
- View Resolver:视图解析器
生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- View:视图
springmvc框架提供了很多的View视图类型的支持。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。 需要用户开发的组件有handler、view |
- SpringMVC的参数绑定:
代码示例:
@GetMapping(MODEL + "/index.do")
public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws IOException {
ModelAndView view = new ModelAndView("/user/userMessage/index.html");
String pageNo = request.getParameter("pageNo");
view.addObject("pageNo", pageNo);
return view;
}
-
-
- 默认支持的参数类型
-
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
HttpServletRequest
通过request对象获取请求信息
HttpServletResponse
通过response处理响应信息
HttpSession
通过session对象得到session中存放的对象
Model/ModelMap(不用)
ModelMap是Model接口的实现类,通过Model或ModelMap向页面传递数据,如下:
//调用service查询商品信息
Items item = itemService.findItemById(id);
model.addAttribute("item", item);
页面通过${item.XXXX}获取item对象的属性值。
使用Model和ModelMap的效果一样,如果直接使用Model,springmvc会实例化ModelMap。
如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代。不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据。 |
如果使用Model则方法可以改造成:
@RequestMapping("/itemEdit")
public String itemEdit(HttpServletRequest request, Model model) {
//从Request中取id
String strId = request.getParameter("id");
Integer id = null;
//如果id有值则转换成int类型
if (strId != null && !"".equals(strId)) {
id = new Integer(strId);
} else {
//出错
return null;
}
Items items = itemService.getItemById(id);
//创建ModelAndView
//ModelAndView modelAndView = new ModelAndView();
//向jsp传递数据
//modelAndView.addObject("item", items);
model.addAttribute("item", items);
//设置跳转的jsp页面
//modelAndView.setViewName("editItem");
//return modelAndView;
return "editItem";
}
-
-
- 参数绑定-基本数据类型绑定
-
@RequestParam:常用于处理简单类型的绑定。
value:入参的请求参数名字。和请求的参数一致可以省略
required:是否必须,默认是true。
defaultValue默认值,用的比较多,比如我们在编辑某样商品的时候,不传参
代码:
@RequestMapping("/itemEdit")
public String editItem(@RequestParam(value="id",defaultValue="1",required=true)Integer ids, Model model) {
Items items = itemService.getItemById(ids);
//把数据传递给页面
model.addAttribute("item", items);
//返回逻辑视图
return "editItem";
}
-
-
-
- 支持的数据类型
-
-
参数类型推荐使用包装数据类型,因为基础数据类型不可以为null
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为true或false。
处理器方法:
public String editItem(Model model,Integer id,Boolean status) throws Exception
请求url:
http://localhost:8080/xxx.action?id=2&status=false
-
-
- 绑定pojo类型
-
@RequestMapping("/updateitem")
public String updateItem(Items items) {
itemService.updateItem(items);
//返回成功页面
return "success";
}
-
-
- 参数绑定-包装pojo
-
public class QueryVo {
private Items items;
public Items getItems() {
return items;
}
public void setItems(Items items) {
this.items = items;
}
}
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo) {
//打印绑定结果
System.out.println(queryVo.getItems().getId());
System.out.println(queryVo.getItems().getName());
return "success";
}
-
-
- 参数绑定-自定义参数绑定(...)
-
注意:提交的表单中不要有日期类型的数据,否则会报400错误。如果想提交日期类型的数据需要用到后面的自定义参数绑定的内容。
代码:
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(source);
return date;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
<!-- 配置注解驱动,如果配置此标签可以不用配置处理器映射器和适配器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 转换器的配置 -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.itheima.springmvc.converter.DateConverter"></bean>
</set>
</property>
</bean>
总结:
- 系统自带了很多转换器,只有少数情况需要我们自已做转换器。
- 如何自已编写转换器
- 如何配置转换器
-
-
- 高级参数绑定-绑定数组类型
-
代码:
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo, String[] ids) {
//打印绑定结果
System.out.println(queryVo.getItems().getId());
System.out.println(queryVo.getItems().getName());
return "success";
}
<c:forEach items="${itemList }" var="item" varStatus="s">
<tr>
<td><input type="checkbox" name="ids" value="${item.id }"></td>
<td>
<input type="hidden" name="itemList[${s.index}].id" value="${item.id }">
<input type="text" name="itemList[${s.index}].name" value="${item.name }">
</td>
<td><input type="text" name="itemList[${s.index}].price" value="${item.price }"></td>
<td><input type="text" name="itemList[${s.index}].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"></td>
<td><input type="text" name="itemList[${s.index}].detail" value="${item.detail }"></td>
<%-- <td><a href="${pageContext.request.contextPath }/item/itemEdit.action?id=${item.id}">修改</a></td> --%>
<td><a href="${pageContext.request.contextPath }/item/itemEdit/${item.id}">修改</a></td>
</tr>
</c:forEach>
varStatus属性常用参数总结下:
${status.index} 输出行号,从0开始。
${status.count} 输出行号,从1开始。
${status.current} 当前这次迭代的(集合中的)项
${status.first} 判断当前项是否为集合中的第一项,返回值为true或false
${status.last} 判断当前项是否为集合中的最后一项,返回值为true或false
begin、end、step分别表示:起始序号,结束序号,跳跃步伐。
-
-
- 高级参数绑定-list类型绑定
-
代码:
<c:forEach items="${itemList }" var="item" varStatus="s">
<tr>
<td><input type="checkbox" name="ids" value="${item.id }"></td>
<td>
<input type="hidden" name="itemList[${s.index}].id" value="${item.id }">
<input type="text" name="itemList[${s.index}].name" value="${item.name }">
</td>
<td><input type="text" name="itemList[${s.index}].price" value="${item.price }"></td>
<td><input type="text" name="itemList[${s.index}].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"></td>
<td><input type="text" name="itemList[${s.index}].detail" value="${item.detail }"></td>
<%-- <td><a href="${pageContext.request.contextPath }/item/itemEdit.action?id=${item.id}">修改</a></td> --%>
<td><a href="${pageContext.request.contextPath }/item/itemEdit/${item.id}">修改</a></td>
</tr>
</c:forEach>
public class QueryVo {
private Items items;
private String[] ids;
private List<Items> itemList;
public List<Items> getItemList() {
return itemList;
}
public void setItemList(List<Items> itemList) {
this.itemList = itemList;
}
@RequestMapping("/queryitem")
public String queryItem(QueryVo queryVo, String[] ids) {
//打印绑定结果
System.out.println(queryVo.getItems().getId());
System.out.println(queryVo.getItems().getName());
return "success";
}
- controller方法有几种返回类型?
void 、modelandview、string
- 返回string有三种情况
//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/item/editItem.jsp
return "item/editItem";
//重定向到queryItem.action地址,request无法带过去
return "redirect:queryItem.action";
//结果转发到editItem.action,request可以带过去
return "forward:editItem.action";
- forward和redirect的区别
- forward地址栏不会发生变化而redirect地址栏会发生变化
- forward是一次请求一次响应而redirect两次请求两次响应
- forward只能在容器内部跳转而redirect不仅可以在内部跳还可以外部跳
- forward更好带上请求参数
4、全局异常处理器(怎么做???)
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理
代码:
自定义异常处理器:
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
//判断异常的种类
String msg = null;
if (exception instanceof CustomerException) {
CustomerException custExp = (CustomerException) exception;
msg = custExp.getExpMessage();
} else {
//如果时候自定义异常,取错误消息
//如果时候运行时异常,取错误的堆栈。
exception.printStackTrace();
StringWriter s = new StringWriter();
PrintWriter printWriter = new PrintWriter(s);
exception.printStackTrace(printWriter);
msg = s.toString();
}
//写日志、发短信、发邮件
//...
//返回一个错误页面,显示错误消息
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", msg);
modelAndView.setViewName("error");
return modelAndView;
}
}
自定义异常类:
public class CustomerException extends Exception {
private String expMessage;
public CustomerException() {
}
public CustomerException(String msg) {
this.expMessage = msg;
}
public String getExpMessage() {
return expMessage;
}
public void setExpMessage(String expMessage) {
this.expMessage = expMessage;
}
}
@RequestMapping("/itemList")
public ModelAndView getItemList() throws Exception {
//自定义异常测试
/*if (true) {
throw new CustomerException("这是我们自定义的异常,哈哈哈。。。。。");
}*/
//int i = 1/0;
//查询商品列表
List<Items> itemList = itemService.getItemList();
//把结果传递给页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemList", itemList);
//设置逻辑视图
modelAndView.setViewName("itemList");
//返回结果
return modelAndView;
}
<!-- 全局异常处理器 -->
<bean class="com.itheima.springmvc.exception.GlobalExceptionResolver"/>
5、json数据交互
代码:
//json数据交互
//@RequestBody:接收json数据并转换成pojo对象
//@ResponseBody:响应json数据,把java对象转换成json并响应
@RequestMapping("/jsontest")
@ResponseBody
public Items jsonTest(@RequestBody Items items) {
return items;
}
- springmvc实现Restful服务
@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。
@RequestMapping("/itemEdit/{id}")
//如果id和方法的形参一致@PathVariable中可以不写内容
public String editItem(@PathVariable(“id”) Integer iid, Model model) {
Items items = itemService.getItemById(iid);
//把数据传递给页面
model.addAttribute("item", items);
//返回逻辑视图
return "editItem";
}
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /:拦截所以请求,不包括jsp。/*:拦截所有请求包括jsp,应该配置“/” -->
<url-pattern>/</url-pattern>
</servlet-mapping>
7、拦截器的使用(...)
1、Spring管理事务有几种方式?
答:有两种方式:
1、编程式事务,在代码中硬编码。(不推荐使用)
2、声明式事务,在配置文件中配置(推荐使用)
声明式事务又分为两种:
a、基于XML的声明式事务
b、基于注解的声明式事务
- SpringMvc的控制器是不是单例模式?如果是,有什么问题?怎么解决?
是单例模式;
所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的;
解决方案是:
①、不要在controller中定义成员变量。
②、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式
为什么是单例?
单例的原因有二:
1、为了性能。
2、不需要多例。(只要controller中不定义属性,那么单例完全是安全的)
- SpringMvc怎么和AJAX相互调用的?
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象
具体步骤如下:
1.加入Jackson.jar
2.在配置文件中配置json的映射
3.在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解