SpringMVC
针对表现层的 MVC
M : Model
V : View
C : Controller
不知道你们玩过Struts没,现在Struts都是一些传统的老项目维护在使用,基本是被市场淘汰了;
之前Struts出过几次比较重大失误,虽然后面也在更新,但是大家心里都怕了,所谓一朝被蛇咬,十年怕井绳,而SpringMVC又是由Spring提供的一个web层框架,Spring背后的社区力量可想而知,如今已经成为web层最优秀的框架;
SpringMVC主要是由前端控制器,处理器映射器,处理器适配器,后端控制器,视图解析器等组成
给我两块钱,我给你一张原理图:(面试常问)
第一步:用户向服务器发送请求,请求被Spring前端控制器 DispatcherServlet捕获;
第二步: DispatcherServlet调用HandlerMapping对请求URL进行解析,得到请求资源标识符(URL),获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
第三步: DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
第四步:提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: >> 1. HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 >> 2. 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 >> 3. 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期 >> 4. 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
第五步: Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
第六步:根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
第七步:ViewResolver 结合Model和View,将模型数据填充至视图中
第八步:将渲染结果返回给客户端。
SSM整合
:https://www.cnblogs.com/msi-chen/p/10531638.html
Controller返回值
ModelAndView: 一般不用 ,Model装载数据,View指定视图
Void : 一般用于返回Json数据,当然也可以在形参上定义request和response
String : 返回的是视图名,可以转发和重定向 return " redirect / forward:index" ; //配合视图解析器
当方法上有@ResponsaBody注解时,mvc不会解析其为跳转路径
POJO:配合@ResponsaBody使用
常用注解和诸多玩法
常用注解
@Conrtoller @RequestBody @Requestarem @ResponseBody
@ResponsaBody注解:将返回的数据封装到Http Response Body中,不会放置到Model中,mvc也不会解析为跳转路径
Json是默认的的一种返回格式(需要相关 jar 的支持)
@RequestBody注解 :可以将请求的Json字符串中的值绑定到Bean上
@RequestMapping : 定义出路起映射规则 value={"/itemList","listItem"},可以是单个值也可以是多个映射规则 数组形式
参数绑定
简单类型参数绑定 保证请求参数key和形参名保持一致即可(8中基本数据类型和包装类和String均受理)
@RequestParam : 请求参数中key和形参名称不一致是使用,进行参数绑定
@RequestParam(value=" ",required=true,default=" ") // 参数名称 -- 是否必须有值 -- 默认给值
绑定POJO,要求参数中的key 和POJO中的属性名得保持一致
绑定包装POJO,页面请求参数如下 stu.name 对应的包装类Class类内有一属性 stu stu内有一属性name
使用简单类型数组接收批量传递的简单数据类型数据,比如String[ ] 或者 POJO内的属性 String[ ]
比如这个:http://localhost:8080/xxx/deleteItem?id=1&id=2&id=3 就可以用Integer[ ] 接收
若相拥List去接收传递的请求参数的话,List必须是一个POJO类内的一个属性,而不能直接以List接收
如果要接收Date类型数据,需要自定义转换器:Conveter
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
配置在springmvc.xml中配置Conveter
<!-- 加载注解驱动 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 转换器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.kkb.ssm.controller.converter.DateConverter"/>
</set>
</property>
</bean>
异常处理器
自定义异常类,继承Excption;
自定义异常处理类 实现HandlerReceptionResolver;
在springmvc.xml中注册该类即可;
图片上传
springmvc的上传,是由commons-fileupload这个jar包实现的
文件上出啊需要指定<form>标签的一个属性 : enctype=”multipart/form-data”
在springmvc中配置Multipart解析器
<!-- multipart类型解析器,文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件的最大尺寸 5M-->
<property name="maxUploadSize" value="5242880"/>
</bean>
Controller类:
@RequestMapping(value = "/updateItem")
public String updateItem(Model model,Item item,MultipartFile pictureFile) throws Exception {
if(pictureFile != null){
System.out.println(pictureFile.getOriginalFilename());
//原始图片名称
String originalFilename = pictureFile.getOriginalFilename();
//如果没有图片名称,则上传不成功
if(originalFilename != null && originalFilename.length()>0)
{
//存放图片的物理路径
String picPath = "E:\\03-teach\\07-upload\\temp\\";
//新文件的名称
String newFileName = UUID.randomUUID()+originalFilename.substring(originalFilename.lastIndexOf("."));
//新的文件
File newFile = new File(picPath+newFileName);
//把上传的文件保存成一个新的文件
pictureFile.transferTo(newFile);
//同时需要把新的文件名更新到数据库中
item.setPic(newFileName);
}else{
throw new BusinessException("图片名称不存在,上传不成功");
}
}
// 根据页面传入的商品信息,调用修改方法,进行修改(此时还没有讲参数绑定,暂时无法进行)
itemService.updateItem(item);
return "success";
Json数据交互
KV结构,请求是Json(一般情况下),@ResponseBody默认响应也是Json 依赖 jackson-databind.jar 当然依赖传递进来的还有一个io.jar
Restful支持
理解:是一种软件架构风格,基于这个风格设计的软件更简洁,更有层次感,Rest指的就是一组架构条件而原则,满足额这个原则设计出来的应用就是Resuful风格
四个表示操作方式的动词: GET 、POST、PUT、DELETE,分别对应四种基本操作
springmvc对RESTful的支持:
web.xml中设置拦截规则为 / ,可以拦截RESTful请求
但也是因为设置了 /,就必须对静态资源进行访问处理
<!-- 当DispatcherServlet配置为/来拦截请求的时候,需要配置静态资源的访问映射 -->
<mvc:resources location=*"/js/"* mapping=*"/js/**"*/>
<mvc:resources location=*"/css/"* mapping=*"/css/**"*/>
@PathVariable注解可以解析出URL中的模版变量 如下所示:
请求URL:http://localhost:8080/ssm/item/1/wangbadan
@RequestMapping(" {id} / {name} ")
public void queryItemByIDAndName(@PathVariable Integer id,@PathVariable String name ){}
@RequestMapping注解可以通过method属性,可以将同一个请求映射到不同的方法上 GET/POST/PUT/DELETE
以至于优化出了以下注解 @GetMapping 、@PostMapping、@PutMapping、@DeleteMapping,效果同上
拦截器:HandlerInterceptor
springmvc有自己的拦截器,实现对请求前后的相关逻辑处理,相当于Servlet的Filter过滤器
springmvc中定义一个Interceptor有一下四种方式:
实现HandlerInterceptor接口,或继承实现了该接口的类 ,比如HandlerInterceptorAdapter
实现Spring的 WebRequestInterceptor接口,或者继承实现了该接口的类
一般常用第一种,实现HandlerInterceptor接口,重写
preHandle :Handler执行前执行,比如登录认证,身份授权,返回Boolean
postHandle :进入Handler,并在返回ModelAndView前执行,一般用于统一指定视图
afterCompletion :执完Handler之后执行,比如异常处理,日志处理,释放资源等
配置拦截器(全局拦截器配置):
<!-- 配置全局mapping的拦截器 -->
<mvc:interceptors>
<!-- 公共拦截器可以拦截所有请求,而且可以有多个 -->
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor1" />
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor2" />
<!-- 如果有多个拦截器,则按照顺序进行配置 -->
<mvc:interceptor>
<!-- /**表示所有URL和子URL路径 -->
<mvc:mapping path="/test/**" />
<!-- 特定请求的拦截器只能有一个 -->
<bean class="com.kkb.ssm.interceptor.MyHandlerInterceptor3" />
</mvc:interceptor>
</mvc:interceptors>
如果有多个拦截器,则配置到最上面的拦截器的优先级最高
SpringMVC父子容器
画了个图外加描述理解一下:
Spring和SpringMVC是两个容器;
Spring容器中存放着mapper代理对象,service对象,而SpringMVC中存放的事Controller对象,子容器可以通过@Autowired访问父容器注册过的中的Java实列,反之父容器想注入子容器中的Java实列就不行,比如在service中注入Controller行不通的;
两个容器导入的配置文件,都只能在自己的容器里面使用,不具有传递性
跨域处理
跨域:域名,端口,协议的组合不同的访问就是跨域
解决跨域的方式有多种:基于Js,基于Jq的JSONP以及基于CORS的方式
JSONP只能解决get方式提交触发的跨域问题,CORS支持多种提交方式
CORS是一个W3C标准,全称:“跨资源共享”,他允许浏览器向跨源服务器发起Ajax请求,克服了Ajax只能同源访问的限制
CORS原理:只需要向响应头header中注入Access-Control-Allow-Origin,这样浏览器检测到header中的Access-Control-Allow-Origin,则就可以跨域操作了。
详情见:https://www.cnblogs.com/msi-chen/p/10511558.html
乱码解决
Get提交:
手段一: 修改Tomcat的编码格式为 UTF-8
手段二:对请求参数进行重新编码,String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
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>
手段二:在RequestMapping注解中produces属性,指定响应体的编码格式