SpringMVC框架笔记
标签:
SpringMVC
框架
MingFeng197
文章目录
SpringMVC简介
SpringMVC是什么?
SpringMVC是Spring的一个模块,提供web层的解决方案(基于MVC设计架构)
MVC在B/S系统中应用方式
MVC是一个设计模式
1. 用户发起request请求至控制器(Controller)
控制接收用户请求的数据,委托给模型进行处理
2. 控制器通过模型(Model)处理数据并得到处理结果
模型通常是指业务逻辑
3. 模型处理结果返回给控制器
4. 控制器将模型数据在视图(View)中展示
web中模型无法将数据直接在视图上显示,需要通过控制器完成。如果在C/S应用中模型是可以将数据在视图中展示的。
5. 控制器将视图response响应给用户
通过视图展示给用户要的数据或处理结果
SpringMVC架构
1、 用户发送请求至前端控制器DispatcherServlet
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器查找Handler。
3、 HandlerMapping处理器映射器根据请求url找到具体的处理器(Handler),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5、 执行处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器,生成View
9、 ViewReslover解析后返回具体View
10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户
组件说明:
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的C,dispatcherServlet是整个流程控制的中心,
由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,
例如:配置文件方式,实现接口方式,注解方式等。
Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
ViewResolver:视图解析器
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,
再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
SpringMVC入门程序
案例:商品列表查询
第一步:导入jar包
Spring开发的jar包
spring-webmvc-3.2.0.jar
第二步:配置文件
web.xml中配置
<!-- 前端控制器 -->
<servlet>
<servlet-name>springmvc</servletname>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载springmvc配置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置文件地址,如果不配置,默认查找classpath下的:servlet名称+"-servlet.xml"即:springmvc-servlet.xml -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servletname>
<!-- 可以配置/ ,此工程所有请求全部由springmvc解析,此种方式可以实现RESTful方式,需要特殊处理对静态文件的解析不能由springmvc解析
可以配置*.do或*.action,所有请求url的扩展名为.do或.action由springmvc进行解析,此方法常用
不可以/* 如果配置/* , 返回jsp也由springmvc解析 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
springmvc.xml配置:
在springmvc.xml中配置springmvc架构三大组件(处理器映射器、适配器、视图解析器)
BeanNameUrlHandlerMapping: 根据请求url(XXXX.action)匹配spring容器bean的 name
找到对应的bean(程序编写的Handler)
<!-- 配置处理器映射器 springmvc框架根据HandlerMapping接口判断是否是处理器映射器 -->
<!-- 根据bean的name进行查找Handler将action的url配置在bean的name中 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器 springmvc框架根据HandlerAdapter接口判断是否是处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
所有处理器映射器都实现HandlerMapping
接口
所有的适配器都是实现了HandlerAdapter
接口
程序编写Handler根据适配器的要求编写。
SimpleControllerHandlerAdapter适配器要求:
通过supports方法知道Handler必须要实现Controller接口:
public boolean supports(Object handler){
return (handler instanceof Controller)
}
Controller接口代码:
public interface Controller{
ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception;
}
Handler编写:
public class ItemController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception{
//使用静态数据将信息列表传到jsp页面
List<Items> itemsList=new ArrayList<Items>();
//这里省略了n个 itemsList.add(items) 操作
ModelAndView modelAndView = new ModelAndView();
//将数据填充到request域
modelAndView.addObject("itemsList",itemsList);
//指定转发的jsp页面
modelAndView.setViewName("/WEB-INF/jsp/itemsList.jsp");
return modelAndView;
}
}
配置Handler:
<!-- 配置Handler 由于使用了BeanNameUrlHandlerMapping处理映射器,name配置为url -->
<bean name="/itemList.action" class="cn.itcast.springmvc.frist.ItemController"/>
配置视图解析器:
<!-- 配置视图解析器(ViewResolver) 要求将jstl的包加到classpath -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
工程部署访问
其它非注解处理器映射器和适配器
SpringMVC允许多个映射器处理器可以同时存在
BeanNameUrlHandlerMapping(映射器)
根据请求URL匹配Spring容器的Bean的Name,找到对应的Bean(程序员编写的Handler)
SimpleUrlHandlerMapping(映射器)
<!-- 简单的URL映射,集中配置Bean的ID对应的URL -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<!-- key:访问路径 值:要访问的bean的id -->
<prop key="/itemsTest1.action">itemController1</prop>
<prop key="/itemsTest2.action">itemController1</prop>
<!-- ...省略... -->
</property>
</bean>
SimpleControllerHandlerAdapter(适配器)
要求程序编写的Handler(Controller)需要实现 Controller接口
HttpRequestHandlerAdapter(适配器)
在springmvc.xml配置:HttpRequestHandlerAdapter
<!-- HttpRequestHandlerAdapter适配器配置 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
要求Handler 实现 HttpRequestHandler接口 开发Handler:
public class ItemController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
//使用静态数据将商品信息列表显示在jsp页面
List<Items> itemsList=new ArrayList<Items>();
//这里省略了n个 itemsList.add(items) 操作
//将数据填充到request域
request.setAttribute("itemsList",itemsList);
//指定转发的jsp页面
request.getRequestDispatcher("/WEB-INF/jsp/itemsList.jsp").forward(request,response);
}
}
配置Handler
<bean id="itemController" class="cn.itcast.springmvc.frist.ItemController"></bean>
<!-- 简单url映射 集中配置bean的url -->
配置SimpleUrlHandlerMapping映射器(参考上面)
DispatcherServlet.properoties
DispatcherServlet前端控制器加载 DispatcherServlet.properoties 配置文件,从而默认加载各各组件,
如果在springmvc.xml中配置了处理器映射器和适配器,以sprintmvc.xml中配置的为准
注解映射器和适配器
注解映射器
在Spring3.1之前默认加载的映射器是: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
3.1之后要使用: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
在springmvc.xml中配置RequestMappingHandlerMapping:
使用RequestMappingHandlerMapping
需要在Handler 中使用@Controller
标识此类是一个控制器,使用@RequestMapping
指定Handler方法所对应的url
<!-- 注解处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
注解适配器
spring3.1之前默认加载映射器是: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
3.1之后要使用:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
RequestMappingHandlerAdapter,不要求Handler实现任何接口,它需要和RequestMappingHandlerMapping注解映射器配对使用,主要解析Handler方法中的形参
注解开发Handler
import org.springframework.stereotype.Controller;
@Controller
public class ItemController{
//@RequestMapping中的URL建议和方法名一致,方便开发维护
@RequestMapping("/queryItems")
public ModelAndView queryItems(){
//使用静态数据将信息列表传到jsp页面
List<Items> itemsList=new ArrayList<Items>();
//这里省略了n个 itemsList.add(items) 操作
ModelAndView modelAndView = new ModelAndView();
//将数据填充到request域
modelAndView.addObject("itemsList",itemsList);
//指定转发的jsp页面
modelAndView.setViewName("/WEB-INF/jsp/itemsList.jsp");
return modelAndView;
}
}
配置Handler:
<!-- 注解的Handler单个配置 -->
<!-- <bean class="cn.itcast.springmvc.frist.ItemController" /> -->
<!-- 建议使用组件扫描,组件扫描可以扫描@Controller、@Service、@Component、@Repsitory -->
<context:component-scan base-package="cn.itcast.springmvc.frist"/>
小结
DispatcherServlet:前端控制器,相当于中央调度器,可以降低组件之间的耦合度。
HandlerMapping:处理器映射器,负责根据url查找Handler
HandlerAdapter:处理器适配器,负责根据适配器要求的规则去执行处理器。可以通过扩展适配器支持不同类型的Handler。
viewResolver:视图解析器,根据逻辑视图名解析成真正的视图
<!-- 配置前缀后缀名 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="前缀" />
<property name="suffix" value="后缀" />
</bean>
1、DispatcherServlet通过HandlerMapping查找Handler
2、DispatcherServlet通过适配器去执行Handler,得到ModelAndView
3、视图解析 视图解析完成得到一个view
4、进行视图渲染 将Model中的数据 填充到request域
springmvc使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter,可用在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。
SpringMVC整合MyBatis
整合思路
在mybatis和spring整合的基础上 添加springmvc。
spring要管理springmvc编写的Handler(controller)、mybatis的SqlSessionFactory、mapper
第一步:整合dao,spring和mybatis整合
第二步:整合service,spring管理service接口,service中可以调用spring容器中dao(mapper)
第三步:整合controller,spring管理controller接口,在controller调用service
导入jar包
配置文件
applicationContext-dao.xml --配置数据源,SqlSessionFactory,mapper扫描器
applicationContext-service.xml --配置Service接口
applicationContext-transaction.xml --事务管理
springmvc.xml --springmvc的配置,配置处理器映射器,适配器,视图解析器
SqlMapConfig.xml --MyBatis的配置文件,配置别名,settings,mapper
applicationContext-dao.xml
配置mybatis的数据源、sqlSessionFactory、mapper扫描器
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 数据库连接池 dbcp -->
<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>
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
</bean>
<!-- mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.springmvc.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
applicationContext-transaction.xml
在此配置文件配置事务,声明式事务控制
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attribute>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
</tx:attribute>
</tx:advice>
<!-- aop 切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.ssm.service.impl.*.*(..))"/>
</aop:config>
前端控制器配置
<!-- 前端控制器配置 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载SpringMVC配置文件 -->
<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>
配置springmvc.xml
<!-- 使用spring组件扫描 -->
<context:component-scan base-package="cn.itcast.controller"/>
<!-- 通过annotation-driven可以代替下面的处理器映射器和适配器 -->
<!-- <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" />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="perfix" value="前缀"/>
<property name="suffix" value="后缀"/>
</bean>
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>
<!—- 使用自动扫描器时,mapper.xml文件如果和mapper.java接口在一个目录则此处不用定义mappers -->
<mappers>
<package name="cn.itcast.ssm.mapper" />
</mappers>
</configuration>
工程结构
> src
> cn.itcast.ssm.controller
> cn.itcast.ssm.mapper
> cn.itcast.ssm.po
> cn.itcast.ssm.service
> config
> mybatis
SqlMapConfig.xml
> spring
applicationContext-dao.xml
applicationContext-service.xml
applicationContext-transaction.xml
springmvc.xml --springmvc
db.properties
log4j.properties
商品列表开发
需求:查询商品列表
功能描述:根据条件查询商品信息,返回商品列表
一般情况下针对查询mapper需要自定义mapper。
首先针对单表进行逆向工程,生成代码
mapper.xml
<sql id="query_items_where">
<if test="itemsCustom!=null">
<if test="itemsCustom.name!=null and itemsCustom.name!=''">
and name like '%${itemsCustom.name}%'
</if>
<if test="itemsCustom.id!=null">
and id = #{itemsCustom.id}
</if>
</if>
</sql>
<!-- 商品查询 parameterType:输入查询条件 -->
<select id="findItemsList" parameterType="cn.itcast.ssm.po.ItemsQueryVo"
resultType="cn.itcast.ssm.po.ItemsCustom">
select * from items
<where>
<include refid="query_items_where"/>
</where>
</select>
包装类
public class ItemsQueryVo{
private ItemsCustom itemsCustom;//商品信息
}
mapper.java
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;//查询商品列表
service
public class ItemsServiceImpl implements ItemsService{
@Autowired//注入mapper
private ItemsMapperCustom itemsMapperCustom;
@Override
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {
return itemsMapperCustom.findItemsList(itemsQueryVo);
}
}
在applicationContext-service.xml中配置service
<bean id="itemsService" class="cn.itcast.ssm.service.impl.ItemsServiceImpl"/>
Controller
@Controller
public class ItemsController{
@Autowired
private ItemsService itemsService;
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception {
List<ItemsCustom> itemsList = itemsService.findItemsList(null);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList",itemsList);
modelAndView.setViewName("itemsList");//指定视图名 这里有自动添加前后缀
return modelAndView;
}
}
JSP
itemsList.jsp
在web.xml配置Spring监听器
<!-- 加载Spring容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/application-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 解决post乱码 -->
<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>
<!-- springmvc的前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必须的 -->
<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>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
注解开发基础
@RequestMapping
设置方法对应的url,完成url映射;
一个方法对应一个url
在class上可以定义根路径 来窄化请求映射
使用method来限定请求方式 通过RequestMethod.GET和RequestMethod.POST
如果请求方式错误,则会抛出异常
@Controller
@RequestMapping("/根路径")
public class ItemsController{
@RequestMapping(value="/访问路径",method=RequestMethod.GET)//访问路径等于: 根路径/访问路径
public ModelAndView queryItems() throws Exception{}
}
Controller方法返回值
1. 返回ModelAndView
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception{
//进行逻辑操作...
ModelAndView mav=new ModelAndView();
mav.addObject("key",obj);//添加到Request域
mav.setViewName("itemsList");//指定逻辑视图名
return mav;
}
3. 返回字符串
@RequestMapping("/queryItems")
public String queryItems(Model model) throws Exception{
//进行逻辑操作...
//方法返回的字符串就是逻辑视图名,Model的作用是将数据填充到Request域
model.addAttribute("key",obj);
return "editItems";
}
4. 返回void
@RequestMapping("/queryItems")
public void queryItems(HttpServletRequest req,HttpServletResponse resp) throws Exception{
//进行逻辑操作...
req.setAttribute("key",obj);
req.getRequestDispatcher("/xxx.jsp").forward(req,resp);
return "editItems";
}
6. redirect 重定向
返回值为“redirect:url路径”
7. forward 转发
返回值为"forward:url路径"
参数绑定
http请求 -> key,value数据 -> HandlerAdapter适配器 ->
早期springmvc是使用 PropertyEditor属性编辑器进行参数绑定(仅支持由字符串传为其它类型);
后期springmvc是使用converter转换器进行参数绑定(支持任意类型转换)->
将key/value数据绑定到方法形参 -> Handler 以方法为单位进行编写 方法形参
默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值
HttpServletRequest
HttpServletResponse
HttpSession
Model
//通过model向页面传递数据; 页面通过${key.xxx}获取obj的属性值
model.addAttribute("key",obj);
@RequestParam
如果request请求的参数名和controller方法的形参数名称一致,适配器自动进行参数绑定。
如果不一致可以通过 @RequestParam 指定request请求的参数名绑定到哪个方法形参上
对于必须要传的参数,通过@RequestParam中属性required设置为true,如果不传此参数则报错
对于有些参数如果不传入,还需要设置默认值,使用@RequestParam中属性defaultvalue设置默认值
PathVariable(“paramName”)
简单数据类型
简单POJO类
包装POJO类
自定义绑定
使用WebDataBinder(了解):
@InitBinder//自定义属性编辑器
public void initBinder(WebDataBinder binder) throws Exception {
//Date.class必须是与controller方法形参pojo属性一致的date类型
//第二个参数布尔值为:是否允许为空
//使用这种方法问题是无法在多个controller共用
binder.registerCustomEditor(Date.class,new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"),true));
}
使用WebBindingInitializer(了解):
使用WebBindingInitializer让多个controller共用 属性编辑器。
自定义WebBindingInitializer,注入到处理器适配器中。
如果想多个controller需要共同注册相同的属性编辑器,可以实现PropertyEditorRegistrar接口,并注入webBindingInitializer中; 如下:编写CustomPropertyEditor:
public class CustomPropertyEditor implements PropertyEditorRegistrar {
@Override
public void registerCustomEditor(PropertyEditorRegistry binder){
binder.registerCustomEditor(Date.class,new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"),true));
}
}
配置如下:
<!-- 注册属性编辑器 -->
<bean id="customPropertyEditor" class="cn.itcast.ssm.propertyeditor.CustomPropertyEditor"></bean>
<!-- 自定义webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPropertyEditor"/>
</list>
</property>
</bean>
<!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 自定义属性编辑器 -->
<property name="webBindingInitializer" ref="customBinder"></property>
</bean>
自定义参数绑定使用转换器
实现Converter接口:
//自定义Converter
public class CustomDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
return simpleDateFormat.parse(source);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
配置转换器:
配置方式一:针对不使用<mvc:annotation-driven>
的配置
<!--注解适配器 -->
<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>
配置方式二:针对使用<mvc:annotation-driven>
的配置
<mvc:annotation-driven conversion-service="conversionService">
</mvc:annotation-driven>
<!-- 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中加入:
<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请求中文参数出现乱码解决方法有两个:
修改tomcat配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一种方法对参数进行重新编码:
//ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8");
小总结
用户请求url到DispatcherServlet前端控制器,相当于中央调度器,降低系统各组件之间耦合度
DispatcherServlet前端控制器通过HandlerMapping根据url找到Handler。
DispatcherServlet前端控制器通过HandlerAdapter处理器适配器执行Handler。
DispatcherServlet前端控制器拿着Handler返回的ModelAndView通过视图解析器ViewResolver去进行视图解析。
视图解析:将程序中写的逻辑视图名,转成真正的视图(springmvc通过view表示各各不同类型的视图)。
DispatcherServlet前端控制器调用View的渲染方法进行视图渲染(将ModelAndView中的Model放到request域)。
使用专门注解处理器映射器(RequestMappingHandlerMapping)和处理器适配器(RequestMappingHandlerAdapter)。
<mvc:annotation-driven/>可以代替上边的处理器映射器和适配器的配置。
在Handler(Controller)中定义很多的方法,一个方法通过RequestMapping和url进行映射。
方法返回值: ModelAndView、string(jsp的逻辑视图名)、void(通过response将数据输出成json)
方法输入参数(形参):
springmvc需要将请求的key/value(串,id=001&type=t002)、解析、绑定到Handler(Controller)中方法的形参上。
springmvc默认支持多类型的参数绑定。
默认支持哪些类型:
HttpServletRequest、response、session、Model(用于将数据填充到request域)
@requestParam注解:用于绑定单个请求参数,常用于简单类型参数(Integer、String 、Float。。。)绑定。
不用 @requestParam要求请求参数的名称和方法形参名一致方可绑定。
对于简单类型参数中的日期型,建议使用自定义参数绑定,对日期型数据个化定义日期的格式。
自定义参数绑定:建议使用Convertor进行参数绑定。
还可以绑定pojo、包装的pojo
数据回显
需求:表单提交出现错误,重新回到表单,用户重新填写数据,刚才提交的参数在页面上回显
对简单类型的数据回显
直接使用Model中的addAttribute()方法进行数据的回显
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id) throws Exception {
//逻辑操作...
model.addAttribute("id",id);
//... 页面显示方式: ${id}
}
POJO类型回显
方法一: 使用Model进行数据回显
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,ItemsCustom item) throws Exception {
//逻辑操作...
model.addAttribute("item",item);
//... 页面显示方式: ${item.id}
}
方法二: 使用@ModelAttribute注解 作用:将请求pojo数据放到model中回显到页面
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,@ModelAttribute(value="item") ItemsCustom item) throws Exception {
//@ModelAttribute的value属性作用:存放到Model中的key 如果不设置 默认与对象名一致
//逻辑操作...
}
@ModelAttribute还可以将方法返回值传到页面
需求:商品类别信息在商品信息页面显示
@ModelAttribute("itemsType")
public Map<String,String> getItemsType() throws Exception {
Map<Sting,String> itemsType=new HashMap<String,String>();
map.put("001","家电");
map.put("002","服装");
//...
return itemsType;
}
使用@ModelAttribute将公用的取数据的方法返回值传到页面,不用在每一个controller方法通过Model将数据传到页面。
页面:
<select>
<c:forEach items="${itemsType}" var="item">
<option value="${item.key}">${item.value}</option>
</c:forEach>
</select>
参数绑定集合类型
绑定数组
页面:
<input type="checkbox" name="delete_id" value="${item.id}"/>
Controller方法:
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] delete_id) throws Exception {
//逻辑操作...
}
绑定List
包装类中定义List集合
页面:
<!--
itemsList:controller方法形参包装类型中list的属性名。
itemsList[0]或itemsList[1]。。,[]中是序号,从0开始。
itemsList[].name:name就是controller方法形参包装类型中list中pojo的属性名 -->
<input type="text" name="itemsList[${elm.index}].name" value="${item.name}"/>
<input type="text" name="itemsList[${elm.index}].price" value="${item.price}"/>
绑定Map
包装类中定义Map对象
页面使用:
<input type="text" name="itemsList['key']" />
SpringMVC与Struts的区别
springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器
springmvc是通过方法的形参接收参数,在使用时可以以单例方式使用,建议使用单例。
struts是通过成员变量接收参数,在使用时必须以多例方式使用。
springmvc是基于方法开发,struts基于类开发。
springmvc将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
springmvc开发以方法为单位进行开发,方法更贴近service(业务方法)。
struts标签解析速度比较慢,建议在实际开发时使用jstl
图片上传
切记:不要把图片上传到工程 目录 ,不方便进行工程 维护。实际电商项目中使用专门图片服务器(http,比如apache、tomcat)
本教程使用图片虚拟目录,通过虚拟目录 访问硬盘上存储的图片目录
配置图片上传解析器
springmvc使用commons-fileupload进行图片上传。
commons-fileupload对应的springmvc的图片上传解析器:org.springframework.web.multipart.commons.CommonsMultipartResolver
加入commons-fileupload
的jar包
commons-fileupload-1.2.2.jar
commons-io-2.4.jar
<!-- 文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件大小的最大为50MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
HTML页面:
<form action="imgUpload" method="post" enctype="multipart/form-data">
<img src="/pic/${itemsCustom.pic}">
<input type="file" name="picFile">
<input type="submit"/>
</form>
Controller代码:
@RequestMapping("imgUpload")//MultipartFile用来接收文件等复杂类型数据
public String imgUpload(MultipartFile picFile)throws Exception{
//进行图片上传
if(picFile!=null && picFile.getOriginalFilename()!=null){
//图片上传成功后,将图片的地址保存到数据库
String filePath="F:\\imageServer\\upload\\";//图片路径的位置
//上传文件原始名称
String originalFilename = picFile.getOriginalFilename();
//新的图片名称 UUID+扩展名
String newFilename=UUID.randomUUID()+originalFilename .substring(originalFilename .lastIndexOf("."));
//新文件
File file=new File(filePath+newFilename);
//将内存中的文件写入硬盘
picFile.transferTo(file);
//图片上传成功,将地址写入数据库
itemsCustom.setPic(newFileName);
}
return "success";
}
JSON数据交互
Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包:
jackson-core-asl-1.9.11.jar
jackson-mapper-asl-1.9.11.jar
在处理器适配器中注入MappingJacksonHttpMessageConverter
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定义属性编辑器,自定义转换器... -->
<!-- 加入Json数据的消息转换器 -->
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
@RequestBody和@ResponseBody
@RequestBody: 将请求的Json数据转换成java对象
@ResponseBody: 将java对象转换成Json数据输出
http请求数据是json格式(json串)contentType=appliation/json;charset=utf-8
使用@RequestBody将json转成java对象(pojo)
@ResponseBody 加在方法形参前
@ResponseBody 方法返回值 controller方法形参(@RequestBody pojo对象)
如果请求的是key/value就不需要使用@RequestBody
请求JSON响应JSON
Controller方法:
@RequestMapping("requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemCustom) throws Exception {
//逻辑操作...
}
页面:
function requestJson(){
$.ajax({
url:"${pageContext.request.contextPath}/requestJson.action",
type:"post",
contentType:"application/json;charset=utf-8",
data:'请求的json数据',
success:function(data){
//...
}
});
}
请求key/value响应json
Controller方法:
@RequestMapping("responseJson")
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemCustom) throws Exception {
//逻辑操作...
return itemsCustom;
}
如果前端处理没有特殊要求建议使用第二种,请求key/value,响应json,方便客户端解析请求结果
validation校验
基本使用
对前端的校验大多数通过js在页面校验,这种方法比较简单,如果对安全性考虑,还要在后台校验。
springmvc使用JSR-303(javaEE6规范的一部分)校验规范,springmvc使用的是Hibernate Validator(和Hibernate的ORM没有关系)
加入Hibernate Validator的jar包
hibernate-validation-4.3.0.Final.jar
jboss-logging-3.1.0.CR2.jar
validation-api-1.0.0.GA.jar
在处理器适配器中配置校验器:
<!-- 注解器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定义编辑器,自定义转换器 -->
<property name="webBindingInitializer" ref="customBinder"/>
</bean>
<!-- 自定义webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- 使用converter进行参数转换 -->
<property name="conversionService" ref="conversionService"/>
<!-- 配置validator校验器 -->
<property name="validator" ref="validator" />
<!-- propertyEditorRegistrars用于属性编辑器 -->
<!-- <property name="propertyEditorRegistrars">
<list>
<ref bean="customPropertyEditor"/>
</list>
</property> -->
</bean>
<!-- 转换器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="cn.itcast.ssm.controller.converter.CustomDateConverter" />
<bean class="cn.itcast.ssm.controller.converter.StringTrimConverter" />
</list>
</property>
</bean>
<!-- 校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<!-- 指定校验使用的资源文件,如果不指定则默认使用classpath下的alidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource" />
</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 name="cacheSeconds" value="120"/>
</bean>
在classpath下创建CustomValidationMessages.properties
# 校验提示信息,
错误信息名称一=错误信息一
错误信息名称二=错误信息二
items.name.length.error=商品名称长度应为1-30个字符
items.createtime.is.notnull=请输入商品生产日期
#...
校验规则:
public class Items {
private Integer id;
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
@NotNull(message="{items.createtime.is.notnull}")
private Date createtime;
}
捕获错误:
需要修改controller方法,每个校验POJO前必须加上@Validated,后面要加BindingResult接收校验错误信息
public String editTest(Model model,@Validated @ModelAttribute(value="itemsCustom") ItemsCustom itemsCustom,BindingResult bindingResult){
if(bindingResult.hasErrors()){
//获取错误
List<ObjectError> errors=bindingResult.getAllErrors();
model.addAttribute("errors",errors);//添加要Request域,准备在页面使用jstl展示
for(ObjectError error:errors){//控制台输出错误信息
System.out.println(error.getDefaultMessage());
}
}
}
在页面输出错误信息:
<c:forEach items="${errors}" var="error">
${error.defaultMessage}<br/>
</c:forEach>
分组校验
针对不同的controller方法通过分组校验达到个性化校验的目的
第一步: 创建分组接口
public interface ValidGroup1{
//接口不定义方法,就是标识,哪些校验规则属于该ValidGroup1分组
}
第二步: 定义校验规则属于哪个分组
public class Items {
private Integer id;
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
//将该校验添加至ValidGroup1分组; 规定该校验属于哪个分组,可以属于多个分组
@NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class})
private Date createtime;
}
第三步: 在Controller方法定义使用校验的分组
//在@Validated中定义使用ValidGroup1组下边的校验
@RequestMapping("/editItems")
public String editItems(Model model,@Validated(value={ValidGroup1.class}) @ModelAttribute(value="itemsCustom") ItemsCustom itemsCustom) throws Exception{}
统一异常处理
一般项目中都需要作异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。
系统中异常类型有哪些?
包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常
统一异常处理解决方案:
定义异常:
针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类:CustomException,用于测试
public class CustomException extends Exception{
private String message;//异常信息
public CustomException(String message){
supper(message);
this.message=message;
}
//getter/setter...
}
异常处理:
要在一个统一异常处理的类中要处理系统抛出的所有异常,根据异常类型来处理。
统一异常处理的类是什么?
在系统中自定义统一的异常处理器,写系统自己的异常处理代码
定义统一异常处理器类
统一异常处理器实现HandlerExceptionResolver接口
public class CustomExceptionResolver implements HandlerExceptionResolver{
//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常,进行异常处理
@Override
public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex){
//统一异常处理代码...
return null;
}
}
配置统一异常处理器:
<!-- 定义统一异常处理器 -->
<bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>
异常处理逻辑
要求所有异常全部向上抛出,在DispatcherServlet统一try/catch捕获处理异常
根据不同的异常类型进行异常处理。
系统自定义的异常类是CustomException ,在controller方法中、service方法中手动抛出此类异常。
针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示。
针对非CustomException异常,对这类重新构造成一个CustomException,异常信息为“未知错误”,此类错误需要在系统测试阶段去排除。
在统一异常处理器CustomExceptionResolver中实现上边的逻辑
public class CustomExceptionResolver implements HandlerExceptionResolver{
//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常,进行异常处理
//handler最终要指定的Handler,它的真实身份是HandlerMethod
@Override
public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex){
//输出异常
ex.printStackTrace();
//统一异常处理代码
//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常信息在错误页面展示
//异常信息
String message=null;
CustomException cex=null;
if(ex instanceof CustomException){
cex = (CustomException) ex;
}else{
//针对非CustomException异常,对这类重新构造成CustomException,异常信息为位置错误
cex = new CustomException("未知错误");
}
message = cex.getMessage();
request.setAttribute("message",message);
//转向到错误页面 这一句会抛出错误?
request.setRequestDispatcher("error,jsp").forward(request,response);
return new ModelAndView();
}
}
RESTful支持
RESTful软件开发理念,RESTful对http进行非常好的诠释。
RESTful即Representational State Transfer
的缩写
什么是RESTful架构:
每一个URI代表一种资源
客户端与服务器之间,传送这种资源的某种表现层
客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"
URL的RESTful实现
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....
RESTful的url是简洁的:http:// localhost:8080/items/editItems/1
参数通过url传递,rest接口返回json数据
需求:根据id查看商品信息,商品信息查看的连接使用RESTful方式实现,商品信息以json返回
第一步: 更改DispatcherServlet配置
<!-- restful的配置 -->
<servlet>
<servlet-name>springmvc_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载springmvc配置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置文件的地址 如果不配置contextConfigLocation,
默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml"
即:springmvc-serlvet.xml -->
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc_rest</servlet-name>
<!-- rest方式配置为/ -->
<url-pattern>/</url-pattern>
</servlet-mapping>
第二步: 参数通过URL传递
//@RequestMapping中指定RESTful方式的url中的参数,参数需要用{}括起来
//@PathVariable将url中{}括起来的参数和形参进行绑定
@RequestMapping("/viewItems/{id}")
public @ResponseBody ItemsCustom veiwItems(@PathVariable("id") Integer id) throws Exception{
ItemsCustom itemsCustom=itemsService.findItemsById(id);
return itemsCustom;
}
设置静态资源解析
当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就报错
需要通过设置对静态资源进行解析
<!-- 静态资源 解析 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/img/" mapping="/img/**"/>
SpringMVC拦截器
用户请求到DispatherServlet中,DispatherServlet调用HandlerMapping查找Handler,
HandlerMapping返回一个拦截的链儿(多个拦截),springmvc中的拦截器是通过HandlerMapping发起的。
在企业开发,使用拦截器实现用户认证(用户登陆后进行身份校验拦截),用户权限拦截
SpringMVC拦截器方法
public class HandlerInterceptor1 implements HandlerInterceptor {
//在执行handler之前来执行的
//用于用户认证校验、用户权限校验
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor1...preHandle");
//如果返回false表示拦截不继续执行handler,如果返回true表示放行
return false;
}
//在执行handler返回modelAndView之前来执行
//如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
//执行handler之后执行此方法
//作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
//实现 系统 统一日志记录
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
配置拦截器:
配置全局的拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping。
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<mvc:interceptor>
<!-- /*代表拦截一层路径 /**拦截多层路径 -->
<mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!-- /**可以拦截路径不管多少层 -->
<!-- <mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean>
</mvc:interceptor> -->
</mvc:interceptors>
执行preHandle是顺序执行。
执行postHandle、afterCompletion是倒序执行
如果preHandle不放行,postHandle、afterCompletion都不执行。
只要有一个拦截器不放行,controller不能执行完成
只有前边的拦截器preHandle方法放行,下边的拦截器的preHandle才执行
日志拦截器或异常拦截器要求:
将日志拦截器或异常拦截器放在拦截器链中第一个位置,且preHandle方法放行
2018年11月4日
MingFeng197