springmvc的基础知识
springmvc框架原理
前端控制器、处理器映射器、处理器适配器、视图解析器
springmvc入门程序
目的:对前端控制器、处理器映射器、处理器适配器、视图解析器
非注解的处理器映射器、处理器适配器
注解的处理器映射器、处理器适配器
springmvc和mybatis整合
springmvc注解开发
常用的注解学习
参数绑定(简单类型、pojo、集合类型)
自定义参数绑定
springmvc和struts2的区别
1.1、什么是springmvc框架
springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合
springmvc是一个基于mvc的web框架
1.2、mvc在b/s系统下的应用
mvc是一个设计模式
1.3、springmvc框架
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找Handler
可以根据xml配置、注解进行查找
第三步:处理器映射器HandlerMapping向前端控制器返回Handler
第四步:前端控制器调用处理器适配器去执行Handler
第五步:处理器适配器去执行Handler
第六步:Handler执行完成给适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
ModelAndView是springmvc框架的一个底层对象,包括Model和View
第八步:前端控制器请求视图解析器去进行视图解析
根据逻辑视图名解析成真正的视图(jsp)
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染
视图渲染将模型数据(在ModelAndView对象中)填充到request域
第十一步:前端控制器向用户响应结果
组件
1、前端控制器DispatcherServlet(不需要自己编写)
作用:接受请求,响应结果,相当于转发器,中央处理器
有了DispatcherServlet减少了其他组件之间的耦合度
2、处理器映射器HandlerMapping(不需要自己编写)
作用:根据请求的url查找Handler
3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行HJandler
4、视图解析器View resolver(不需要自己编写)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
5、视图 view
View是一个接口,实现类支持不同的view类型(jsp、freemarker、pdf...)
2.1、需求
整合springmvc和mybaits
商品列表查询
2.2环境
数据库
java环境
jdk1.8
eclipse oxygen
springmvc版本:spring5.0.2
需要spring所有jar(一定要spring-webmvc)
2.3配置前端控制器
<!-- springmvc前端控制器 -->
<servlet>
<!-- 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-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</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>
2.4配置处理器适配器
在classpath下的springmvc.xml中配置处理器适配器
查看SimpleControllerHandlerAdapter源码
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
此适配器能执行实现controller接口的Handler
public interface Controller {
/**
* Process the request and return a ModelAndView object which the DispatcherServlet
* will render. A {@code null} return value is not an error: it indicates that
* this object completed request processing itself and that there is therefore no
* ModelAndView to render.
* @param request current HTTP request
* @param response current HTTP response
* @return a ModelAndView to render, or {@code null} if handled directly
* @throws Exception in case of errors
*/
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
2.5、开发Handler
需要实现controller接口,才能由org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter适配器执行
2.6、配置处理器映射器
在classpath下的springmvc.xml中配置处理器映射器
2.7、配置视图解析器
在springmvc.xml文件配置如下
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 处理器视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
InternalResourceViewResolver:支持JSP视图解析
viewClass:JstlView表示JSP模版页面需要使用JSTL标签库,所以classpath中必须包含jstl相关的jar包
prefix和suffix:查找视图页面的前缀和后缀,最终视图地址为
3.1、非注解的处理器映射器和适配器
处理器映射器:
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
另一个映射器
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
<!-- 简单url映射 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- 对itemsController进行url映射,url是/queryItems.action -->
<prop key="/queryItems.action" >itemsController1</prop>
<prop key="/items2.action">controller的beanid</prop>
</props>
</property>
</bean>
多个映射器可以并存,前端控制器判断url能让哪些映射器映射,就让正确的映射器处理
3.2、非注解的处理器适配器(适配器管理的bean可以从他们实现接口中的supports方法看出instanceof)
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
要求编写的Handler实现Controller接口
另一个适配器HttpRequestHandlerAdapter
HttpRequestHandlerAdapter,http请求处理器适配器,
要求编写的Handler实现HttpRequestHandler接口
所有实现了org.springframework.web.servlet.mvc.HttpRequestHandler接口的Bean通过此适配器进行适配、执行
适配器配置如下
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
Controller实现如下
public class ItemsController2 implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//调用service查找数据库,查询商品列表,这里使用静态数据模拟
ArrayList<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setId(UUID.randomUUID().toString());
items_1.setName("联想");
items_1.setPrice(100f);
items_1.setDetail("我的天");
items_1.setCreatetime(new Date());
Items items_2 = new Items();
items_2.setName("神舟");
items_2.setPrice(200f);
items_2.setDetail("啊啊啊");
items_2.setId(UUID.randomUUID().toString());
items_2.setCreatetime(new Date());
itemsList.add(items_1);
itemsList.add(items_2);
//设置模型数据
request.setAttribute("itemsList", itemsList);
//设置转发的视图 与视图解析器的前后缀无关需要补全地址
request.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(request, response);
//此适配器的handleRequest方法没有返回ModelAndView,可通过response修改定义响应内容,比如返回json数据
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
}
4、DispatcherServlet properties
springmvc-webmvc中org.springframework.web.servlet中有DispatcherServlet.properties
前端控制器从上边的文件中加载处理映射器、适配器、视图解析器等组件,如果不在springmvc.xml中配置,使用默认加载的
5、注解的处理器映射器和适配器
在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器 5.0.2中找不到
在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器
在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter注解适配器 5.0.2中找不到
在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器
5.1、配置注解映射器和适配器
<!-- 注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 使用mvc:annotation-driven代替上边注解映射器和注解适配器配置
mvc:annotation-driven默认加载很多的参数绑定方法
比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上面的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
实际中用mvc:annotation-driven
-->
<mvc:annotation-driven></mvc:annotation-driven>
5.2开发注解Handler
使用主键的映射器和注解的适配器。(注解的映射器和适配器)
//使用Controller标识他是一个控制器
@Controller
public class ItemsController3{
//商品查询列表
//一边拿建议将url和方法写成一样
//@RequestMapping实现对queryItems方法和url进行映射,一个方法对应一个url
@RequestMapping("/queryItems3.action")
public ModelAndView queryItems() throws Exception{
ArrayList<Items> itemsList = new ArrayList<Items>();
Items items_1 = new Items();
items_1.setId(UUID.randomUUID().toString());
items_1.setName("联想3");
items_1.setPrice(100f);
items_1.setDetail("我的天");
items_1.setCreatetime(new Date());
Items items_2 = new Items();
items_2.setName("神舟3");
items_2.setPrice(200f);
items_2.setDetail("啊啊啊");
items_2.setId(UUID.randomUUID().toString());
items_2.setCreatetime(new Date());
itemsList.add(items_1);
itemsList.add(items_2);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相当于request的setAttribute,在jsp页面中通过itemsList取数据
modelAndView.addObject("itemsList",itemsList);
//指定视图
modelAndView.setViewName("/items/itemsList");;
return modelAndView;
}
//定义其他的方法
//商品添加
//商品修改
5.3在spring容器中加载Handler
如果已经配置了那么会出现该方法已经配置会报错
<!-- 对于注解的Handler可以单个配置
实际开发中建议使用组件扫描
-->
<!-- <bean class="springmvc_01.my.controller.ItemsController3"/> -->
<!-- 可以扫描controller、service...
这里让扫描controller,指定controller的包
-->
<context:component-scan base-package="springmvc_01.my.controller"></context:component-scan>
6、源码分析
通过前端控制器源码分析springmvc的执行过程
第一步:前端控制器接受请求
调用doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
第二步:前端控制器调用处理映射器查找Handler
mappedHandler = getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
第三步:调用处理器适配器执行Handler,得到执行结果ModelAndView
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
第四步:视图渲染,将model数据填充到request域render方法中
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
调用view的渲染方法,将model数据填充到request域
(渲染是先由实现了View接口的抽象类AbstractView的render方法调用继承抽象类AbstractUrlBasedView【继承了AbstractView】的类InternalResourceView
中的renderMergedOutputModel方法调用回AbstractView类的exposeModelAsRequestAttributes完成model数据填充到request域)
渲染方法:
view.render(mv.getModelInternal(), request, response);
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.forEach((modelName, modelValue) -> {
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
});
}
7、入门程序小结
通过入门程序理解springmvc前端控制器、处理器映射器、处理器适配器、视图解析器用法。
前端控制器配置:
第一种:*.action,访问以*.action结尾由DispatcherServlet进行解析
第二种:/所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析
使用此种方式可以实现RESTful风格的url
处理器映射器:
非注解处理器映射器
注解的处理器映射器
对标记@Controller类中标识有@RequestMapping的方法进行映射,在@RequestMapping中定义映射的url
使用注解的映射器不用在xml中配置url和Handler的映射关系
处理器适配器
非注解处理器适配器
注解的处理器适配器
注解处理器适配器和注解的处理器映射器配对使用。理解为不能使用非注解映射器进行映射
<mvc:annotation-driven></mvc:annotation-driven>可以代替下边的配置
<!-- 注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
视图解析器配置前缀和后缀:
<!-- 处理器视图解析器
解析jsp,默认使用jstl标签classpath下得有jstl的包
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
程序中不用指定前缀和后缀
8、springmvc和mybatis整合
8.1、需求
使用springmvc和mybatis完成商品列表查询
8.2、整合思路
springmvc+mybatis的系统架构:
第一步:整合dao层
mybatis和spring整合,通过spring管理mapper接口
使用mapper的扫描器自动扫描mapper接口在spring中进行注册
第二步:整理service层
通过spring管理service层
使用配置方式将service接口配置在spring配置文件中
实现事务控制
第三步:整合springmvc
由于springmvc是spring的模块,不需要整合
8.3、准备环境
数据库:mysql5.5
java环境:
jdk1.8
eclipse oxygen
springmbc版本:spring5.0.9
所需要的jar包:
数据库驱动包:mysql5.5
mybatis的jar包
mybatis和spring整合包
log4j包
dbcp数据库连接池包
spring5.0.9所有jar包
jstl包
工程结构:
sql
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, '2016-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');