springmvc
DispatcherServlet:前端控制器
概念:spring在web层的一个mvc架构的框架
作用:
处理用户请求与相应
渲染视图
特点:
清晰的角色划分,各司其职
可定制的映射器,处理器
灵活的model转换
spring标签库
功能:请求、响应、模型、视图
快速入门
导包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
1、web.xml配置前端控制器
<servlet>
<servlet-name>dispatherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2、编写springmvc.xml
<context:component-scan base-package="com.springmvc.controller"/>
3、编写Controller
@Controller
public class UserController {
// login这个处理器与url进行绑定
@RequestMapping("login")
public ModelAndView login(Integer username) {
ModelAndView mav = new ModelAndView();
mav.addObject("user", username);// model,相当于将数据放到request域对象中
mav.setViewName("user.jsp");// view
return mav;
}
}
springMVC大致原理
以下组件通常使用框架提供实现:
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
Handler:处理器
Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
View Resolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstl、freemarker、pdf等。我们最常用的视图就是jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。 需要用户实现的组件有handler、view
springMVC原理在代码中的体现
1、根据请求的路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法)
2、然后匹配路径对应的拦截器
3、通过HandlerMapping接口实现类获得HandlerExecutionChain对象(包含HandlerMethod和拦截器)
4、有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象
调用HandlerMethod内部handle的时候:
1、使用各种HandlerMethodArgumentResolver接口实现类,完成参数绑定2、使用到各种Converter接口实现类,完成类型转换3、使用各种HandlerMethodReturnValueHandler接口实现类,处理返回值4、最终返回值被处理成ModelAndView对象5、这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理
5、RequestToViewNameTranslator接口实现类将请求地址解析为视图名(若有手动设置视图名,则使用手动设置的)
6、通过各种View和ViewResolver接口实现类渲染视图(将Model中的数据放到request域对象中,页面的编译。。。)
非注解方式配置springMVC
在DispatherServlet.propterties文件中
HandlerMapping= BeanNameUrlHandlerMapping, DefaultAnnotationHandlerMapping(该类已过期)
HandlerAdapter= HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnotationMethodHandlerAdapter(该类已过期)
HandlerExceptionResolver= AnnotationMethodHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver
RequestToViewNameTranslator= DefaultRequestToViewNameTranslator
ViewResolver= InternalResourceViewResolver
FlashMapManager= SessionFlashMapManager
开启注解扫描的方式配置springMVC
<mvc:annotation-driven />
作用:
创建RequestMappingHandlerMapping
创建RequestMappingHandlerAdapter
注册Converter的各种实现类,例如json转换,日期格式转换等
1、RequestMappingHandlerMapping
作用:注解式处理器映射器,对类中标记@ResquestMapping的方法进行映射,根据ResquestMapping定义的url匹配ResquestMapping标记的方法,匹配成功返回HandlerMethod对象给前端控制器,HandlerMethod对象中封装url对应的方法Method。
注意:从spring3.1版本开始,废除了DefaultAnnotationHandlerMapping的使用,推荐使用RequestMappingHandlerMapping完成注解式处理器映射。
配置如下:
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
2、RequestMappingHandlerAdapter
作用:注解式处理器适配器,对标记@ResquestMapping的方法进行适配。
注意:从spring3.1版本开始,废除了AnnotationMethodHandlerAdapter的使用,推荐使用RequestMappingHandlerAdapter完成注解式处理器适配。
配置如下:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
视图与模型
视图
springmvc.xml配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
//返回的字符串就是视图名称,提前是在springmvc.xml中配置了后缀名
@RequestMapping("view1")
public String view1(){
return "formString";
}
//如果返回返回值,那么默认视图名为请求地址(约定优于配置原则)
@RequestMapping("formString")
public void view2(){}
模型数据
//Model的本质就是map集合,视图解析器会将Model中的数据放到request域对象中
//原始的写法,使用ModelAndView
@RequestMapping("model1")
public ModelAndView model1(){
ModelAndView mav=new ModelAndView();
mav.addObject("user", new User("lisi","123"));
mav.setViewName("model1");//如果这句不写,那么使用约定的视图名
return mav;
}
//简化了上面的写法
@RequestMapping("model2")
public String model2(Model model){
model.addAttribute("username", new User("lisi","123"));
return "model1";
}
//再简化了上面的写法,如果没有返回视图名,那么默认使用请求地址作为视图名
@RequestMapping("model3")
public void model3(Model model){
model.addAttribute("user", new User("lisi","123"));
}
//------------------综合--------------------
@RequestMapping("model4")
public void model4(Model model,User user){
//将user添加到Model中
model.addAttribute("user", user);
}
//简化上面的写法
@RequestMapping("model5")
public void model5(@ModelAttribute User user){//直接将参数绑定好后的user添加到Model中
}
@ModelAttribute("user")
//将返回值暴露为模型数据,因为没有@RequestMapping注解,所以该方法不是处理器,它在每个处理器之前执行
public User getUser(){
return new User("zhangsan", "123");
}
请求与响应
参数绑定
可以被注入的类型: HttpServletRequest, HttpServletResponse, HttpSession, Principal, Locale, InputStream, Reader, OutputStream, Writer, String, StringBuffer, Pojo, Date, List, Map, Array, Model, ModelMap等等
转换服务
转换服务为参数绑定时,处理数据类型的转换。
DefaultConversionService:默认的类型转换服务实现
DefaultFormattingConversionService:带数据格式化支持的类型转换服务实现,一般使用该服务实现即可。
springmvc在开启注解驱动,注册了很多Converter的实现类,但是如果不能满足需求,可以自定义转换器。具体如下:
//实现Converter接口
public class MyDateConverter implements Converter<String, Date>{
//重写convert方法
@Override
public Date convert(String str) {
SimpleDateFormat sdf=null;
//java正则表达式不用写/开头和结尾,js中需要
Pattern pattern1=Pattern.compile("^\\d{4}年\\d{1,2}月\\d{1,2}日$");
Pattern pattern2=Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$");
Pattern pattern3=Pattern.compile("^\\d{4}/\\d{2}/\\d{2}$");
try {
if(pattern1.matcher(str).matches()){
sdf=new SimpleDateFormat("yyyy年MM月dd日");
return sdf.parse(str);
}
if(pattern2.matcher(str).matches()){
sdf=new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(str);
}
if(pattern3.matcher(str).matches()){
sdf=new SimpleDateFormat("yyyy/MM/dd");
return sdf.parse(str);
}
//或者下面的写法
/*if(str.matches("^\\d{4}年\\d{2}月\\d{2}日$")){
sdf=new SimpleDateFormat("yyyy年MM月dd日");
return sdf.parse(str);
}
if(str.matches("^\\d{4}-\\d{2}-\\d{2}$")){
sdf=new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(str);
}
if(str.matches("^\\d{4}/\\d{2}/\\d{2}$")){
sdf=new SimpleDateFormat("yyyy/MM/dd");
return sdf.parse(str);
}*/
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
将自定义的转换器添加到springmvc转换服务中
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.hemi.controller.converter.MyDateConverter"/>
</list>
</property>
</bean>
注意: 1、如果是单纯的日期格式转换可以使用@DateTimeFormat来实现
@DateTimeFormat(pattern="yyyy年MM月dd日")//写了该注解,默认的yyyy/MM/dd hh:mm:ss的格式就无法使用了
private Date date;
2、上面的转换只能处理url参数和请求体参数,如果是json中的日期格式转换,请使用@JsonFormat注解,请看下面的json部分。
json
导包
1 将json-->bean
2 将bean-->json
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.1</version>
</dependency>
1、json数据发送
后台:
1、@RequestBody
2、jackson的3个jar包
@RequestMapping("jsonToBean")
@ResponseBody //返回的对象封装成json
public User jsonToBean(@RequestBody User user){//@RequestBody将请求的json封装成对象
return user;
}
前台:
1、contentType:appliation/json;charset=utf-8
2、JSON.stringify(json),
var json={username:"lisi",password:"123"};//定义json对象
$(function(){
$("#btn").click(function(){
$.ajax({
url:"/springmvc/jsonToBean",
type:"post",
contentType:"application/json;charset=utf-8",
//js原生的一个将json对象转成json字符串的工具类
data:JSON.stringify(json),
dataType:"text",
success:function(data){
$("#content").html(data);
}
});
});
});
2、解决json日期格式化问题
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date date;
3、解决json写入@ResponseBody后出现中文乱码的问题
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--解决返回json乱码-->
<value>application/json;charset=UTF-8</value>
<!--解决返回字符串乱码-->
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
<!--或者下面这种方式-->
<!-- <mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters> -->
</mvc:annotation-driven>
4、json其他常用的注解
@JsonInclude(JsonInclude.Include.NON_NULL)//当属性为null的时候,就不序列该属性,不为null就序列化
public class User {
@JsonIgnore //忽略某个属性,不要被json序列化
private Integer id;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date date;
}
文件上传下载
文件上传
1、基本步骤
1、handler参数添加@RequestParam MultipartFile multipart
//name指的是<input name=""> 注意:不能是驼峰命名,必须都是小写
@RequestParam(name="user_photo",required=false) MultipartFile multipart
2、调用MultipartFile的transforTo(file)的方法,将文件写入到本地
3、springmvc.xml文件配制CommonsMultipartResolver视图
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
4、上传文件的jsp
2、注意问题
上传文件的路径
String path = req.getServletContext().getRealPath("statics" + File.separator + "uploadfile");
如果项目没有发布到tomcat服务器中,那么默认是在eclipse的对应项目目录中,这样导致图片找不到路径
解决方法:
1、将项目发布到tomcat,但是每次重新加载项目或者重启服务器就会删除已上传的文件
2、上传到一个统一的文件夹,项目引用该文件夹下的文件,这样不会被迫删除已有文件
<Context path="/statics" docBase="E:/download" reloadable="true" crossContext="true" />
<param-name>listings</param-name><param-value>true</param-value>
3、上传到其他http服务器上,例如nginx服务器,推荐!
文件下载
服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment
Content-Disposition为属性名,计划安排之意
attachment:附件方式下载
filename为默认保存时的文件名
resp.setContentType("application/force-download");
resp.addHeader("Content-Disposition", "attachment;filename=" + filename);byte[] byteArray =
FileUtils.readFileToByteArray(file);resp.getOutputStream().write(byteArray);
配置静态资源文件访问
方式一:
<mvc:resources location="/statics/" mapping="/statics/**">
</mvc:resources>
方式二:使用servlet容器默认的DefaultServlet
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
方式三:原理与方式二一致
<mvc:default-servlet-handler />
拦截器
自定义拦截器,实现HandlerInterceptor接口,并重写三个方法
public class MyInterceptor implements HandlerInterceptor {
preHandle(){}//执行Handler之前,返回true,则继续进行,返回false则中断请求
postHandle(){}//执行Handler时
afterCompletion(){}//执行Handler之后
}
将自定义拦截器,添加到springmvc的拦截体系中
<mvc:interceptors>
<mvc:interceptor>
<!--拦截器的请求-->
<mvc:mapping path="/**"/>
<!--放行的请求,即公开的地址-->
<mvc:exclude-mapping path="/login/**"/>
<bean class="com.hemi.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
多个拦截器执行顺序
只要preHandler()返回true,那么afterCompletion()就会执行
三个拦截器,如果preC返回fales,那么最终执行的是preA-->preB-->preC-->afterB-->afterA
使用详细说明