Springmvc一览

mvc模型:
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"/>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值