c:控制层-相当于中转站,中央处理器,负责分配任务,比如struts2的StrutsPrepareAndExecuteFilter,SpringMVC的DispacherServlet
m:模型层-相当于处理单元,逻辑单元,比如pojo,service,action
v:视图层-jsp,freemarker,excel,pdf
SpringMVC的处理流程(围绕DispacherServlet):
1、发起请求到前端控制器DispacherServlet
2、前端控制器请求处理器映射器HandleMapping查找handle,通过xml配置或者注解的方式进行查找
3、处理器映射器HandleMapping向前端控制器DispacherServlet返回Handler
4、前端控制器DispacherServlet调用处理器适配器HandleAdapter去执行Handle
5、处理器适配器HandleAdapter执行Handle
6、Handler(后端控制器controller)执行完给处理器适配器HandleAdapter返回ModelAndView
7、处理器适配器HandleAdapter给前端控制器DispacherServlet返回ModelAndView
8、前端控制器DispacherServlet请求视图解析器ViewResolver去进行视图解析
9、视图解析器ViewResolver给前端控制器DispacherServlet返回View.
10、前端控制器DispacherServlet进行视图渲染--将ModelAndView中的model填充到request域
11、前端控制器DispacherServlet向用户响应结果
· 用户发送请求至前端控制器DispatcherServlet
· DispatcherServlet收到请求调用HandlerMapping处理器映射器。
· 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
· DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
· 执行处理器(Controller,也叫后端控制器)。
· Controller执行完成返回ModelAndView
· HandlerAdapter将controller执行结果● ModelAndView返回给DispatcherServlet
· DispatcherServlet将ModelAndView传给● ViewReslover视图解析器
· ViewReslover解析后返回具体View
· DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
· DispatcherServlet响应用户。
组件:
1、前端控制器DispacherServlet(需要在web.xml中配置)作用:用于接收请求,响应结果,相当于转发器
2、处理器映射器HandleMapping(spring提供很多映射器,需要配置)
作用:根据请求的url查找Handle
3、处理器适配器HandleAdapter(spring提供很多适配器,需要配置)
作用:检测符合HandleAdapter规定要求的Handle,然后执行Handle(HandleApapter比如SimpleControllerHandleAdapter会有一个方法校验是否继承了Controller接口 -- instanceOf ,只有继承了Controller接口,SimpleControllerHandleAdapter才会执行这个Handle)
4、处理器Handle(需要开发)
编写Handle时按照handleAdapter的要求(比如SimpleControllerHandleAdapter这个适配器要求Handle必须实现Controller接口)去做,
这样适配器才能正确执行handle
5、视图解析器View resolver(spring提供很多适配器,需要配置)
作用:运行视图解析,根据逻辑视图名解析真正的视图View
6、视图View(需要开发)
view是一个接口,实现类支持不同的view类型(jsp,freemarker,pdf...)
web.xml中配置前端控制器:
<!--springmvc前端控制器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
第一种:*.action: .action结尾由DispacherServlet进行解析
第二种: /,访问的地址都由DispacherServlet进行解析,对于静态文件的解析需要配置不让他解析,需要配置静态文件目录,RESTful风格有讲解
使用此方式可以实现RESTful风格的url-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
Servlet拦截匹配规则可以自已定义,拦截哪种URL合适?
当映射为@RequestMapping("/user/add")时,为例:
1、拦截*.do、*.htm,
例如:/user/add.do
这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。
2、拦截/,例如:/user/add
可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。
弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。想实现REST风格,事情就是麻烦一些。后面有解决办法还算简单。
3、拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。
非注解配置:
BeanNameUrlHandlerMapping、SimpleControllerHandleAdapter:
<!--配置Handler-->
<bean id="itemsController" name="/queryItems.action" class="xx.xx.xx.xxx.ItemsController"/>
<!--配置非注解映射器,配置这个映射器,适配器将会把bean的name作为url进行查找,需要在配置handler时指定bean name(url)-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--配置非注解处理器适配器,所有处理器适配器都实现HanlerAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandleAdapter"/>
<!--视图解析器,解析jsp,默认使用jstl标签,classpath下必须有jstl包-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀和后缀,配置之后
request.getRequestDispacher("/WEB-INF/jsp/items/itemlist.jsp").forword(request,response);
可以写成
request.getRequestDispacher("items/itemlist").forword(request,response);-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="sufix" value=".jsp"/>
</bean>
我们知道处理器Handle的编写会按照适配器的规则来定,那么SimpleControllerHandleAdapter这个适配器要求Handle必须实现Controller接口,返回ModelAndView:
public class ItemsController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception{
//xxx
ModelAndView mv = new ModelAndView();
mv.addObject("itemLists" ,itemsLists);
mv.setViewName("/WEB-INF/jsp/items/itemlist.jsp");
return mv;
}
}
SimpleUrlHandlerMapping、HttpRequestHandlerAdapter:
<!--简单url映射,另外一个映射器配置-->
<bean class="org.springframwork.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!--key对应url名称,value对应handle的id-->
<!--这种映射器可以给一个handle配置多个url链接-->
<prop key="/queryItems.action">itemsController</prop>
<prop key="/queryItems2.action">itemsController</prop>
</props>
</property>
</bean>
<!--另外一个非注解适配器,所有的handle必须实现HandleAdapter接口-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
Handle必须实现HttpRequestHandlerAdapter接口,没有返回值:
public class ItemsController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception{
//xxx
request.setAttribute("itemLists" ,itemsLists);
//设置转发的视图
request.getRequestDispacher("/WEB-INF/jsp/items/itemlist.jsp").forword(request,response);
//此方法可以通过修改response,设置相应的数据格式,比如相应json数据
//response.setCharacterEncoding("utf-8");
//response.setContentType("application/json,charset=utf-8");
//response.getWriter.write("jsonStr");
}
}
总结:
1、springmvc提供了 多个处理器适配器HandleAdapter, 多个处理器映射器HandleMapping,上面代码是有两个非注解的HandleAdapter
(HttpRequestHandlerAdapter,SimpleControllerHandleAdapter)和两个非注解的HandleMapping(BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping),
实际开发用的不多,用的多的是注解HandleMapping(RequestMappingHandleMapping)和HandleAdapter(RequestMappingHandlerAdapter)
因为首先配置挺麻烦,而且根据这两个适配器写的handle,内部只能实现一个方法handleRequest,非常不方便
2、多个映射器可以并存,前端控制器判断url能让哪些映射器映射,就让正确的映射器处理
3、即使上面的代码不配置适配器,也不配置映射器,都能正常执行代码,为什么呢??
因为org.springframework.web.servlet下有一个默认配置文件DispatcherServlet.properties,他把所有的适配器和映射器都加载进来了
所以可以不配置都行。配置如下:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
但是它的注解的映射器和适配器,配置的是3.1版本以下的,3.1以上版本我们不用这两个类,所以注解映射器和注解适配器要需要我们自己配置,见下面详解
注解配置:
<!--注解映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注解适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--使用mvc:annotation-driver代替上边注解映射器RequestMappingHandlerMapping和注解适配器RequestMappingHandlerAdapter的配置
mvc:annotation-driven默认加载很多的参数绑定方法,比如json转换器就默认加载了,实际开发使用这种配置,去掉上面的bean配置-->
<mvc:annotation-driven></mvc:annotation-driven>
Handle使用注解:
@Controller
public class ItemsController{
//@RequestMapping实现对queryItems方法的url进行映射,一个方法对应一个url
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception{
}
}
使用注解进行开发,除了加注解,还要配置注解扫描的包
<!--可以扫描controller,service....-->
<context:component-scan base-package="xxx.xx.xx.controller"></context:component-scan>
注意:
注解处理器适配器和注解映射器是配对使用,RequestMappingHandlerMapping、RequestMappingHandlerAdapter是couple
但是我们有更好的配置<mvc:annotation-driven></mvc:annotation-driven>,单独配置这个即可。无需配置couple
springmvc核心配置:
<!--注解扫描类-->
<context:component-scan base-package="xx.xx.xx.controller"></context:component-scan>
<!--配置注解处理器映射器和处理器适配器-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="sufix" value=".jsp"/>
</bean>
@RequestMapping:
1、url映射:定义controller方法对应的url,进行处理器映射使用。
2、窄化请求映射:
//为了对url进行管理,可以在这里定义根路径,最终访问url是根路径+子路径
@Controller
@RequestMapping("/items")
public class ItemsController{
}
3、限制http请求方式
//http请求方式可以是post和get
@RequestMapping(value="toEditItemsUI",method={RequestMethod.POST,RequestMethod.GET})
public ModelAndView toEditItemsUI() throws Exception{
}
controller的几种返回值:
1、返回ModelAndView:
@RequestMapping(value="toEditItemsUI",method={RequestMethod.POST,RequestMethod.GET})
public ModelAndView toEditItemsUI(Integer id) throws Exception{
ItemsCustom itemCustom = itemsService.findItemsById(id);
//返回modelAndView
ModelAndView mv = new ModelAndView();
//将商品信息放到mv
mv.addObject("itemsCustom", itemCustom);
//商品修改页面
mv.setViewName("items/editItemsUI");
return mv;
}
2、返回string(类似strut2开发)
如果controller方法返回string,表示返回逻辑视图名。真正视图(jsp路径)= 前缀+ 逻辑视图名 + 后缀 ,这种写法更方便, 推荐使用:
@RequestMapping(value="toEditItemsUI",method={RequestMethod.POST,RequestMethod.GET})
public String toEditItemsUI(Integer id,Model model) throws Exception{
ItemsCustom itemCustom = itemsService.findItemsById(id);
//返回modelAndView
//ModelAndView mv = new ModelAndView();
//将商品信息放到mv
//mv.addObject("itemsCustom", itemCustom);
//商品修改页面
//mv.setViewName("items/editItemsUI");
//注意这种写法更简单
model.addAttribute("itemsCustom",itemCustom);
return "items/editItemsUI"
//redirect重定向:
//特点:url变化,request数据无法传递到重定向的地址,因为重定义是重新进行request,二次请求(request无法共享)
//比如修改成功后,返回到原来的列表
//return "redirect:queryItems.action";
//forward页面转发
//url不变,request共享
//return "forward:queryItems.action"; //在同一个类,所以不要根目录
}
3、返回void(类似原始servlet开发,要加request,response形参)
这种需要在controller方法形参上定义request和response,使用request和response来指定响应结果:
@RequestMapping(value="toEditItemsUI",method={RequestMethod.POST,RequestMethod.GET})
public void toEditItemsUI(HttpServletRequest request,HttpServletResponse response) throws Exception{
//使用request转向页面
request.getRequestDispacher("页面路径").forward(request,response);
//重定向
request.sendRedirect("url");
//也可以通过response指定相应结果,例如响应json数据
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(jsonstr);
}
springmvc参数绑定:
从客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上,
springmvc中,接收页面提交的参数是通过方法的形参来接收,而不是和struts2一样,在controller中定义成员变量来接收。
参数绑定是处理器适配器HandleAdapter处理。因为是HandleAdapter来执行Handle(Controller)
处理器适配器调用springmvc提供的参数绑定组件将key/value数据转成controller方法的形参。
参数绑定组件:在springmvc早期版本使用PropertyEditor(只能将字符串转成java对象)
后期使用Converter(进行任意类型的转换)
springmvc提供了很多converter(转换器)
在特殊情况下需要自定义converter eg.对日期数据绑定需要自定义converter
参数绑定默认支持的类型:
1、HttpServletRequest
2、HttpServletResponse
3、HttpSession
4、Model/ModelMap
model是一个接口,modelMap是一个接口实现
作用:将model数据填充到request域,返回参数为string的controller方法,就要用Model形参,
model.addAttribute("itemsCustom",itemCustom);
5、数组格式绑定
需求:批量删除(type="checkbox" name="item_id")
形参: Integer[] item_id 或者写在 pojo里面
6、list绑定
需求:批量提交数据,批量提交数据(输入多个商品,批量提交)
形参:List<Pojo> pojoLists 或者写在 pojo里面
<c:foreach items="${itemsList}" var="item" varStatus="status">
<tr>
<td><input type="text" name="itemsList[${status.index}].name" value="${item.name}"/></td>
<td><input type="text" name="itemsList[${status.index}].price" value="${item.price}"/></td>
</tr>
</c:foreach>
7、Map绑定
<input type="text" name="itemInfo['name']"/>
Map<String,Object> itemInfo
自定义支持的类型:
1、简单类型(Integer,Float,Double,String,Short,Long,Boolean):
只要形参的定义和表单name属性定义的名字一致,就可以绑定成功。
如果不一致,就需要使用@RequestParam来定义和表单元素name字段一样的名称
比如:
/**
* required 为是否必填
* defaultValue 为指定默认值
*/
public String toEditItemsUI(Model model,@RequestParam(value="id",required=true,defaultValue="1") Integer itemId) throws Exception{
//这样可以绑定<input type="text" name="id"/>传递过来的值
}
2、pojo绑定
页面中input的name属性和controller的pojo形参中属性名称一致,就可以将页面中数据绑定到pojo
3、自定义参数绑定实现日期类型绑定
原理:将input传递过来的日期字符串转换成java.util.Date
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!--转换器-->
<property name="converters">
<list>
<bean class="xx.xxx.xxx.controller.convrter.CustomDateConverter"/>
</list>
</property>
</bean>
<!--参数绑定是适配器做的事,这个配置是添加注解适配器和注解映射器,所以在这里配置-->
<mvc:annotation-driver conversion-service="conversionService"></mvc:annotation-driver>
/**
* 两个泛型参数的意思就是:
* 要把String转成Date
*/
public class CustomDateConverter implements Convert<String,Date>{
@Override
public Date convert(String source){
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
return df.parse(source);
}catch(Exeption e){
//绑定失败返回null
return null;
}
}
}
服务端校验:
项目中,通常使用较多的是前端校验,比如js校验,对于安全性要求较高的建议在服务器端进行校验。
服务端校验:
控制层controller:校验页面请求参数的合法性,在服务端控制层controller校验,不区分客户端类型(浏览器,移动端,远程调用)
业务层service: 使用较多,主要校验关键业务参数,仅限于service接口中使用的参数
持久层dao:一般不校验
springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)
校验思路:页面提交请求的参数,请求到controller方法中,使用validation进行校验,如果出错,将错误信息返回页面
引入包:
hibernate-validator-4.3.0.Final.jar
jboss-logging-3.1.CR2.jar
validation-api-1.0.0.GA.jar
配置检验器:
<!--校验器-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!--校验器提供类,这里引用hibernate的validation-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator">
<!--校验器使用的资源文件,如果不指定默认使用classpath下的validationMessages.properties-->
<property name="validationMessageSource" ref="messageSoource"/>
</bean>
<!--校验信息配置文件-->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessage">
<!--资源文件名-->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!--资源文件编码格式-->
<property name="fileEncoding" value="utf-8"/>
<!--对资源文件内容缓存时间,单位秒-->
<property name="cacheSeconds" value="120"/>
</bean>
<!--校验器注入到处理器适配器:-->
<!--conversionService配置的是类型转换,validator是配置的校验器-->
<mvc:annotation-driven conversion-service="conversionService" validation="validator"></mvc:annotation-driven>
应用:
public class Items{
//校验名在1~30之间
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
//非空校验
//NotNull(message="{items.createtime.isNull}")
private Date createTime;
}
CustomValidationMessages.xml
#添加校验错误提交信息
items.name.length.error=请输入1到30个字符的商品名字
items.createtime.isNull=请输入商品的生成日期
在controller的写法:
//在需要校验的pojo之前加入注解@Validated,在需要校验的pojo后边添加BindingResult接收校验出错信息
//注意@Validated和BindingResult bindingResult是成对出现,并且新参顺序是固定的(一前一后)
@RequestMapping("/editItemsUI")
public String editItemsUI(Model model,HttpServletRequest request,Integer id,
@Validated ItemsCustom itemsCustomer, BindingResult bindingResult) throws Exception{
if(bindingResult.hasError()){
//输出错误信息
List<ObjectError> allErrors=bindingResult.getAllErrors();
for(ObjectError objectError : allErrors){
syso(objectError.getDefaultMessage());
}
//将错误信息传到页面
model.addAttribute("allErrors",allErrors);
return "items/editItemsUI";
}
}
<c:if test="${allErrors!=null}">
<c:foreach items="${allErrors}" var="error">
${error.defaultMessage}
</c:foreach>
</c:if>
分组校验:
问题:我可能要在添加商品,修改商品都要校验商品Items pojo校验,要用到Items类,但是我修改商品,不要校验createTime字段!
定义分组接口:
public interface ValidGroup1{
//不定义任何东西,仅仅对不同的校验规则进行分组
//这个分组只校验商品名字
}
@RequestMapping("/editItemsUI")
public String editItemsUI(Model model,HttpServletRequest request,Integer id,
@Validated(value={ValidGroup1.class}) ItemsCustom itemsCustomer, BindingResult bindingResult) throws Exception{
}
@Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class})
private String name;
pojo数据回显:
1、springmvc默认对pojo数据进行回显
pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)
@RequestMapping("/editItemsUI")
public String editItemsUI(Model model,HttpServletRequest request,Integer id,
@Validated ItemsCustom itemsCustomer, BindingResult bindingResult) throws Exception{
//相当于这一步springmvc会默认操作,将传入进来的pojo放到request域,那么前端jsp用el表达式可以获取到数据
model.addAttribute("itemsCustomer",itemsCustomer);
return "items/editItemsUI";
}
<!--回显的key=pojo类型的首字母小写-->
<input type="text" name="name" value="${itemsCustomer.name}"/>
<input type="text" name="price" value="${itemsCustomer.price}"/>
问题:如果jsp写的不是${itemsCustomer.name}而是${items.name},回显的key不同,那么默认回显就不会成功,怎么办??
@ModelAttribute作用:
1、指定pojo回显到页面在request中的key
2、可以将方法的返回值传到页面(没有url,写法很变态,不研究)
那么问题的解决方法就是:
//指定回显到jsp的request的key为items
@RequestMapping("/editItemsUI")
public String editItemsUI(Model model,HttpServletRequest request,Integer id,
@ModelAttribute("items") @Validated ItemsCustom itemsCustomer, BindingResult bindingResult) throws Exception{
//相当于这一步springmvc会默认操作,将传入进来的pojo放到request域,那么前端jsp用el表达式可以获取到数据
//model.addAttribute("itemsCustomer",itemsCustomer);
return "items/editItemsUI";
}
那么这样的jsp将能够回显:
<input type="text" name="name" value="${items.name}"/>
简单类型回显:
最简单解决回显的问题,是不用@ModelAttribute,而是使用model
就是手动设置request域,也就是
model.addAttribute("itemsCustomer",itemsCustomer);
简单类型的回显,必须用这种方法:
model.addAttribute("id",id);
全局异常处理:
异常包括两类:预期异常,运行时异常RuntimeExeption
预期异常通过捕获异常从而获取异常信息,运行时异常主要通过规范代码开发,测试手段减少异常的发生。
springmvc提供全局异常处理器HandleExceptionResolver(一个系统只有一个异常处理器)进行统一异常处理
处理思路:
系统遇到异常,在程序中手动抛出,dao抛给service,service给controller,controller抛给前端控制器,前端控制器 调用全局处理器。
全局处理器处理思路:
解析出异常类型
如果是自定义异常,获取异常消息,在错误页面显示
如果不是自定义异常,构造一个自定义异常类型,消息为“未知错误”
自定义异常类:
public class CustomException extends Exception{
private String message;
public CustomException(String message){
super(message);
this.message= message;
}
//set ,get
}
定义全局异常处理类:
public class CustomExceptionResolver implements HandleExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,
Object handler, Exception ex){
CustomException exception = null ;
if(ex instanceof CustomException){
exception = (CustomException)ex;
}else{
exception = new CustomException("未知错误");
}
ModelAndView mv = new ModelAndView();
mv.addObject("message", exception.getMessage());
mv.setViewName("error");
return mv;
}
public ItemsCustom findItemsById(Integer id) throws Exeption{
Items items = itemsMapper.selectByPrimaryKey(id);
if(items==null){
throws new CustomException("商品信息不存在");
}
return items;
}
如果
与业务功能相关的异常,建议在service中抛出异常,
与业务无关的异常,建议在controller中抛出
上面的功能,建议在service中抛出,与业务有关!
最后,在springmvc.xml中配置全局异常处理器:
<!--全局异常处理器,只要实现HandleExceptionResolver接口就是全局异常处理器-->
<bean class="xx.xx.xxx.xx.CustomExceptionResolver"/>
中文乱码:
post乱码:
<!-- 编码过滤,防止乱码 -->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
get乱码:
解决方法一:
修改tomcat配置文件添加编码与工程编码一致:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
添加这个配置:URIEncoding="utf-8"
解决方法二:
String username = new String(request.getParameter("username").getBytes("ISO8859-1"), "UTF-8");
因为我们工程一般用的utf-8,而tomcat的默认编码为IS08859-1
springmvc和struts2区别:
1、springmvc基于方法开发的,struts2是基于类开发的
springmvc将url和controller中的方法映射,映射成功后springmvc生成一个handle对象,对象只包括一个method,方法执行结束,形参数据销毁
2、springmvc可以进行单例开发,也建议用单例开发,因为他没有成员变量,成员变量也是注入的方法,struts用成员变量接收参数,必须用多例开发。
3、springmvc的controller开发类似service开发,因为要什么参数,就可以在形参中定义参数。
4、经过实际测试,struts2的速度慢,在于适用struts2标签,因此建议适用jstl标签。
5、struts2用的比较多,漏洞就比较多,所以用struts2,应该用最新的包
6、struts2使用成员变量接收参数的,那么类一多,成员变量维护比较麻烦。
springmvc文件上传
<form id="itemForm" action="" method="post" enctype="multipart/form-data">
在springmvc中配置multipart类型转换器
<!-- 配置文件上传,如果没有使用文件上传可以不用配置,当然如果不配,那么配置文件中也不必引入上传组件包 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默认编码 -->
<property name="defaultEncoding" value="utf-8" />
<!-- 文件大小最大值 -->
<property name="maxUploadSize" value="800000000" />
<!-- 内存中的最大值 -->
<property name="maxInMemorySize" value="40960" />
</bean>
在图片虚拟目录中,一定将图片目录进行分级创建(提高I/O性能),一般我们采用按日期进行分级创建
handle,采用MultipartFile进行接收
@RequestMapping("/updateItems")
public String updateItems(Model model,MultipartFile images){
if(images!=null){
String originalName = images.getOriginalFilename();
String newName = UUID.randomUUID() + originalName.subString(originalName.lastInexOf("."));
File newFile = new File(realPath + newName);
//将内存中的数据写入磁盘
images.transferTo(newFile);
}
}
Springmvc Json交互:
1、请求json,输出json,在前端还要讲请求内容转换成json,不太方便
2、请求key/value,输出json,常用
两个springmvc实现json交互的注解:
@RequestBody 将json串转成java对象
@ResponseBody将java对象转换成json串
contentType:默认为"application/x-www-form-urlencoded"
这个告诉服务器,请求的参数类型是key/value类型的
如果要传递的参数为json类型
contentType: "aplication/json"
json参数调用,json数据返回案例:
$.ajax({
type:"post",
url : '${pageContext.request.contextPath}/xxx.action',
contentType:'application/json;charset=utf-8',
//如果是key/value,就用默认的application/x-www-form-urlencoded,所以不必加contentType
data: '{"name":"手机","price":99}',
success:function(data){
//返回参数
}
});
@RequestMapping("/xxx")
public @ResponseBody ItemsCustom xxx(@RequestBody ItemsCustom itemsCustom){
ItemsCustom bean = itemsService.updateItems(itemsCustom);
return bean;
}
RESTful(Representational State Transfer)
RESTFUL是一个开发理念
REST风格:
http://../items/001
特点:url简洁,将参数通过url传到服务端
spring实现RESTful风格的请求,需要用到一个注解:@PathVariable
作用:用于将URL中的模板变量,映射到功能处理方法的参数上
@RequestMapping("/itemsView/{id}")
public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer id) throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
支持RESTful的前端控制器配置:
<!-springmvc的前端控制器,REST风格,必须配置url-pattern为/ -->
<!--可以配置两种风格,当配置成/,所有请求都走springmvc的前端控制器,这样静态资源将无效-->
<servlet>
<servlet-name>SpringMVC_REST</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config/spring-mvc.xml</param-value>
</init-param>
<!-- 启动顺序,让这个Servlet随容器一起启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC_REST</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
静态资源解析:
<!-- js,css,html,img... -->
<mvc:resources mapping="/javascript/**" location="/static_resources/javascript/"/>
<!--页面引用到/styles/**的资源都从/static_resources/css里面进行查找-->
<mvc:resources mapping="/styles/**" location="/static_resources/css/"/>
<mvc:resources mapping="/images/**" location="/static_resources/images/"/>
过滤器:
public class HandleInterceptor1 implements HandleInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse,
Object handler) throws Exception{
//进入Handle方法之前执行
//true表示放行,false表示拦截
//应用场景:用于身份验证,身份授权
return true;
}
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse,
Object handler ,ModelAndView modelAndView) throws Exception{
//进入Handle方法之后,返回ModelAndView之前执行
}
@Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse,
Object handler ,Exception ex) throws Exception{
//执行Handle完成执行此方法
//应用场景:统一异常处理,统一日志处理
}
}
springmvc拦截器针对HandleMapping进行拦截设置
如果在HandleMapping中配置拦截器,经过该HandleMapping映射成功的Handle最终使用该拦截器
<bean class="org.springframework.web.servlet.handle.BeanNameUrlHandleMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="xx.xx.xx.HandleInterceptor1"/>
<bean id="handlerInterceptor2" class="xx.xx.xx.HandleInterceptor2"/>
一般不推荐使用
类似全局拦截器(用的多):springmvc配置类似全局的拦截器,注入到每个HandleMapping
<mvc:interceptors>
<mvc:interceptor>
<!--多个拦截器,顺序执行-->
<!-- /**表示所有url包括子url路径-->
<mvc:mapping path="/**"/>
<bean class="xx.xxx.xx.xx.HandleInterceptor1></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--多个拦截器,顺序执行-->
<!-- /**表示所有url包括子url路径-->
<mvc:mapping path="/**"/>
<bean class="xx.xxx.xx.xx.HandleInterceptor2></bean>
</mvc:interceptor>
</mvc:interceptors>
测试拦截器:
拦截器1放行,2放行:preHandle1
preHandle2
postHandle2
postHandle1
afterCompletion2
afterCompletion1
总结:
perHandle方法顺序执行
postHandle、afterCompletion按拦截器配置的逆向顺序执行。
1放行,2不放行:
打印:
preHandle1
preHandle2
afterCompletion1
1不放行,2不放行:
打印:
preHandle1
比如: 统一日志处理拦截器,需要该拦截器preHandle一定要放行,且将他放到拦截器链的第一位。因为放在第一位且放行,
才能保证afterCompletion一定执行,如果不在第一个位置,不能保证其他拦截器有没有放行,如果不放行,afterCompletion就不能执行。
比如:登录认证拦截器,放在拦截器链第一位,权限校验拦截器,放在登录拦截器之后(因为登录了才验证权限)
登录校验:
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse,
Object handler) throws Exception{
String url = request.getRequestURI();
//判断url是否是公共地址(实际项目需要配置到配置文件中)
if(url.indexOf("login.action")>=0){
return true;
}
HttpSession session = request.getSession();
//.....
if(xxx) return true;
//跳转登录界面
request.getRequestDispacher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
tomcat技巧:
add External Web Module...
将物理目录作为虚拟目录映射到tomcat
配置后,相当于在server.xml中添加
<Context docBase="F:\develop\imageServer" path="/pic" reloadable="false"/>