SpringMVC是Spring的一个模块,SpringMVC和Spring不需要通过中间进行整合(struts2与spring整合需要单独的jar包), springMVC是基于MVC的Web框架。
下图是Spring框架中的SpringMVC
Spring框架图MVC是一个设计模式,下图是它在b/s系统下的应用
传统MVC我的天,看这个图是不是不难啊,看着很简单的样子。
这个故事还没有完结 --亚索
一、user向服务器server发起请求,请求到达前端控制器DispatcherServlet
二、前端控制器DispatcherServlet调用处理器映射器HandlerMapping返回该请求的handler
三、处理器映射器HandlerMapping根据url查找请求对应的hander(XML注释、注解)
四、前端控制器DispatcherServlet获取handler之后,调用处理器适配器HandlerAdapter
五、处理器适配器HandlerAdapter来调用Handel处理器,并获去Handel处理器返回的ModelAndView对象
六、处理器适配器将ModelAndView对象返回给前端控制器DispatcherServlet
七、前端控制器DispatcherServlet请求进行视图解析
八、视图解析器View resolver进行解析,很据逻辑视图名,转换成真正的视图(例如:JSP),返回view给前端控制器 DispatcherServlet
九、前端控制器DispatcherServlet请求渲染视图,将ModelAndView对象的内容填充到view中
十、前端控制器DispatcherServlet将响应结果返回给user
从上面的描述中很明显能抽取出几个重要的组件
前端控制器DispatcherServlet
作用:用于接收用户的请求,进行转发,响应,相当于中央处理器, 它降低了组件之间的耦合度。相当于中央控制器,掌控着其他个组件的运作。 这是不需要程序员自己编写的,框架本身提供了。
处理器映射器HandlerMapping
根据url查找handler,不需要自己编写。
处理器适配器HandlerAdapter
按照特的规则去执行handler
视图解析器View resolver
很据逻辑视图名,转换成真正的视图(例如:JSP) Handel处理器 需要程序自己开发,要按照处理器适配器HandlerAdapter特定的规则去编写
view
view的一个接口,不同的实现代表不同的view
小结 上面的组件只有view和handler处理器是需要自己编写的
可以追踪org.springframework.web.servlet.DispatcherServlet的源码,可以看到
imageuser-pattern:解释
第一种:*.action 访问以action结尾,由DispatcherServlet进行解析
第二种:/ 所有访问的地址都由DispatherServlet进行解析,静态文件可以通过配置不使用DispatcherServlet解析,符合Restful风格;
第三种:/* 这是错误的,这样的配置是解析jsp,仍会由DispatherServlet进行解析,不能根据jsp去找到Handler,会报错的!!
简单处理器适配器,可以跟踪一下源码:
image public class SimpleControllerHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof Controller); } public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); } public long getLastModified(HttpServletRequest request, Object handler) { if (handler instanceof LastModified) { return ((LastModified) handler).getLastModified(request); } return -1L; } }可以看到简单处理器支配器实现了Controller接口
==这一步需要先自己开发Handler,移至下一步==
<!-- 配置处理器 --> <bean name="/items.action" class="com.aikuyun.ssm.controller.ItemsController"/>解释一下:name属性时自己编写的,处理器映射器隔根据这个name去查找handler
==先不关心数据库,只是简单的模拟一下数据==
在com.aikuyun.ssm.controller包中重新建ItemsController.java
代码如下:要实现Controller接口
package com.aikuyun.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.aikuyun.ssm.po.Items; /* * * @ClassName: ItemsController * @Description: TODO(这里用一句话描述这个类的作用) * @author 陶世磊 * @date 2017年3月20日 * */ //实现org.springframework.web.servlet.mvc.Controller接口,按照特定的规范 public class ItemsController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // 商品列表 List<Items> itemsList = new ArrayList<Items>(); Items items_1 = new Items(); items_1.setName("联想笔记本"); items_1.setPrice(6000f); items_1.setDetail("ThinkPad T430 联想笔记本电脑!"); Items items_2 = new Items(); items_2.setName("苹果手机"); items_2.setPrice(5000f); items_2.setDetail("iphone6苹果手机!"); itemsList.add(items_1); itemsList.add(items_2); //创建ModelAndView对象 ModelAndView modelAndView = new ModelAndView(); // 填充数据 modelAndView.addObject("itemsList", itemsList); // 填充视图 modelAndView.setViewName("/WEB-INF/items/itemsList.jsp"); //返回ModelAndView return modelAndView; } }开发Handler之后要在配置文件中配置该类,==返回看上一步==;
打开服务器之后,在浏览器里访问:http://localhost:8080/项目名/items.action
查看测试结果,OK!
非注解的处理器映射器主要学习两个 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping简单url映射
在配置文件中进行配置如下:
<!-- 根据bean的name进行查找Handler 将action的url配置在bean的name中 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 配置另一个映射器 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!-- 根据key的值进行映射,访问itemsController --> <prop key="/items2.action">itemsController</prop> </props> </property> </bean> <!-- 配置处理器 --> <bean id="itemsController" name="/items.action" class="com.aikuyun.ssm.controller.ItemsController"/>分别看一下源码:理解一下原理 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { /** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override //很据benaname查找,要在对应的handler的bean里面加上name属性 protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); } }==很据benaname查找,要在对应的handler的bean里面加上name属性==
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping简单url映射
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { private final Map<String, Object> urlMap = new HashMap<String, Object>(); /** * Map URL paths to handler bean names. * This is the typical way of configuring this HandlerMapping. * <p>Supports direct URL matches and Ant-style pattern matches. For syntax * details, see the {@link org.springframework.util.AntPathMatcher} javadoc. * @param mappings properties with URLs as keys and bean names as values * @see #setUrlMap */ public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } /** * Set a Map with URL paths as keys and handler beans (or handler bean names) * as values. Convenient for population with bean references. * <p>Supports direct URL matches and Ant-style pattern matches. For syntax * details, see the {@link org.springframework.util.AntPathMatcher} javadoc. * @param urlMap map with URLs as keys and beans as values * @see #setMappings */ public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); } /** * Allow Map access to the URL path mappings, with the option to add or * override specific entries. * <p>Useful for specifying entries directly, for example via "urlMap[myKey]". * This is particularly useful for adding or overriding entries in child * bean definitions. */ public Map<String, ?> getUrlMap() { return this.urlMap; } /** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } /** * Register all handlers specified in the URL map for the corresponding paths. * @param urlMap Map with URL paths as keys and handler beans or bean names as values * @throws BeansException if a handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry<String, Object> entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } } }==需要在bean里面输入map集合的参数key指定url,value指定要映射的handler==
学两个非注解的适配器
一个是:org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter 开发的处理器必须实现Controller接口
另一个是:org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter 开发的处理必须实现HttpRequestHandler接口
前端控制器器从上面的属性文件中加载配置其他组件的加载类型,如果用户不自定义编写加载处理,用使用这个属性文件的的配置组件;
image注意:
在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器。
在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。
在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter注解适配器。
在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器。
在springmvc.xml中,加入:
<!-- 配置注解映射器和适配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>==还有一种配置方式:(实际开发用这种)==
<!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置 mvc:annotation-driven默认加载很多的参数绑定方法, 比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的 RequestMappingHandlerMapping和RequestMappingHandlerAdapter 实际开发时使用mvc:annotation-driven --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 配置处理器 -->代码部分:
package com.aikuyun.ssm.controller; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.aikuyun.ssm.po.Items; /* * * @ClassName: ItemsController * @Description: TODO(注解的适配器和映射器) * @author 陶世磊 * @date 2017年3月22日 * */ //使用Controller表示,它是一个控制器 @Controller public class ItemsController3{ //注解,对方法和url进行映射,一个方法对应一个url(可以写多个方法) @RequestMapping("/queryItems") public ModelAndView queryItems() throws Exception { // 商品列表 List<Items> itemsList = new ArrayList<Items>(); Items items_1 = new Items(); items_1.setName("联想笔记本"); items_1.setPrice(6000f); items_1.setDetail("ThinkPad T430 联想笔记本电脑!"); Items items_2 = new Items(); items_2.setName("苹果手机"); items_2.setPrice(5000f); items_2.setDetail("iphone6苹果手机!"); itemsList.add(items_1); itemsList.add(items_2); // ModelAndView modelAndView = new ModelAndView(); // 填充数据 modelAndView.addObject("itemsList", itemsList); // 填充视图 modelAndView.setViewName("/WEB-INF/items/itemsList.jsp"); return modelAndView; } }访问:http://localhost:8080/SpringMVC20170320/queryItems.action
这里出现了一个困扰我很久 的问题,查了很多资料之后恍然大悟。
image项目的目录结构如下:
image1.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> <!-- 定义别名 --> <typeAliases> <!-- 批量定义别名 指定包名,mybatis会自动扫描该包下的po类,别名就是类型(首字母可以大写或者小写) --> <package name="com.ssm.po"/> </typeAliases> <!-- 加载配置文件 --> <!-- <mappers> 批量加载mapper 指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载 遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录 中 上边规范的前提是:使用的是mapper代理方法 <package name="com.aikuyun.ssm.mapper"/> </mappers> --> </configuration>2.applicationContext-dao.xml配置数据源,配置SqlSessionFactory,扫描mapper接口
<?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-3.2.xsd"> <!-- 加载属性文件,该属性文件的命名规则要有一定的特殊性 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="30" /> <property name="maxIdle" value="5" /> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml"></property> <property name="dataSource" ref="dataSource"></property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ssm.mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> </beans>1.定义一个service接口
和mapper接口里面的方法一样2.在applicationContext-service.xml中配置service
<?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/contexthttp://www.springframework.org/schema/context/spring-context-3.2.xsd"> <bean id="itemsService" class="com.ssm.service.imp.ItemsServiceImp"></bean> </beans>3.在applicationContext-transaction.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 事务管理器 对mybatis操作数据库事务控制,spring使用jdbc的事务控制类 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 数据源 dataSource在applicationContext-dao.xml中配置了 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 传播行为 --> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> <!-- aop --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.ssm.service.impl.*.*(..))"/> </aop:config> </beans>1.springmvc.xml文件里配置前端控制器,适配器,映射器,视图解析器,Handler
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 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-3.2.xsd"> <!-- 配置注解的适配器 和注解的映射器 --> <mvc:annotation-driven></mvc:annotation-driven> <!--配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"></property> <property name="suffix" value=".jsp"></property> </bean> <!--扫描handler --> <context:component-scan base-package="com.ssm.controller"> </context:component-scan> </beans>2.配置前端控制器---在web.xml中加入:
servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等等) 如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-serlvet.xml(springmvc-servlet.xml) --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>3.编写Handler
package com.ssm.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.ssm.mapper.ItemsMapper; import com.ssm.po.ItemsCustom; @Controller public class ItemsController { @Autowired private ItemsMapper itemsMapper; @RequestMapping("/queryItems") public ModelAndView queryItems() throws Exception{ List<ItemsCustom> itemsList = itemsMapper.findItems(null); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("itemsList",itemsList); modelAndView.setViewName("items/itemsList"); return modelAndView; } }4.编写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>在web.xml中加入:
<!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/spring/applicationContext-dao.xml, /WEB-INF/classes/spring/applicationContext-service.xml,/WEB-INF/classes/spring/applicationContext-tansaction.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>完成上面所有工作之后的项目结构:
image image