1.1 Springmvc是什么?

SpringMvc是spring的一个模块,基于MVC的一个框架 无需中间整合层来整合 。

1.2 框架结构

springmvc从header里提取参数_spring

1.3 架构流程

1、用户发送请求至前端控制器DispatcherServlet
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器
3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
4、DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5、执行处理器(Controller,也叫后端控制器)
6、Controller执行完成返回ModelAndView
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、ViewReslover解析后返回具体View
10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11、DispatcherServlet响应用户

1.4 组件说明
  • DispatcherServlet:前端控制器
    用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
  • HandlerMapping:处理器映射器
    HandlerMapping负责根据用户请求url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
  • Handler:处理器
    Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
    由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
  • HandlAdapter:处理器适配器
    通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
  • ViewResolver:视图解析器
    View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
  • View:视图
    springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
    一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。
需要用户开发的组件有handler、view

1.5 常用配置简要说明

组件扫描器
使用context:component-scan自动扫描标记@Controller的控制器类,
在springmvc.xml配置文件中配置如下:

<!-- 配置controller扫描包,多个包之间用,分隔 -->
<context:component-scan base-package="cn.itcast.springmvc.controller" />

注解驱动
可以在springmvc.xml配置文件中使用mvc:annotation-driven替代注解处理器和适配器的配置。

<!-- 注解驱动 -->
<mvc:annotation-driven />

视图解析器
在springmvc.xml配置文件中配置如下:

<!-- 配置视图解析器 -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 配置逻辑视图的前缀 -->
		<property name="prefix" value="/WEB-INF/jsp/" />
		<!-- 配置逻辑视图的后缀 -->
		<property name="suffix" value=".jsp" />
	</bean>
1.6 参数绑定

页面点击修改按钮,发起请求
http://127.0.0.1:8080/springmvc-web/itemEdit.action?id=1

  • 需要从请求的参数中把请求的id取出来,想获得Request对象只需要在Controller方法的形参中添加一个参数即可。Springmvc框架会自动把Request对象传递给方法。
  • 使用@RequestParam,如果提交的参数很多,或者提交的表单中的内容很多的时候,可以使用简单类型接受数据,也可以使用pojo接收数据。
    要求:pojo对象中的属性名和表单中input的name属性一致。
@RequestMapping("/itemEdit")
public ModelAndView queryItemById(HttpServletRequest request) {
	// 从request中获取请求参数
	String strId = request.getParameter("id");
	Integer id = Integer.valueOf(strId);
	// 根据id查询商品数据
	Item item = this.itemService.queryItemById(id);

	// 把结果传递给页面
	ModelAndView modelAndView = new ModelAndView();
	// 把商品数据放在模型中
	modelAndView.addObject("item", item);
	// 设置逻辑视图
	modelAndView.setViewName("itemEdit");

	return modelAndView;
}
/**
使用@RequestParam常用于处理简单类型的绑定。
value:参数名字,即入参的请求参数名字,如value=“itemId”表示请求的参数	   区中的名字为itemId的参数的值将传入
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
TTP Status 400 - Required Integer parameter 'XXXX' is not present
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
**/
@RequestMapping("/itemEdit")
public String queryItemById(@RequestParam(value = "itemId", required = true, defaultValue = "1") Integer id,
		ModelMap modelMap) {
	// 根据id查询商品数据
	Item item = this.itemService.queryItemById(id);

	// 把商品数据放在模型中
	modelMap.addAttribute("item", item);

	return "itemEdit";
}

默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
HttpServletRequest–>通过request对象获取请求信息
HttpServletResponse–>通过response处理响应信息
HttpSession–>通过session对象得到session中存放的对象

1.6.1 Model/ModelMap

如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代。

@RequestMapping("/itemEdit")
public String queryItemById(HttpServletRequest request, Model model) {
	// 从request中获取请求参数
	String strId = request.getParameter("id");
	Integer id = Integer.valueOf(strId);

	// 根据id查询商品数据
	Item item = this.itemService.queryItemById(id);

	// 把结果传递给页面
	// 把商品数据放在模型中
	model.addAttribute("item", item);

	return "itemEdit";
}
1.6.2 自定义Converter
//Converter<S, T>
//S:source,需要转换的源的类型
//T:target,需要转换的目标类型
public class DateConverter implements Converter<String, Date> {
	@Override
	public Date convert(String source) {
		try {
			// 把字符串转换为日期类型
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
			Date date = simpleDateFormat.parse(source);

			return date;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 如果转换异常则返回空
		return null;
	}
}

配置Converter

!-- 配置注解驱动 -->
<!-- 如果配置此标签,可以不用配置... -->
<mvc:annotation-driven conversion-service="conversionService" />

<!-- 转换器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
	<property name="converters">
		<set>
			<bean class="cn.springmvc.demo.converter.DateConverter" />
		</set>
	</property>
</bean>
1.6.3 绑定数组

修改itemList.jsp页面,增加多选框,提交url是queryItem.action

<form action="${pageContext.request.contextPath }/queryItem.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>商品id<input type="text" name="item.id" /></td>
<td>商品名称<input type="text" name="item.name" /></td>
<td><input type="submit" value="查询"/></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
	<td>选择</td>
	<td>商品名称</td>
	<td>生产日期</td>
</tr>
<c:forEach items="${itemList }" var="item">
<tr>
	<td><input type="checkbox" name="ids" value="${item.id}"/></td>
	<td>${item.name }</td>
	<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
</tr>
</c:forEach>

</table>
</form>

Controller方法中可以用String[]接收,或者pojo的String[]属性接收。两种方式任选其一即可。

@RequestMapping("queryItem")
public String queryItem(QueryVo queryVo, Integer[] ids) {

	System.out.println(queryVo.getItem().getId());
	System.out.println(queryVo.getItem().getName());

	System.out.println(queryVo.getIds().length);
	System.out.println(ids.length);

	return "success";
}
1.7 @RequestMapping
1.7.1 URL路径映射

@RequestMapping(value=“item”)或@RequestMapping("/item")
value的值是数组,可以将多个url映射到同一个方法

/**
 * 查询商品列表
 * @return
 */
@RequestMapping(value = { "itemList", "itemListAll" })
public ModelAndView queryItemList() {
	// 查询商品数据
	List<Item> list = this.itemService.queryItemList();

	// 创建ModelAndView,设置逻辑视图名
	ModelAndView mv = new ModelAndView("itemList");

	// 把商品数据放到模型中
	mv.addObject("itemList", list);
	return mv;
}

在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头

1.7.2 请求方法限定
  • 限定GET方法
@RequestMapping(method = RequestMethod.GET)

如果通过POST访问则报错:
HTTP Status 405 - Request method ‘POST’ not supported

  • 限定POST方法
@RequestMapping(method = RequestMethod.POST)

如果通过GET访问则报错:
HTTP Status 405 - Request method ‘GET’ not supported

  • GET和POST都可以
@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})
1.8 Controller方法返回值
1.8.1 返回ModelAndView

controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view

1.8.2 返回void
  • 1、使用request转发页面,如下:
    request.getRequestDispatcher(“页面路径”).forward(request, response);
    request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request, response);
  • 2、可以通过response页面重定向:
    response.sendRedirect(“url”)
    response.sendRedirect("/springmvc-web2/itemEdit.action");
  • 3、可以通过response指定响应结果,例如响应json数据如下:
    response.getWriter().print("{“abc”:123}");
@RequestMapping("queryItem")
public void queryItem(HttpServletRequest request, HttpServletResponse response) throws Exception {
	// 1 使用request进行转发
	// request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request,
	// response);

	// 2 使用response进行重定向到编辑页面
	// response.sendRedirect("/springmvc-web2/itemEdit.action");

	// 3 使用response直接显示
	response.getWriter().print("{\"abc\":123}");
}
1.8.3 返回字符串
  • 逻辑视图名
    controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址
    //指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/itemList.jsp
    return “itemList”;
  • Redirect重定向
    Contrller方法返回字符串可以重定向到一个url地址
@RequestMapping("updateItem")
public String updateItemById(Item item) {
	// 更新商品
	this.itemService.updateItemById(item);

	// 修改商品成功后,重定向到商品编辑页面
	// 重定向后浏览器地址栏变更为重定向的地址,
	// 重定向相当于执行了新的request和response,所以之前的请求参数都会丢失
	// 如果要指定请求参数,需要在重定向的url后面添加 ?itemId=1 这样的请求参数
	return "redirect:/itemEdit.action?itemId=" + item.getId();
}
  • forward转发
    Controller方法执行后继续执行另一个Controller方法
    如下商品修改提交后转向到商品修改页面,修改商品的id参数可以带到商品修改方法中。
@RequestMapping("updateItem")
public String updateItemById(Item item) {
	// 更新商品
	this.itemService.updateItemById(item);

	// 修改商品成功后,继续执行另一个方法
	// 使用转发的方式实现。转发后浏览器地址栏还是原来的请求地址,
	// 转发并没有执行新的request和response,所以之前的请求参数都存在
	return "forward:/itemEdit.action";

}
1.9 异常处理器

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理

springmvc从header里提取参数_mvc_02

public class MyException extends Exception {
	// 异常信息
	private String message;

	public MyException() {
		super();
	}

	public MyException(String message) {
		super();
		this.message = message;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

}
public class CustomHandleException implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception exception) {
		// 定义异常信息
		String msg;

		// 判断异常类型
		if (exception instanceof MyException) {
			// 如果是自定义异常,读取异常信息
			msg = exception.getMessage();
		} else {
			// 如果是运行时异常,则取错误堆栈,从堆栈中获取异常信息
			Writer out = new StringWriter();
			PrintWriter s = new PrintWriter(out);
			exception.printStackTrace(s);
			msg = out.toString();

		}

		// 把错误信息发给相关人员,邮件,短信等方式
		// TODO

		// 返回错误页面,给用户友好页面显示错误信息
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("msg", msg);
		modelAndView.setViewName("error");

		return modelAndView;
	}
}

在springmvc.xml中添加:

<!-- 配置全局异常处理器 -->
<bean 
id="customHandleException" 	class="cn.test.ssm.exception.CustomHandleException"/>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<h1>系统发生异常了!</h1>
	<br />
	<h1>异常信息</h1>
	<br />
	<h2>${msg }</h2>

</body>
</html>

异常测试

@RequestMapping(value = { "itemList", "itemListAll" })
public ModelAndView queryItemList() throws Exception {
	// 自定义异常
	if (true) {
		throw new MyException("自定义异常出现了~");
	}

	// 运行时异常
	int a = 1 / 0;

	// 查询商品数据
	List<Item> list = this.itemService.queryItemList();
	// 创建ModelAndView,设置逻辑视图名
	ModelAndView mv = new ModelAndView("itemList");
	// 把商品数据放到模型中
	mv.addObject("itemList", list);

	return mv;
}
1.9 @RequestBody

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容(json数据)转换为java对象并绑定到Controller方法的参数上。

1.10 RESTful支持

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、修改、查询。
传统方式操作资源
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE

**使用(@PathVariable() Integer id)获取url上的数据 **

/**
 * 使用RESTful风格开发接口,实现根据id查询商品
 * 
 * @param id
 * @return
 */
@RequestMapping("item/{id}")
@ResponseBody
public Item queryItemById(@PathVariable() Integer id) {
	Item item = this.itemService.queryItemById(id);
	return item;
}
1.11 拦截器

Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
实现HandlerInterceptor接口,如下:

public class HandlerInterceptor1 implements HandlerInterceptor {
	// controller执行后且视图返回后调用此方法
	// 这里可得到执行controller时的异常信息
	// 这里可记录操作日志
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("HandlerInterceptor1....afterCompletion");
	}

	// controller执行后但未返回视图前调用此方法
	// 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		System.out.println("HandlerInterceptor1....postHandle");
	}

	// Controller执行前调用此方法
	// 返回true表示继续执行,返回false中止执行
	// 这里可以加入登录校验、权限拦截等
	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		System.out.println("HandlerInterceptor1....preHandle");
		// 设置为true,测试使用
		return true;
	}
}

在springmvc.xml中配置拦截器

<!-- 配置拦截器 -->
<mvc:interceptors>
	<mvc:interceptor>
		<!-- 所有的请求都进入拦截器 -->
		<mvc:mapping path="/**" />
		<!-- 配置具体的拦截器 -->
		<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1" />
	</mvc:interceptor>
	<mvc:interceptor>
		<!-- 所有的请求都进入拦截器 -->
		<mvc:mapping path="/**" />
		<!-- 配置具体的拦截器 -->
		<bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2" />
	</mvc:interceptor>
</mvc:interceptors>

preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用

应用:

1、有一个登录页面,需要写一个Controller访问登录页面
2、登录页面有一提交表单的动作。需要在Controller中处理。
a)判断用户名密码是否正确(在控制台打印)
b)如果正确,向session中写入用户信息(写入用户名username)
c)跳转到商品列表
3、拦截器。
a)拦截用户请求,判断用户是否登录(登录请求不能拦截)
b)如果用户已经登录。放行
c)如果用户未登录,跳转到登录页面。

@Controller
@RequestMapping("user")
public class UserController {

	/**
	 * 跳转到登录页面
	 * 
	 * @return
	 */
	@RequestMapping("toLogin")
	public String toLogin() {
		return "login";
	}

	/**
	 * 用户登录
	 * 
	 * @param username
	 * @param password
	 * @param session
	 * @return
	 */
	@RequestMapping("login")
	public String login(String username, String password, HttpSession session) {
		// 校验用户登录
		System.out.println(username);
		System.out.println(password);

		// 把用户名放到session中
		session.setAttribute("username", username);

		return "redirect:/item/itemList.action";
	}

}
Override
erride
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
	// 从request中获取session
	HttpSession session = request.getSession();
	// 从session中获取username
	Object username = session.getAttribute("username");
	// 判断username是否为null
	if (username != null) {
		// 如果不为空则放行
		return true;
	} else {
		// 如果为空则跳转到登录页面
		response.sendRedirect(request.getContextPath() + "/user/toLogin.action");
	}

	return false;
}

在springmvc.xml配置拦截器

<mvc:interceptor>
	<!-- 配置商品被拦截器拦截 -->
	<mvc:mapping path="/item/**" />
	<!-- 配置具体的拦截器 -->
	<bean class="cn.itcast.ssm.interceptor.LoginHandlerInterceptor" />
</mvc:interceptor>