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
使用詳細說明