第一天
课程安排:
1、什么是springMVC?
2、springMVC框架原理
前端控制器、处理器映射器、处理器适配器、视图解析器。
3、springMVC入门程序
目的:对前端控制器、处理器映射器、处理器适配器、视图解析器
非注解的处理器映射器、处理器适配器
注解的处理器映射器、处理器适配器
4、springMVC和mybatis整合
5、springMVC注解开发
常用的注解学习
参数绑定(简单类型、pojo、集合类型)
自定义参数绑定
6、springMVC和struts2的区别
1、springMVC框架
1.1、什么是springMVC
springMVC是spring框架的一个模块,springMVC和spring无需通过中间整合层进行整合。
springMVC是一个基于MVC的web框架。
1.2、什么是MVC
MVC是一个设计模式,MVC在b/s系统下的应用:
1.3、springMVC框架
第一步:发起请求到 前端控制器DispatcherServlet
。
第二步:前端控制器请求处理器映射器HandlerMapping
查找Handler。
可以根据xml配置、注解进行查找。
第三步:处理器映射器HandlerMapping向前端控制器返回Handler
。
第四步:前端控制器调用处理器适配器去执行Handler。
第五步:处理器适配器HandlerAdapter
去执行Handler。
第六步:Handler执行完成给适配器返回ModelAndView
。
第七步:处理器适配器向前端控制器返回ModelAndView。
ModelAndView是springMVC框架的一个底层对象,包括model和view。
第八步:前端控制请求视图解析器
去进行视图解析。
根据逻辑视图名解析成真正的视图(jsp)。
第九步:视图解析器向前端控制器返回view。
第十步:前端控制器进行视图渲染。
视图渲染将模型数据(在ModelAndView对象中)填充到request域。
第十一步:前端控制器向用户响应结果。
组件:
1、前端控制器DispatcherServlet(不需要程序员开发)
作用:接收请求,响应结果,相当于转发器、中央处理器。
2、处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的url查找Handler
3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
4、处理器Handler(需要程序员开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
5、视图解析器View resolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
6、视图view(需要程序员开发jsp)
view是一个接口,实现类支持不用的view类型(jsp、freemarker、pdf……)
2、入门程序
2.1、环境准备
-
数据库环境:
/* Navicat MySQL Data Transfer Source Server : ZWD Source Server Version : 50711 Source Host : localhost:3306 Source Database : mybatis Target Server Type : MYSQL Target Server Version : 50711 File Encoding : 65001 Date: 2019-11-29 17:34:25 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `items` -- ---------------------------- DROP TABLE IF EXISTS `items`; CREATE TABLE `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL COMMENT '商品名称', `price` float(10,1) NOT NULL COMMENT '商品定价', `detail` text COMMENT '商品描述', `pic` varchar(64) DEFAULT NULL COMMENT '商品图片', `createtime` datetime NOT NULL COMMENT '生产日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of items -- ---------------------------- INSERT INTO `items` VALUES ('1', '台式机', '3000.0', '该电脑质量非常好!!!!', null, '2015-02-03 13:22:53'); INSERT INTO `items` VALUES ('2', '笔记本', '6000.0', '笔记本性能好,质量好!!!!!', null, '2015-02-09 13:22:57'); INSERT INTO `items` VALUES ('3', '背包', '200.0', '名牌背包,容量大质量好!!!!', null, '2015-02-06 13:23:02'); -- ---------------------------- -- Table structure for `orderdetail` -- ---------------------------- DROP TABLE IF EXISTS `orderdetail`; CREATE TABLE `orderdetail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `orders_id` int(11) NOT NULL COMMENT '订单id', `items_id` int(11) NOT NULL COMMENT '商品id', `items_num` int(11) DEFAULT NULL COMMENT '商品购买数量', PRIMARY KEY (`id`), KEY `FK_orderdetail_1` (`orders_id`), KEY `FK_orderdetail_2` (`items_id`), CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of orderdetail -- ---------------------------- INSERT INTO `orderdetail` VALUES ('1', '1', '2', '3'); INSERT INTO `orderdetail` VALUES ('2', '2', '3', '5'); INSERT INTO `orderdetail` VALUES ('3', '2', '2', '3'); -- ---------------------------- -- Table structure for `orders` -- ---------------------------- DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '下单用户id', `number` varchar(32) NOT NULL COMMENT '订单号', `createtime` datetime NOT NULL COMMENT '创建订单时间', `note` varchar(100) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`), KEY `FK_orders_1` (`user_id`), CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of orders -- ---------------------------- INSERT INTO `orders` VALUES ('1', '10', '11', '2019-09-18 09:41:28', '33'); INSERT INTO `orders` VALUES ('2', '1', '10', '2019-11-20 16:48:52', '44'); -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性别', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '张晓敏', null, '2', null); INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '北京市'); INSERT INTO `user` VALUES ('16', '张小明', null, '1', '河南郑州'); INSERT INTO `user` VALUES ('22', '陈小明', null, '1', '河南郑州'); INSERT INTO `user` VALUES ('24', '张三丰', null, '1', '河南郑州'); INSERT INTO `user` VALUES ('25', '赵文迪', '2019-09-10', '女', '贵州遵义'); INSERT INTO `user` VALUES ('27', '赵文迪', '2019-09-09', '女', '贵州遵义'); INSERT INTO `user` VALUES ('28', '赵文迪', '2019-09-09', '女', '贵州遵义'); INSERT INTO `user` VALUES ('29', '赵文迪', '2019-09-09', '女', '贵州遵义');
-
创建web项目:jdk1.8+tomcat8.0
-
导入spring的jar包:需要spring4.3所有jar(一定包括spring-webmvc-4.3.0.RELEASE.jar)
2.2、配置前端控制器
在类路径下添加文件:springmvc.xml
在web.xml文件中配置前端控制器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>01.springmvc入门程序</display-name>
<!-- 配置springMVC前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--contextConfigLocation:
配置springMVC加载的配置文件
如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
第一种:*.action:访问一.action结尾由DispatcherServlet进行解析
第二种:/:所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析。
使用此种方式可以实现RESTFUL风格的url.
第三种:/*:这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- 配置springMVC前端控制器 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
2.3、配置处理其适配器
在类路径下的springmvc.xml
中配置处理器适配器。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置处理器适配器:所有的处理器适配器都实现HandlerAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 配置处理器映射器 -->
<!-- 视图解析器 -->
</beans>
SimpleControllerHandlerAdapter:
支持实现Controller
的Handler。
2.5、编写Handler
编写一个实现Controller
接口的Handler。ItemsController1.java
package com.zwd.ssm.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.zwd.ssm.po.Items;
/**
* 实现Controller接口的处理器
* @author Administrator
*
*/
public class ItemsController1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("进来。");
//调用service
List<Items> itemsList = new ArrayList<>();
Items item1 = new Items();
item1.setId(1);
item1.setName("aaa");
item1.setPrice(777);
Items item2 = new Items();
item1.setId(2);
item1.setName("BBB");
item1.setPrice(888);
itemsList.add(item1);
itemsList.add(item2);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当于request的setAttribute,在jsp页面中通过itemsList获取数据
modelAndView.addObject("itemsList", itemsList);
//指定视图
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
}
2.6 、编写前端页面jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>查询商品列表</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/item/queryItem.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td><input type="submit" value="查询"/></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item">
<tr>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>
<td><a href="${pageContext.request.contextPath }/item/editItem.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
2.7、配置处理器映射器
在类路径下的springmvc.xml
中配置:
<!-- 2、配置处理器映射器 -->
<!-- BeanNameUrlHandlerMapping:
将bean的name作为url进行查找,需要配置Handler时指定beanName(就是url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 配置Handler -->
<bean name="/queryItems.action" class="com.zwd.ssm.controller.ItemsController1"></bean>
2.8、配置视图解析器
在springmvc.xml
中配置视图解析器。
<!-- 3、视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
整个springmvc.xml
的配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1、配置处理器适配器:所有的处理器适配器都实现HandlerAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 2、配置处理器映射器 -->
<!-- BeanNameUrlHandlerMapping:
将bean的name作为url进行查找,需要配置Handler时指定beanName(就是url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 配置Handler -->
<bean name="/queryItems.action" class="com.zwd.ssm.controller.ItemsController1"></bean>
<!-- 3、视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
</beans>
2.9、访问页面
http://localhost:8080/01.springmvcFirst/queryItems.action
3、非注解的处理器映射器和适配器
3.1、非注解的处理器映射器
类型1:BeanNameUrlHandlerMapping
<!-- 类型1:BeanNameUrlHandlerMapping:
将bean的name作为url进行查找,需要配置Handler时指定beanName(就是url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> </bean>
<!-- 配置Handler -->
<bean name="/queryItems.action" class="com.zwd.ssm.controller.ItemsController1"> </bean>
类型2: SimpleUrlHandlerMapping
<!-- 类型2:SimpleUrlHandlerMapping -->
<bean id="itemsController" class="com.zwd.ssm.controller.ItemsController1"></bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/queryItems1.action">itemsController</prop>
<prop key="/queryItems2.action">itemsController</prop>
</props>
</property>
</bean>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1、配置处理器适配器:所有的处理器适配器都实现HandlerAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 2、配置处理器映射器:两种类型可以并存 -->
<!-- 类型1:BeanNameUrlHandlerMapping:
将bean的name作为url进行查找,需要配置Handler时指定beanName(就是url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 配置Handler -->
<bean id="/queryItems.action" class="com.zwd.ssm.controller.ItemsController1"></bean>
<!-- 类型2:SimpleUrlHandlerMapping -->
<bean id="itemsController" class="com.zwd.ssm.controller.ItemsController1"></bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/queryItems1.action">itemsController</prop>
<prop key="/queryItems2.action">itemsController</prop>
</props>
</property>
</bean>
<!-- 3、视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
</beans>
前端映射器会找到对应url使用的处理器映射器,能使用哪个就使用哪个。
3.2、非注解的适配器
类型1:SimpleControllerHandlerAdapter
<!-- 类型1:要求:需要编写的Handler实现Controller接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
package com.zwd.ssm.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.zwd.ssm.po.Items;
/**
* 实现Controller接口的处理器
* @author Administrator
*
*/
public class ItemsController1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//调用service
List<Items> itemsList = new ArrayList<>();
Items item1 = new Items();
item1.setId(1);
item1.setName("aaa");
item1.setPrice(777);
Items item2 = new Items();
item1.setId(2);
item1.setName("BBB");
item1.setPrice(888);
Items item3 = new Items();
item1.setId(3);
item1.setName("ccc");
item1.setPrice(999);
itemsList.add(item1);
itemsList.add(item2);
itemsList.add(item3);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当于request的setAttribute,在jsp页面中通过itemsList获取数据
modelAndView.addObject("itemsList", itemsList);
//指定视图
modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
return modelAndView;
}
}
类型2:HttpRequestHandlerAdapter
<!-- 类型2:要求:需要编写的Handler实现HttpRequestHandler接口 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">
</bean>
package com.zwd.ssm.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.HttpRequestHandler;
import com.zwd.ssm.po.Items;
/**
* 实现HttpRequestHandler的接口
* @author Administrator
*
*/
public class ItemsController2 implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//调用service
List<Items> itemsList = new ArrayList<>();
Items item1 = new Items();
item1.setId(1);
item1.setName("aaa");
item1.setPrice(777);
Items item2 = new Items();
item1.setId(2);
item1.setName("BBB");
item1.setPrice(888);
Items item3 = new Items();
item1.setId(3);
item1.setName("ccc");
item1.setPrice(999);
itemsList.add(item1);
itemsList.add(item2);
itemsList.add(item3);
//设置模型数据
request.setAttribute("itemsList", itemsList);
//设置转发视图
request.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(request, response);
//使用此方法可以通过修改response,设置响应的数据格式,比如响应json数据
// response.setCharacterEncoding("utf-8");
// response.setContentType("application/json;charset=utf-8");
// response.getWriter().write("json串");
}
}
3.3、DispatcherServlet.properties
前端控制器从上边的文件中加载处理器映射器、适配器、视图解析器等组件,如果不在springmvc.xml
中配置,则使用默认加载的。
4、注解的处理器映射器和适配器
4.1、注解处理器映射器
3.1之前使用:
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
3.1之后使用:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
4.2、注解适配器
3.1之前使用:
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
3.1之后使用:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
4.3、配置注解映射器和适配器
记得添加mvc约束。
<!-- 注解的适配器和映射器的配置 -->
<!-- 方式一:单独配置两个 -->
<!-- 1、配置注解的处理器适配器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
<!-- 2、配置注解的处理器映射器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
<!-- 方式二:使用mvc:annotation-driven代理方式一。
mvc:annotation-driven:默认加载很多的参数绑定方法。比如json转换解析器就默认加载了。
实际开发使用mvc:annotation-driven-->
<mvc:annotation-driven></mvc:annotation-driven>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 注解的适配器和映射器的配置 -->
<!-- 方式一:单独配置两个 -->
<!-- 1、配置注解的处理器适配器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
<!-- 2、配置注解的处理器映射器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
<!-- 方式二:使用mvc:annotation-driven代理方式一。
mvc:annotation-driven:默认加载很多的参数绑定方法。比如json转换解析器就默认加载了。
实际开发使用mvc:annotation-driven-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
</beans>
4.4、注解Handler编写
@Controller、@RequestMapping
@Controller:使用@Controller注解标识该类是一个控制器。
@RequestMapping:使用@RequestMapping实现handleRequest方法和url的映射,一个方法对应一个url
注解适配器和注解映射器需要配套使用。
ItemsController3.java
package com.zwd.ssm.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import com.zwd.ssm.po.Items;
/**
* 使用注解的映射器、适配器的Controller
* @author Administrator
*
*/
//处理器适配器:使用@Controller注解标识该类是一个控制器
@Controller
public class ItemsController3{
//处理器映射器注解:使用@RequestMapping实现handleRequest方法和url的映射,一个方法对应一个url
//一般建议url和方法名称一致。
@RequestMapping("/queryItems")
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//调用service
List<Items> itemsList = new ArrayList<>();
Items item1 = new Items();
item1.setId(1);
item1.setName("aaa");
item1.setPrice(777);
Items item2 = new Items();
item1.setId(2);
item1.setName("BBB");
item1.setPrice(888);
Items item3 = new Items();
item1.setId(3);
item1.setName("ccc");
item1.setPrice(999);
itemsList.add(item1);
itemsList.add(item2);
itemsList.add(item3);
//设置模型数据
request.setAttribute("itemsList", itemsList);
//设置转发视图
request.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(request, response);
//使用此方法可以通过修改response,设置响应的数据格式,比如响应json数据
// response.setCharacterEncoding("utf-8");
// response.setContentType("application/json;charset=utf-8");
// response.getWriter().write("json串");
}
//其它方法
}
4.5、将Handler注入aop
<!-- 配置Controller -->
<!-- <bean class="com.zwd.ssm.controller.ItemsController3"></bean> 或者直接扫描注解 -->
<!-- 扫描注解: -->
<context:component-scan base-package="com.zwd.ssm.controller"></context:component-scan>
4.6、访问调试
http://localhost:8080/03.HandlerMappingAndAdapter_anno/queryItems.action
5、源码分析
分析springmvc的执行过程。
第一步:前端控制器接受请求
调用doDispatch()
第二步:前端控制器调用处理器映射器。根据url查找handler
第三步:根据handler查找对应的处理器适配器,并调用该适配器执行该handler返回ModelAndView
第四步:视图渲染。将model数据填充到request域。
视图解析得到view。
6、入门程序小结
前端控制器
<!-- 配置springMVC前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--contextConfigLocation:
配置springMVC加载的配置文件
如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml) -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
第一种:*.action:访问一.action结尾由DispatcherServlet进行解析
第二种:/:所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析。
使用此种方式可以实现RESTFUL风格的url.
第三种:/*:这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
处理器映射器
非注解的处理器映射器(两种):BeanNameUrlHandlerMapping
、SimpleUrlHandlerMapping
<!-- 类型1:BeanNameUrlHandlerMapping:
将bean的name作为url进行查找,需要配置Handler时指定beanName(就是url) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> </bean>
<!-- 配置Handler -->
<bean name="/queryItems.action" class="com.zwd.ssm.controller.ItemsController1"> </bean>
<!-- 类型2:SimpleUrlHandlerMapping -->
<bean id="itemsController" class="com.zwd.ssm.controller.ItemsController1"></bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/queryItems1.action">itemsController</prop>
<prop key="/queryItems2.action">itemsController</prop>
</props>
</property>
</bean>
处理器适配器
非注解的处理器适配器(两种):SimpleControllerHandlerAdapter
、HttpRequestHandlerAdapter
<!-- 类型1:要求:需要编写的Handler实现Controller接口 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 类型2:要求:需要编写的Handler实现HttpRequestHandler接口 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">
</bean>
注解的处理器映射器和适配器
<!-- 注解的适配器和映射器的配置 -->
<!-- 方式一:单独配置两个 -->
<!-- 1、配置注解的处理器适配器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
<!-- 2、配置注解的处理器映射器: -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
<!-- 方式二:使用mvc:annotation-driven代理方式一。
mvc:annotation-driven:默认加载很多的参数绑定方法。比如json转换解析器就默认加载了。
实际开发使用mvc:annotation-driven-->
<mvc:annotation-driven></mvc:annotation-driven>
视图解析器
<!--视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/items"></property><!-- 前缀 -->
<property name="suffix" value=".jsp"></property><!-- 后缀 -->
</bean>
//在视图解析器中配置前缀和后缀
//<property name="prefix" value="/WEB-INF/jsp/"></property><!-- 前缀 -->
//<property name="suffix" value=".jsp"></property><!-- 后缀 -->
request.getRequestDispatcher("items/itemsList").forward(request, response);
7、springMVC和mybatis整合
7.1、需求
使用springMVC和mybatis完成商品列表的查询。
7.2、整合思路
springmvc和mybatis的系统架构
第一步:整合dao层
mybatis和spring整合,通过spring管理mapper接口。
使用mapper的扫描器自动扫描mapper接口在spring中的注册。MapperScannerConfigurer
第二步:整合service层
通过spring管理service接口。
通过配置方式将service接口配置在spring配置文件中。
实现事务控制(xml配置)。
第三步:整合springmvc的模块,不需要整合。
7.3、准备环境
-
创建web项目
-
导包:数据库连接驱动+数据库连接池+mybatis的jar包+mybatis和spring整合jar包+日志包+spring的jar包+jstl。
-
在类路径下创建日志文件:
log4j.properties
# Global logging configuration # 在开发环境下使用DEBUG;在生产环境下使用info或者error log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
-
在类路径下创建数据库配置文件:
db.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=root
7.4、整合dao
sqlMapConfig.xml
-
创建
sqlMapConfig.xml
文件:config->mybatis->sqlMapConfig.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 1、配置全局settings:根据需要配置 --> <!-- 2、配置别名 --> <typeAliases> <!-- 批量扫描别名 --> <package name="com.zwd.ssm.po"/> <!-- 单个别名定义 <typeAlias type="" alias=""/>--> </typeAliases> <!--3、配置mapper: 由于使用spring和mybatis的整合包进行mapper扫描,这里就不需要配置了(MapperScannerConfigurer)。 必须遵循:mapper.xml和mapper.java文件同名且在一个目录 --> <!-- <mappers></mappers> --> </configuration>
applicationContext_dao.xml
mapper批量扫描器:MapperScannerConfigurer
-
创建
applicationContext.xml
文件:config->mybatis->applicationContext.xml<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 1、配置数据库的连接 --> <!-- 加载数据库配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 通过数据库连接池dpcp连接和管理数据库 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="maxActive" value="30"></property> <property name="maxIdle" value="5"></property> </bean> <!-- 2、配置mybatis的SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <!-- 配置mybatis的全局配置文件sqlMapConfig.xml --> <property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"></property> </bean> <!-- 3、配置扫描mapper生成dao的实现类:在此处配置就不用在sqlMapConfig.xml文件中配置mapper扫描 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 扫描包下的mapper,如果需要扫描多个包使用逗号分隔开 --> <property name="basePackage" value="com.zwd.ssm.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> </beans>
逆向工程生成po类和mapper
手动定义商品查询mapper
针对综合查询mapper,一般情况会有相关关联查询,建议自定义mapper。
定义Items.java
的扩展类:ItemsCusotm.java
package com.zwd.ssm.po;
/**
* 商品类的扩展类
* @author Administrator
*
*/
public class ItemsCustom extends Items {
//扩展属性
}
定义Items.java
的包装类(包装了查询条件):ItemsVo.java
package com.zwd.ssm.po;
/**
* 商品包装对象
* @author Administrator
*
*/
public class ItemsVo {
//包装查询条件
private ItemsCustom items ;
public ItemsCustom getItemsCustom() {
return items;
}
public void setItemsCustom(ItemsCustom items) {
this.items = items;
}
}
ItemsMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zwd.ssm.mapper.ItemsMapper" >
<sql id="itemsList_where">
<if test="items != null">
<if test="items.name != null and items.name != ''">
name like '%${items.name}%'
</if>
</if>
</sql>
<!-- 查询商品信息:
parameterType:传入包装对象(包装了查询条件)
resultType:建议使用扩展对象
-->
<select id="findItemsList" parameterType="com.zwd.ssm.po.ItemsVo" resultType="com.zwd.ssm.po.ItemsCustom">
select * from items
<where>
<include refid="itemsList_where"></include>
</where>
</select>
</mapper>
ItemsMapper.java
package com.zwd.ssm.mapper;
import java.util.List;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.po.ItemsVo;
public interface ItemsMapper {
/**
* 查询商品信息
* @param itemsVo
* @return
*/
public List<ItemsCustom> findItemsList(ItemsVo itemsVo) throws Exception;
}
7.5、整合service
ItemsService接口和实现类
ItemsService.java
package com.zwd.ssm.service;
import java.util.List;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.po.ItemsVo;
/**
* 商品信息的service层接口
* @author Administrator
*
*/
public interface ItemsService {
/**
* 查询商品信息
* @param itemsVo
* @return
* @throws Exception
*/
public List<ItemsCustom> findItemsList(ItemsVo itemsVo) throws Exception;
}
ItemsServiceImpl.java
package com.zwd.ssm.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.zwd.ssm.mapper.ItemsMapper;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.po.ItemsVo;
import com.zwd.ssm.service.ItemsService;
/**
* 商品信息service层实现类
* @author Administrator
*
*/
//@Service
public class ItemsServiceImpl implements ItemsService {
@Resource(name="itemsMapper")
private ItemsMapper itemsMapper;
@Override
public List<ItemsCustom> findItemsList(ItemsVo itemsVo) throws Exception {
return itemsMapper.findItemsList(itemsVo);
}
}
使用xml配置管理service
applicationContext_service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置service -->
<bean id="itemsService" class="com.zwd.ssm.service.impl.ItemsServiceImpl"></bean>
</beans>
使用xml配置管理事务
applicationContext_tx.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置事务控制 -->
<!-- 1、配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2、配置事务的通知 -->
<tx:advice id="tx_Advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 3、配置aop -->
<aop:config>
<aop:pointcut expression="execution(* com.zwd.ssm.servie.impl.*.*(..))" id="cut1"/>
<aop:advisor advice-ref="tx_Advice" pointcut-ref="cut1"/>
</aop:config>
</beans>
7.6、整合MVC
配置前端控制器web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>04.ssm</display-name>
<!-- 配置springMVC前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--contextConfigLocation:
配置springMVC加载的配置文件
如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml) -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
第一种:*.action:访问一.action结尾由DispatcherServlet进行解析
第二种:/:所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析。
使用此种方式可以实现RESTFUL风格的url.
第三种:/*:这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
配置springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1、扫描注解 -->
<context:component-scan base-package="com.zwd.ssm.controller"></context:component-scan>
<!-- 2、配置注解的处理器适配器和映射器 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 3、配置视图解析器 -->
<!-- InternalResourceViewResolver:解析jsp,默认使用jstl标签。注意导入jstl的包 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/items"></property><!-- 前缀 -->
<property name="suffix" value=".jsp"></property><!-- 后缀 -->
</bean>
</beans>
jsp页面itemsList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>查询商品列表</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/queryItem.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td><input type="submit" value="查询"/></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item">
<tr>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>
<td><a href="${pageContext.request.contextPath }/editItem.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
编写Controller
package com.zwd.ssm.controller;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.service.ItemsService;
/**
* 商品信息Controller层
*
* @author Administrator
*
*/
@Controller
public class ItemsController {
@Resource(name = "itemsService")
private ItemsService itemsService;
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception {
System.out.println("成功!");
// 调用service
List<ItemsCustom> itemsList = itemsService.findItemsList(null);
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当于request的setAttribute,在jsp页面中通过itemsList获取数据
modelAndView.addObject("itemsList", itemsList);
// 指定视图
modelAndView.setViewName("/itemsList");
return modelAndView;
}
}
配置spring容器的监听器web.xml
<!-- 加载spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/applicationContext_*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>04.ssm</display-name>
<!-- 加载spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/applicationContext_*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置springMVC前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--contextConfigLocation:
配置springMVC加载的配置文件
如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml) -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
第一种:*.action:访问一.action结尾由DispatcherServlet进行解析
第二种:/:所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析。
使用此种方式可以实现RESTFUL风格的url.
第三种:/*:这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
7.7、部署访问
http://localhost:8080/04.ssm/queryItems.action
8、修改商品信息功能开发
使用上一个例子(7)的环境
8.1、需求
操作流程:
1、进入商品查询列表页面(jsp/items/itemsList.jsp
)
2、点击修改,进入商品修改页面(jsp/items/editItems.jsp
),页面中显示了要修改的商品(从数据库中查询)
要修改的商品从数据库中查询,根据商品id(主键)查询商品信息。
3、在商品修改页面,修改商品信息,修改后,点击提交。页面跳转到jsp/success.jsp
8.2、mapper开发
mapper:使用逆向工程中的方法。
1、根据id查询商品信息。selectByPrimaryKey()
2、根据id更新items表的数据。updateByPrimaryKeyWithBLOBs()
8.3、service开发
service:
1、根据id查询商品信息
2、修改商品信息
ItemsService.java
package com.zwd.ssm.service;
import java.util.List;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.po.ItemsVo;
/**
* 商品信息的service层接口
* @author Administrator
*
*/
public interface ItemsService {
/**
* 查询商品信息
* @param itemsVo
* @return
* @throws Exception
*/
public List<ItemsCustom> findItemsList(ItemsVo itemsVo) throws Exception;
/**
* 根据id查询商品信息
* @param id
* @return
*/
public ItemsCustom findItemsById(Integer id);
/**
* 根据id更新商品信息
* @param itemsId
* @param items
*/
public void updateItemsById(Integer itemsId,ItemsCustom itemsCustom);
}
ItemsServiceImpl.java
package com.zwd.ssm.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zwd.ssm.mapper.ItemsMapper;
import com.zwd.ssm.mapper.ItemsMapperExample;
import com.zwd.ssm.po.Items;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.po.ItemsVo;
import com.zwd.ssm.service.ItemsService;
/**
* 商品信息service层实现类
* @author Administrator
*
*/
@Service("itemsService")
public class ItemsServiceImpl implements ItemsService {
@Resource(name="itemsMapper")
private ItemsMapper itemsMapper;
@Autowired
private ItemsMapperExample ItemsMapperExample;
/**
* 查询商品信息
*/
@Override
public List<ItemsCustom> findItemsList(ItemsVo itemsVo) throws Exception {
return itemsMapper.findItemsList(itemsVo);
}
/**
* 查询商品信息
*/
@Override
public ItemsCustom findItemsById(Integer id) {
Items items = ItemsMapperExample.selectByPrimaryKey(id);
//中间对商品进行业务处理
//...
//将所有信息封装到items的扩展类ItemsCustom中
ItemsCustom itemsCustom = new ItemsCustom();
//将items的属性值拷贝到itemsCustom中
BeanUtils.copyProperties(items, itemsCustom);
return itemsCustom;
}
/**
* 查询商品信息
*/
@Override
public void updateItemsById(Integer itemsId, ItemsCustom itemsCustom) {
//添加业务校验,通常在service中对关键参数进行校验
//校验id是否为空,如果为空抛出异常。
itemsCustom.setId(itemsId);
ItemsMapperExample.updateByPrimaryKeyWithBLOBs(itemsCustom);
}
}
8.4、controller开发
方法:
1、商品信息修改页面显示。
2、商品信息修改提交。
商品信息页面和提交成功后的页面
editItems.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>修改商品信息</title>
</head>
<body>
<form id="itemForm" action="${pageContext.request.contextPath }/editItemsSubmit.action" method="post" >
<input type="hidden" name="id" value="${itemsCustom.id }"/>
修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${itemsCustom.name }"/></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${itemsCustom.price }"/></td>
</tr>
<tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime" value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
</tr>
<%-- <tr>
<td>商品图片</td>
<td>
<c:if test="${item.pic !=null}">
<img src="/pic/${item.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</td>
</tr> --%>
<tr>
<td>商品简介</td>
<td>
<textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="ISO-8859-1"%>
<!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>成功提示</title>
</head>
<body>
哈哈
</body>
</html>
ItemsController
没有实现页面之间的数据传送。
package com.zwd.ssm.controller;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.service.ItemsService;
/**
* 商品信息Controller层
*
* @author Administrator
*
*/
@Controller
public class ItemsController {
@Resource(name = "itemsService")
private ItemsService itemsService;
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception {
System.out.println("成功!");
// 调用service
List<ItemsCustom> itemsList = itemsService.findItemsList(null);
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 相当于request的setAttribute,在jsp页面中通过itemsList获取数据
modelAndView.addObject("itemsList", itemsList);
// 指定视图
modelAndView.setViewName("items/itemsList");
return modelAndView;
}
/**
* 修改商品信息页面展示
* @return
* @throws Exception
*/
@RequestMapping("/editItems")
public ModelAndView editItems() throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(1);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsCustom", itemsCustom);
modelAndView.setViewName("items/editItems");
return modelAndView;
}
/**
* 修改提交商品信息
* @return
* @throws Exception
*/
@RequestMapping("/editItemsSubmit")
public ModelAndView editItemsSubmit() throws Exception{
//获取提交的items,更新到数据库
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
return modelAndView;
}
}
8.6、部署访问
http://localhost:8080/05.updateItemsInfo/queryItems.action
9、@RequestMapping
使用上一个例子(8)的环境
url映射
定义controller方法对应的url,进行处理器映射使用。
窄化请求映射
限制http请求方式
10、Controller方法返回值
返回ModelAndView
需要在方法结束时定义ModelAndView,将model和view分别进行设置。
/**
* 修改商品信息页面展示
* @return
* @throws Exception
*/
@RequestMapping("/editItems")
public ModelAndView editItems() throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(1);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsCustom", itemsCustom);
modelAndView.setViewName("items/editItems");
return modelAndView;
}
返回string
-
表示返回逻辑视图名:真正视图(jsp路径)=前缀+逻辑视图名+后缀。
/** * 修改商品信息页面展示:返回值string * @return * @throws Exception */ @RequestMapping("/editItems") public String editItems(Model model) throws Exception{ ItemsCustom itemsCustom = itemsService.findItemsById(1); //Model:通过形参中的model将model数据传到页面 //相当于modelAndView.addObject("itemsCustom", itemsCustom)方法 model.addAttribute("itemsCustom", itemsCustom); return "items/editItems"; }
-
redirect重定向
商品修改提交后,重定向到商品查询列表。
redirect重定向特点:浏览器地址栏中的url会变化。
修改提交的request数据无法传到重定向的地址。因为重定向后重新发送一个请求,
request无法共享。
//重定向 return "redirect:queryItems.action";
-
forword页面转发
通过forward进行页面转发,浏览器地址栏url不变,request可以共享。
//请求转发 return "forward:queryItems.action";
返回void
在controller方法形参上可以定义request和response,使用request或response指定响应结果:
1、使用request转向页面,如下:
request.getRequestDispatcher("页面路径").forward(request, response);
2、也可以通过response页面重定向:
response.sendRedirect("url")
3、也可以通过response指定响应结果,例如响应json数据如下:
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
11、参数绑定
11.1、参数绑定过程
客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上。
springMVC中,接收页面提交的数据时通过方法形参来接收的。而不是在controller类中定义成员变量接收。
11.2、默认支持的类型
直接在controller方法形参上定义下边类型的对象,就可以使用这些对象。在参数绑定过程中,如果遇到下边类型直接进行绑定。
HttpServletRequest
通过request对象获取请求信息。
HttpServletResponse
通过response处理响应信息。
HttpSession
通过session对象得到session中存放的对象。
Model/ModelMap
model是一个接口,modelMap是一个接口实现。
作用:将model数据填充到request域。
11.3、简单类型
@RequestParam
通过@RequestParam对简单类型的参数进行绑定。
如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致。
//参数绑定:@RequestParam里面将request传入的参数名称和形参进行绑定。
//value:标签的name属性值
//required:该参数是否必须传入
//defaultValue:默认值
public ModelAndView editItems(@RequestParam(value="id",required=true,defaultValue="1") int id)
/**
* 修改商品信息页面展示
* @return
* @throws Exception
*/
@RequestMapping("/editItems")
//参数绑定:@RequestParam里面将request传入的参数名称和形参进行绑定。
//value:标签的name属性值
//required:该参数是否必须传入
//defaultValue:默认值
public ModelAndView editItems(@RequestParam(value="id",required=true,defaultValue="1") int id) throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(id);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsCustom", itemsCustom);
modelAndView.setViewName("items/editItems");
return modelAndView;
}
11.4、pojo绑定
要求:页面中input的name值和controller形参中的pojo的属性名称一致即可完成绑定。
/**
* 修改提交商品信息:pojo参数绑定
* @return
* @throws Exception
*/
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(Integer id,ItemsCustom itemsCustom) throws Exception{
//获取提交的items,更新到数据库
itemsService.updateItemsById(id, itemsCustom);
//重定向
//return "redirect:queryItems.action";
//请求转发
return "forward:queryItems.action";
}
页面input的name值:
形参ItemsCustom中的属性名称:
11.5、自定义参数类型绑定实现日期类型绑定
对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。将请求日期数据串传成 日期类型,要转换的日期类型和pojo中日期属性的类型保持一致。
自定义日期类型转换
需要实现Converter
类中的convert()
方法。
package com.zwd.ssm.controller.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class DateConverter implements Converter<String, Date> {
//实现将日期串转为Date日期类型
@Override
public Date convert(String str) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
在springmvc中配置添加转换器:方式一
<!-- 1、配置注解的适配器和映射器 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
FormattingConversionServiceFactoryBean
<!-- 4、自定义参数类型绑定 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 转换器 -->
<property name="converters">
<list>
<!-- 日期类型转换 -->
<bean class="com.zwd.ssm.controller.converter.DateConverter"></bean>
</list>
</property>
</bean>
在springmvc中配置添加转换器:方式二(自学)
<!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="customBinder"></property>
</bean>
<!-- 自定义webBinder -->
<bean id="customBinder"
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService" />
</bean>
<!-- conversionService -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 转换器 -->
<property name="converters">
<list>
<bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/>
</list>
</property>
</bean>
问题
post乱码
在web.xml添加post乱码filter
在web.xml中加入:
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
get乱码
get乱码原因:tomcat编码和文件编码不一致。
对于get请求中文参数出现乱码解决方法有两个:
1、修改tomcat配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
2、另外一种方法对参数进行重新编码:
String userName = new
String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
springMVC和struts的区别
1、springMVC是基于方法开发的,struts2是基于类开发的。
springMVC将url和controller方法映射。映射成功后springMVC生成一个Handler对象,对象中值包括了一个method。方法执行结束,形参数据销毁。
2、springMVC可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。
3、经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts2建议使用jstl。
第二天
课程安排:
在商品查询和商品修改功能案例驱动下进行学习:
包装类型pojo参数绑定
集合类型的参数绑定:数组、list、map...
商品修改添加校验,学习springMVC提供校验validation(使用的是hibernate校验框架)
数据回显
统一异常处理
上传图片
json数据交互
restful支持
拦截器
1、包装类型pojo参数绑定
1.1、需求
商品查询controller方法中实现商品查询条件传入。
1.2、实现方法
- 第一种方法:在形参中添加
HttpServletRequest
request参数,通过接受查询条件参数。 - 第二种方法:在形参中让包装类型的pojo接受查询条件参数。
1.3、包装类型pojo参数绑定
-
页面参数:
商品名称: input name="itemsCustom.name "
<form action="${pageContext.request.contextPath }/items/queryItems.action" method="post"> 查询条件: <table width="100%" border=1> <tr> <td>商品名称:<input name="itemsCustom.name"/></td> <td><input type="submit" value="查询"/></td> </tr> </table>
-
包装类型pojo: ItemsCustom itemsCustom;
注意set和get方法要写!!
package com.zwd.ssm.po; /** * 商品信息包装类 * @author Administrator * */ public class ItemsQueryVo { ItemsCustom itemsCustom; public ItemsCustom getItemsCustom() { return itemsCustom; } public void setItemsCustom(ItemsCustom itemsCustom) { this.itemsCustom = itemsCustom; } }
2、集合类型的参数绑定
2.1、数组
需求
在商品展示页面(itemsList.jsp)批量删除商品。
页面代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>查询商品列表</title>
<script type="text/javascript">
function deleteItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath}/items/deleteItems.action";
document.itemsForm.submit();
}
function queryItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
document.itemsForm.submit();
}
</script>
</head>
<body>
<form
name="itemsForm"
action="${pageContext.request.contextPath }/items/queryItems.action"
method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>商品名称:<input name="itemsCustom.name" /></td>
<td><input type="button" value="查询" οnclick="queryItems()"/>
<input type="button" value="批量删除" οnclick="deleteItems()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>选择</td>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList}" var="item">
<tr>
<td><input type="checkbox" name="items_id" value="${item.id}"></input></td>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}"
pattern="yyyy-MM-dd HH:mm:ss" /></td>
<td>${item.detail }</td>
<td><a
href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
表现层实现
关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
形参名称与标签的name值一致
controller方法定义:
页面定义:
2.2、List
需求
通常在需要批量提交数据时,将提交的数据绑定到List<pojo>
中,比如:成绩录入(录入多门课的成绩,然后批量提交)。
该例子需求:批量商品修改。修改多个商品信息,将多个商品信息一起提交到controller。
页面代码:editItemsQuery.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>查询商品列表</title>
<script type="text/javascript">
function editItemsAllSubmit(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath}/items/editItemsAllSubmit.action";
document.itemsForm.submit();
}
function queryItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
document.itemsForm.submit();
}
</script>
</head>
<body>
<form
name="itemsForm"
action="${pageContext.request.contextPath }/items/queryItems.action"
method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>商品名称:<input name="itemsCustom.name" /></td>
<td><input type="button" value="查询" οnclick="queryItems()"/>
<input type="button" value="批量提交" οnclick="editItemsAllSubmit()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
</tr>
<c:forEach items="${itemsList}" var="item" varStatus="status">
<tr>
<td><input name="itemsList[${status.index}].name" value="${item.name}"></input></td>
<td><input name="itemsList[${status.index}].price" value="${item.price}"></input></td>
<td><input name="itemsList[${status.index}].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"></input></td>
<td><input name="itemsList[${status.index}].detail" value="${item.detail}"></input></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
表现层实现
使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list属性。
展示修改信息页面的controller:
/**
* 5.批量修改商品信息页面:
* @return
*/
@RequestMapping(value="/editItemsQuery",method={RequestMethod.GET,RequestMethod.POST})
public ModelAndView editItemsQuery(ItemsQueryVo itemsQueryVo){
List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/editItemsQuery");
return modelAndView;
}
点击批量提交后的controller:
/**
* 6、批量修改后的提交页面(批量提交):list参数绑定
* @param itemsQueryVo
* @return
*/
@RequestMapping(value="/editItemsAllSubmit")
public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo){
return "redirect:editItemsQuery.action";
}
形参类型:需要一个list集合来接收批量提交的数据。
页面定义:
2.3、map
也通过在包装pojo中定义map类型属性。
在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。
包装类中定义Map对象如下:
Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
//get/set方法..
}
页面定义如下:
<tr>
<td>学生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo['name']"/>
年龄:<inputtype="text"name="itemInfo['price']"/>
.. .. ..
</td>
</tr>
Contrller方法定义如下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}
3、服务端校验
3.1、校验理解
项目中,通常使用较多的是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:
控制层controller:校验页面请求的参数的合法性。在服务端控制层controller校验,不区分客户端类型(浏览器,手机客户端,远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。
3.2、springmvc校验
springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
校验思路:
页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
具体需求:商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。
3.3、环境准备
- 添加校验的jar包:
3.4、添加校验配置文件:CustomValidationMessages.properties
3.5、配置校验器:springmvc.xml
<!-- 5、配置校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
<!-- 指定校验使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 校验错误信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名 -->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 资源文件编码格式 -->
<!--<property name="fileEncodings" value="utf-8"></property>错误配置-->
<property name="defaultEncoding" value="utf-8"></property>
<!-- 资源文件内容缓存时间 ,单位秒-->
<property name="cacheSeconds" value="120"></property>
</bean>
3.6、将校验器配置到处理器适配器中:springmvc.xml
配置方式1:
validator="validator"
<!-- 1、配置注解的处理器适配器和映射器 -->
<mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven>
配置方式2(自学):
<!-- 自定义webBinder -->
<bean id="customBinder"
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator" />
</bean>
<!-- 注解适配器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="customBinder"></property>
</bean>
3.7、添加校验规则items.java
items.java
//message:错误输入的提示信息
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
private Float price;
private String pic;
@NotNull(message="{items.createtime.isNull}")
private Date createtime;
3.8、错误消息文件配置CustomValidationMessages
#配置错误提示信息
items.name.length.error=商品名称在1到30个字符之间
items.createtime.isNull=请输入商品的日期
3.9、在controller中捕获校验信息
**捕获校验信息:**在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult的形参,接收校验出错信息。
注意:@Validated和BindingResult形参是配对出现的,并且顺序是一前一后。
@Validated ItemsCustom itemsCustom,BindingResult bindingResult
/**
* 7、点击提交后的操作:信息校验
* @param id
* @param itemsCustom
* @return
*/
//校验信息:在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult的形参,接收校验出错信息。
//注意:@Validated和BindingResult形参是配对出现的,并且顺序是一前一后。
@RequestMapping(value="/editItemsSubmit",method={RequestMethod.POST,RequestMethod.GET})
public String editItemsSubmit(Integer id,
@Validated ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception{
//获取校验错误信息
if(bindingResult.hasErrors()){
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError objectError : allErrors) {
System.out.println(objectError.getDefaultMessage());
}
}
//获取提交的items,更新到数据库
itemsService.updateItems(id, itemsCustom);
return "forward:queryItems.action";
}
3.10、在页面中显示错误信息
-
将错误信息传到页面:
List<ObjectError> allErrors = bindingResult.getAllErrors(); //将错误信息传导页面 model.addAttribute("allErrors", "allErrors");
/** * 7、点击提交后的操作:信息校验 * @param id * @param itemsCustom * @return */ //校验信息:在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult的形参,接收校验出错信息。 //注意:@Validated和BindingResult形参是配对出现的,并且顺序是一前一后。 @RequestMapping(value="/editItemsSubmit",method={RequestMethod.POST,RequestMethod.GET}) public String editItemsSubmit(Integer id,Model model, @Validated ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception{ //获取校验错误信息 if(bindingResult.hasErrors()){ List<ObjectError> allErrors = bindingResult.getAllErrors(); for (ObjectError objectError : allErrors) { System.out.println(objectError.getDefaultMessage()); } //将错误信息传导页面 model.addAttribute("allErrors", "allErrors"); return null; } //获取提交的items,更新到数据库 itemsService.updateItems(id, itemsCustom); return "forward:queryItems.action"; }
-
在页面中获取错误信息
<!-- 显示错误信息 -->
<c:if test="${allErrors != null}">
<c:forEach items="${allErrors}" var="error">
${error.defaultMessage }<br/>
</c:forEach>
</c:if>
问题:获取到的校验信息乱码
https://blog.youkuaiyun.com/stloven5/article/details/53312012
资源文件编码格式配置错误
正确配置方式:
<!-- 资源文件编码格式 -->
<property name="fileEncodings">
<props><prop key="classpath:CustomValidationMessages">UTF-8</prop></props>
</property>
或者直接配置:
<property name="defaultEncoding" value="utf-8"></property>
3.11、分组校验
需求:
在pojo中定义校验规则,而pojo是被多个controller所共用,当不同的controller方法对同一个pojo进行校验。但是每个controller方法需要不同的校验。
解决方法:
定义多个校验分组(其实是一个java接口),分组中定义有哪些规则。
每个controller方法使用不同的校验分组。
定义分组:接口定义
package com.zwd.ssm.controller.validation;
/**
* 校验分组
* @author Administrator
*
*/
public interface ValidGroup1 {
//此接口中不需要定义任何方法,仅是对不同的校验规则进行分组
//此分组校验商品名称长度
}
在校验规则中添加分组
在controller方法上使用分组校验
3.12、校验注解
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
4、数据回显:
4.1、什么是数据回显
提交后,如果出现错误,将刚才提交的数据回显到刚才的提交的页面。
4.2、pojo数据回显方法:@ModelAttribute
1、springMVC默认对pojo数据进行回显。
pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)。
2、使用@ModelAttribute
可以指定pojo回显到页面在request中的key。页面就可以根据该key来取值。
3、@ModelAttribute
还可以将方法的返回值传到页面
/**
* 商品分类
* @return
*/
//@ModelAttribute表示最终将方法返回值放在request中的key中
@ModelAttribute(value="itemTypes")
public Map<String,String> getItemTypes(){
Map<String,String> itemTypes = new HashMap<String,String>();
itemTypes.put("101", "数码");
itemTypes.put("102", "母婴");
return itemTypes;
}
<td>商品名称:<input name="itemsCustom.name" />
商品类型:
<select name="itemTypes">
<c:forEach items="${itemTypes}" var="itemType">
<option value="${itemType.key}">${itemType.value}</option>
</c:forEach>
</select>
</td>
4.3、简单类型回显方法
对于简单数据类型,如:Integer、String、Float等使用Model将传入的参数再放到request域实现显示。
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id)throws Exception{
//传入的id重新放到request域
model.addAttribute("id", id);
5、异常处理
5.1、异常处理思路
系统中异常包括两类,预期异常和运行异常RunTimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springMVC前端控制器交给异常处理器进行异常处理。如下图:
springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
5.2、自定义异常类
对不同的异常类型定义异常类,继承Exception。
package com.zwd.ssm.exception;
/**
* 系统自定义的异常类:针对预期的异常,需要在程序中抛出此类异常。
* @author Administrator
*
*/
public class CustomException extends Exception {
//异常信息
private String message;
public CustomException(String message){
super(message);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
5.3、全局异常处理器
思路:
系统遇到异常,在程序中手动抛出,dao抛给service、service给controller,controller抛给前端控制器,前端控制器调用全局异常处理器。
全局异常处理器处理器思路:
解析出异常类型
如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示。
如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
必须继承HandlerExceptionResolver
package com.zwd.ssm.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
/**
* 全局异常处理器
* @author Administrator
*
*/
public class CustomExceptionResolver implements HandlerExceptionResolver{
/**
* Object handler:处理器适配器要执行的handler(只有一个method)
* Exception ex:系统抛出的异常
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse respnse, Object handler,
Exception ex) {
/* String message = null;
// 解析出异常类型
// 如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示。
if(ex instanceof CustomException){
message = ex.getMessage();
}
// 如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
else{
message="未知错误";
}*/
//上面代码重写为:
CustomException customException = null;
if(ex instanceof CustomException){
customException = new CustomException(ex.getMessage());
}
else{
customException = new CustomException("未知错误");
}
//错误信息
String message = customException.getMessage();
ModelAndView modelAndView = new ModelAndView();
//将错误信息传到页面
modelAndView.addObject("message", message);
//指向错误页面
modelAndView.setViewName("error");
return modelAndView;
}
}
5.4、错误页面
error.jsp
<%@ 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>错误页面</title>
</head>
<body>
错误页面
</body>
</html>
5.5、在springmvc中配置全局异常处理器
<!-- 6、配置全局异常处理器:只要实现了HandlerExceptionResolver接口,就是一个全局异常处理器 -->
<bean class="com.zwd.ssm.exception.CustomExceptionResolver"></bean>
5.6、异常测试
在controller、service、dao中任意一处需要手动抛出自定义异常。如果是程序中手动抛出的异常,在错误页面中显示自定义的异常信息,如果不是手动抛出异常说明是一个运行时异常,在错误页面只显示“未知错误”。
在商品修改的controller方法中抛出异常 :
在service中抛出异常:
如果与业务功能相关的异常,建议在service中抛出异常。
与业务功能没有关系的异常,建议在controller中抛出。
上边的功能,建议在service中抛出异常。
6、上传图片
6.1、需求
在修改商品页面,添加上传商品图片功能。enctype="multipart/form-data"
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemsSubmit.action"
method="post" enctype="multipart/form-data">
6.2、导入jar包
6.3、springmvc中对多部件类型的解析
在页面form中提交enctype="multipart/form-data"的数据时,需要springmvc对multipart类型的数据进行解析。
在springmvc.xml中配置解析器:
<!-- 7、上传图片:配置多部件类型解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize" >
<value>5242880</value>
</property>
</bean>
6.4、创建图片虚拟目录来存储图片
- 通过图形界面来创建:
-
在tomcat服务器的config/server.xml中添加配置
<Context docBase="G:\imgs" path="/pic" reloadable="true"/>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log" suffix=".txt"/> <Context docBase="06.ssm" path="/06.ssm" reloadable="true" source="org.eclipse.jst.jee.server:06.ssm"/> <Context docBase="G:\imgs" path="/pic" reloadable="true"/> </Host>
**注意:**在图片虚拟目录中,一定将图片目录分级创建(提高i/o性能),一般我们采用按日期(年、月、日)进行分级创建。
6.5、上传图片代码
页面代码
<tr>
<td>商品图片</td>
<td>
<c:if test="${itemsCustom.pic !=null}">
<img src="/pic/${itemsCustom.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="items_pic"/>
</td>
</tr>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>修改商品信息</title>
</head>
<body>
<!-- 显示错误信息 -->
<c:if test="${allErrors != null}">
<c:forEach items="${allErrors}" var="error">
${error.defaultMessage }<br/>
</c:forEach>
</c:if>
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemsSubmit.action"
method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="${itemsCustom.id }"/>
修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${itemsCustom.name }"/></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${itemsCustom.price }"/></td>
</tr>
<tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime" value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
</tr>
<tr>
<td>商品图片</td>
<td>
<c:if test="${itemsCustom.pic !=null}">
<img src="/pic/${itemsCustom.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="items_pic"/>
</td>
</tr>
<tr>
<td>商品简介</td>
<td>
<textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
controller代码
MultipartFile items_pic
/**
* 7、点击提交后的操作:上传图片
* @param id
* @param itemsCustom
* @return
*/
//MultipartFile:获取页面上传图片的信息
@RequestMapping(value="/editItemsSubmit",method={RequestMethod.POST,RequestMethod.GET})
public String editItemsSubmit(Integer id,Model model,
@ModelAttribute(value="items")
@Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
BindingResult bindingResult,
MultipartFile items_pic) throws Exception{
//获取校验错误信息
if(bindingResult.hasErrors()){
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError objectError : allErrors) {
System.out.println(objectError.getDefaultMessage());
}
//将错误信息传导页面
model.addAttribute("allErrors", allErrors);
return "items/editItems";
}
//上传图片
if(items_pic != null && items_pic.getOriginalFilename()!= null && items_pic.getOriginalFilename()!=""){
//存储图片的物理路径
String pic_path = "G:\\imgs\\";
//原始名称
String originaleFilename = items_pic.getOriginalFilename();
//新的图片名称
String newFileName = UUID.randomUUID() + originaleFilename.substring(originaleFilename.lastIndexOf("."));
//新图片
File newFile = new File(pic_path+newFileName);
//将内存中的数据写入磁盘
items_pic.transferTo(newFile);
//将新图片名称写到itemsCustom中
itemsCustom.setPic(newFileName);
}
//获取提交的items,更新到数据库
itemsService.updateItems(id, itemsCustom);
return "forward:queryItems.action";
}
7、json数据交互
7.1、 为什么要进行json数据交互
json数据格式在接口调用中、HTML页面中较常用,json格式比较简单,解析还比较方便。
比如:webService接口,传输json数据。
7.2、springmvc进行json交互
1、请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便。
2、请求key/value、输出json。此方法比较常用。
@RequestBody
作用:
@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。
@ResponseBody
作用:
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端
7.3、环境准备
springmvc中使用jackson的包进行json转换(@RequestBody、@ResponseBody使用下边的包进行json转换)
上述包不适用,已更换为下面三个json包。
7.4、配置json转换器
注意:如果使用<mvc:annotation-driven />
则不用定义下边的内容。
<!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
7.5、json测试
7.5.1、输入json,输出json
jsp页面
使用jQuery的ajax提交json串,对输出的json结构进行解析。
- 将jQuery文件放在WebRoot下:
-
jsp页面代码
<%@ 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>json交互测试</title> </head> <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script> <script type="text/javascript"> //请求json输出json function requestJson(){ $.ajax({ type:'post', url:'${pageContext.request.contextPath }/requestJson.action', contentType:'application/json;charset=utf-8', //请求的数据格式是json串 data:'{"name":"手机","price":999}', success:function(data){//data:返回的json串 alert(data); } }); } //请求key/value输出json function requestKV(){ } </script> <body> <input type="button" οnclick="requestJson()" value="请求json输出json"/> <input type="button" οnclick="requestKV()" value="请求json输出json"/> </body> </html>
controller
package com.zwd.ssm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zwd.ssm.po.ItemsCustom;
/**
* json的测试controller
* @author Administrator
*
*/
@Controller
public class JsonTextController {
/**
* 输入json(商品信息),输出json(商品信息)
* @param itemsCustom
* @return
*/
//@RequestBody:将输入的json串转为java对象
//@ResponseBody:将输出的java对象转为json串输出
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
return itemsCustom;
}
}
访问
http://localhost:8080/08.json/jsonTest.jsp
7.5.2、输入key/value,输出json
jsp页面
<%@ 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>json交互测试</title>
</head>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
//请求json输出json
function requestJson(){
$.ajax({
type:'post',
url:'${pageContext.request.contextPath }/requestJson.action',
//ContentType没指定将默认为:application/x-www-form-urlencoded
contentType:'application/json;charset=utf-8',
//请求的数据格式是json串
data:'{"name":"手机","price":999}',
success:function(data){//data:返回的json串
alert(data);
}
});
}
//请求key/value输出json
function requestKV(){
$.ajax({
type:'post',
url:'${pageContext.request.contextPath }/requestKV.action',
//这里不用指定contentType,默认就是这里key/value的类型
//contentType:'application/x-www-form-urlencoded',
data:'name=手机&price=999',
success:function(data){//data:返回的json串
alert(data.name);
}
});
}
</script>
<body>
<input type="button" οnclick="requestJson()" value="请求json输出json"/>
<input type="button" οnclick="requestKV()" value="请求json输出json"/>
</body>
</html>
controller
package com.zwd.ssm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zwd.ssm.po.ItemsCustom;
/**
* json的测试controller
* @author Administrator
*
*/
@Controller
public class JsonTextController {
/**
* 输入json(商品信息),输出json(商品信息)
* @param itemsCustom
* @return
*/
//@RequestBody:将输入的json串转为java对象
//@ResponseBody:将输出的java对象转为json串输出
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
return itemsCustom;
}
/**
* 请求key/value,输出json
* @param itemsCustom
* @return
*/
@RequestMapping("/requestKV")
public @ResponseBody ItemsCustom requestKV(ItemsCustom itemsCustom){
return itemsCustom;
}
}
访问
http://localhost:8080/08.json/jsonTest.jsp
报错问题
问题已在上面更改。
-
jquery-1.4.4.min.js找不到,404
解决:js文件放错位置。应放在WebRoot下。
-
org.springframework.web.HttpMediaTypeNotSupportedException:
Content type ‘application/json;charset=utf-8’ not supported
https://blog.youkuaiyun.com/fpxty/article/details/72835993
解决:json包版本太低,更换json包。
8、RESTful支持
8.1、什么是RESTful
RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。
1、对url进行规范,写RESTful格式的url
非REST的url:http://…/queryItems.action?id=001&type=T01
REST的url风格:http://…/items/001
特点:url简洁,将参数通过url传到服务端
2、http的方法规范
不管是删除、添加、更新。。使用url是一致的,如果进行删除,需要设置http的方法为delete,同理添加后台controller方法:判断http方法,如果是delete执行删除,如果是post执行添加。
3、对http的contentType规范
请求时指定contentType,要json数据,设置成json格式的type。。
8.2、REST测试
需求
查询商品信息,返回json数据。
controller
package com.zwd.ssm.controller;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zwd.ssm.po.ItemsCustom;
import com.zwd.ssm.service.ItemsService;
/**
* RESTful测试
* @author Administrator
*
*/
@Controller
public class RestfulController {
@Resource(name="itemsService")
private ItemsService itemsService;
/**
* 查询商品信息:restful风格的url
* @param id
* @return
* @throws Exception
*/
//{id}:表示占位符,通过@PathVariable("id")获取占位符中的参数。
//如果占位符中的名称和形参名称一致,在@PathVariable中可以不指定名称
@RequestMapping("/restfulItemsquery/{id}")
public @ResponseBody ItemsCustom restfulItemsquery(@PathVariable("id") Integer id) throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
// @RequestMapping("/restfulItemsquery/{id}/{type}")
// public @ResponseBody ItemsCustom restfulItemsquery1(@PathVariable("id") Integer id,@PathVariable("type") String aaa) throws Exception{
// ItemsCustom itemsCustom = itemsService.findItemsById(id);
// return itemsCustom;
// }
}
REST方法的前端控制器配置
因为之前把url-pattern设置成*.action,所以使用restful风格的url(http://localhost:8080/08.json/restfulItemsquery/1)直接访问上面controller时,会发生404错误,找不到对应的url的handler。改成’/'即可访问。
HTTP Status 404 - /08.json/restfulItemsquery/1
<!-- 2、配置springmvc前端控制器 -->
<!-- The front controller of this Spring Web application, responsible for
handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- <servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping> -->
<!-- 使用restful,需要将url-pattern设置成 '/',否则无法访问,因为上述只能访问.action后缀的url -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
对静态资源的访问
http://localhost:8080/08.json/js/jquery-1.4.4.min.js
如果/这样设置,那么在访问静态资源的时候会发生404错误。因为根据地址找不到相应的handler,需要在springmvc.xml中添加对静态资源的访问。
<!-- 8、对静态资源的访问:如果<url-pattern>/</url-pattern>这样设置的,那么再访问静态资源的时候会发生404错误。因为根据地址找不到相应的handler,需要添加对静态资源的访问
静态资源包括:js、css、img.... -->
<mvc:resources location="/js/" mapping="/js/**"/>
报错问题
在添加<mvc:resources location="/js/" mapping="/js/**"/>
配置后,启动tomcat异常:
DEBUG [localhost-startStop-1] - Original ConversionService attempt
failed - ignored since PropertyEditor based conversion eventually
succeeded org.springframework.core.convert.ConversionFailedException:
Failed to convert from type java.util.ArrayList<?> to type
java.util.List<org.springframework.core.io.Resource> for value
‘[/js/]’; nested exception is
org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type java.lang.String to
type org.springframework.core.io.Resource
解决参考地址:
https://blog.youkuaiyun.com/jevons优快云/article/details/60577714
9、拦截器
9.1、拦截器的定义
继承HandlerInterceptor
接口:
package com.zwd.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截器例子
* @author Administrator
*
*/
public class HandlerInterceptor1 implements HandlerInterceptor{
/**
* 1、进入Handler方法之前执行
*/
//应用场景:用于身份认证、身份授权
//比如身份认证,如果认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
//return false表示拦截,不再向下执行handler方法
//return true表示放行
return false;
}
/**
* 2、进入handler方法之后,返回modelAndView之前执行。
*/
//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
}
/**
* 3、handler执行完成后执行此方法
*/
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
}
}
9.2、拦截器配置springmvc.xml
方式一:针对HandlerMapping进行拦截设置
springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该HandlerMapping映射成功的handler最终使用该拦截器。一般不推荐使用。
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
方式二:配置类似全局的拦截器
springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。
<!-- 9、全局拦截器配置 -->
<mvc:interceptors>
<!-- 多个拦截器,顺序执行 -->
<mvc:interceptor>
<!-- /**:表示拦截所有url包括子url路径 -->
<mvc:mapping path="/**"/>
<bean class="com.zwd.ssm.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zwd.ssm.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
9.3、拦截器的测试
需求
测试多个拦截器各各方法执行时机。
编写两个拦截
HandlerInterceptor1
package com.zwd.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截器例子
* @author Administrator
*
*/
public class HandlerInterceptor1 implements HandlerInterceptor{
/**
* 进入Handler方法之前执行
*/
//应用场景:用于身份认证、身份授权
//比如身份认证,如果认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
//return false表示拦截,不再向下执行handler方法
//return true表示放行
System.out.println("HandlerInterceptor1...preHandle");
return true;
}
/**
* 进入handler方法之后,返回modelAndView之前执行。
*/
//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
/**
* handler执行完成后执行此方法
*/
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
HandlerInterceptor2
package com.zwd.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截器例子
* @author Administrator
*
*/
public class HandlerInterceptor2 implements HandlerInterceptor{
/**
* 进入Handler方法之前执行
*/
//应用场景:用于身份认证、身份授权
//比如身份认证,如果认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
//return false表示拦截,不再向下执行handler方法
//return true表示放行
System.out.println("HandlerInterceptor2...preHandle");
return true;
}
/**
* 进入handler方法之后,返回modelAndView之前执行。
*/
//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("HandlerInterceptor2...postHandle");
}
/**
* handler执行完成后执行此方法
*/
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("HandlerInterceptor2...afterCompletion");
}
}
拦截器在springmvc.xml中的配置如9.2方式二。
拦截器1和拦截器2都放行
HandlerInterceptor1…preHandle
HandlerInterceptor2…preHandle
HandlerInterceptor2…postHandle
HandlerInterceptor1…postHandle
HandlerInterceptor2…afterCompletion
HandlerInterceptor1…afterCompletion
总结:
preHandle方法按顺序执行
postHandle和afterCompletion按拦截器配置的逆向顺序执行。
拦截器1放行、拦截器2不放行
HandlerInterceptor1…preHandle
HandlerInterceptor2…preHandle
HandlerInterceptor1…afterCompletion
总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle都不会执行。
拦截器1不放行、拦截器2不放行
HandlerInterceptor1…preHandle
HandlerInterceptor1…preHandle
总结:
拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
拦截器1 preHandle不放行,拦截器2不执行。
小结
根据测试结果,对拦截器应用进行总结。
比如:统一日志处理拦截器,需要该拦截器preHandle一定要放行,且将它放在拦截器链接中第一个位置。
比如:登陆认证拦截器,放在拦截器链接中第一个位置。权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过后才校验权限)
9.4、拦截器应用:实现登录认证
需求
1、用户请求url
2、拦截器进行拦截校验
如果请求的url是公开地址(无需登录即可访问的url),让放行
如果用户session不存在跳转到登录页面
如果用户session存在放行,继续操作
登录页面:login.jsp
<%@ 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>系统登陆</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/login.action" method="post">
用户账号:<input type="text" name="username" /><br/>
用户密码 :<input type="password" name="password" /><br/>
<input type="submit" value="登陆"/>
</form>
</body>
</html>
登录的controller方法
LoginController.java
package com.zwd.ssm.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 登录
* @author Administrator
*
*/
@Controller
public class LoginController {
/**
* 登录
* @param session
* @param username
* @param password
* @return
*/
@RequestMapping("/login")
public String login(HttpSession session,String username,String password){
//调用service进行身份认证...
session.setAttribute("username", username);
//重定向到商品查询页面
return "redirect:/items/queryItems.action";
}
/**
* 退出登录
* @param session
* @return
*/
@RequestMapping("/logout")
public String logout(HttpSession session){
//清除session
session.invalidate();
return "redirect:/items/queryItems.action";
}
}
登录认证拦截实现
package com.zwd.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截器例子
* @author Administrator
*
*/
public class LoginInterceptor implements HandlerInterceptor{
/**
* 用户身份验证
*/
//应用场景:用于身份认证、身份授权
//比如身份认证,如果认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//获取url地址
StringBuffer url = request.getRequestURL();
//判断该地址是否是公开地址(实际项目中公开地址需要写到配置文件中),这里是登录页面的地址
if(url.indexOf("login.action")>=0){
//是登录页面,放行
return true;
}
if(request.getSession() != null && request.getSession().getAttribute("username") != null){
//不是登录页面,但用户已经登录,放行
return true;
}
//否则:不是登录连接并且session为空:跳转到登录页面,不放行
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
/**
* 进入handler方法之后,返回modelAndView之前执行。
*/
//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("HandlerInterceptor2...postHandle");
}
/**
* handler执行完成后执行此方法
*/
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("HandlerInterceptor2...afterCompletion");
}
}
拦截器配置:springmvc.xml
<!-- 9、全局拦截器配置 -->
<mvc:interceptors>
<!-- 多个拦截器,顺序执行 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zwd.ssm.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!-- /**:表示拦截所有url包括子url路径 -->
<mvc:mapping path="/**"/>
<bean class="com.zwd.ssm.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zwd.ssm.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
访问
http://localhost:8080/08.json/items/queryItems.action