Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
擊右上角【關(guān)注】發(fā)哥微課堂頭條號(hào),get更多相關(guān)技能~
0x00:示例項(xiàng)目
以一個(gè)項(xiàng)目示例總結(jié)下 SpringMVC 環(huán)境的搭建基本流程,項(xiàng)目結(jié)構(gòu)如下圖:
0x01:導(dǎo)入 jar 包
SpringMVC 主要 jar 包如下圖,需導(dǎo)入到 WEB-INF 下的 lib 目錄。
0x02:配置前端控制器
傳統(tǒng)的 servlet 開(kāi)發(fā)中,請(qǐng)求都在 web.xml 中配置,然后配置到對(duì)應(yīng)的 servlet 中。同樣,在 SpringMVC 中,請(qǐng)求也需要配置到對(duì)應(yīng)的 servlet 上,而做配置的就是前端控制器,用于攔截符合配置的 url 請(qǐng)求。在 SpringMVC 中,正是通過(guò)前端控制器 DispatcherServlet 來(lái)對(duì)請(qǐng)求進(jìn)行攔截并處理的。配置 web.xml,示例代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 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:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
配置和 servlet 類(lèi)似,以上配置會(huì)攔截所有以 action 為后綴的請(qǐng)求,交給前端控制器 DispatcherServlet 去處理,定義了 SpringMVC 的核心配置文件為 springmvc.xml。
0x03:配置處理器映射器
在 springmvc.xml 配置文件中添加處理器映射器,示例代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 處理器映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
</beans>
處理器映射器有多種,它們都實(shí)現(xiàn)了 HandlerMapping 接口,示例代碼中用的是 BeanNameUrlHandlerMapping 類(lèi),映射規(guī)則是把 bean 的 name 作為 url 進(jìn)行查找。
0x04:配置處理器適配器
由之前的 SpringMVC 工作流程可知,當(dāng)處理器映射器 HandlerMapping 為前端控制器 DispatcherServlet 返回控制器 Handler 后,前端控制器就會(huì)給處理器適配器 HandlerAdapter 去執(zhí)行相關(guān)的 Handler 控制器也就是 Controller。
處理器適配器也有多種,它們都實(shí)現(xiàn)了 HandlerAdapter 接口,這里使用 SimplerControllerHandlerAdapter 適配器,在 springmvc.xml 中添加以下代碼:
<!-- 處理器適配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
0x05:配置視圖解析器
當(dāng)處理器適配器處理了相關(guān)的具體方法后,就會(huì)返回一個(gè) ModelAndView 對(duì)象,這個(gè)對(duì)象包含了要跳轉(zhuǎn)的視圖信息 view 和視圖上需要顯示的數(shù)據(jù) model,此時(shí)前端控制器會(huì)請(qǐng)求視圖解析器 ViewResolver 來(lái)解析 ModelAndView 對(duì)象。
視圖解析器也有很多種,這里使用默認(rèn)的 InternalResourceViewResolver,在 springmvc.xml 中添加以下代碼:
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
基本配置完成后,既需要配置處理器 Handler 了。
0x06:配置 Handler 處理器
因?yàn)樯衔奶幚砥鬟m配器使用的是 SimpleControllerHandlerAdapter,所以這里的 Handler 需要實(shí)現(xiàn) Controller 接口。編寫(xiě)一個(gè)加載水果列表信息的功能,名為 FruitsControllerTest,示例代碼如下:
package com.fageweiketang.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.fageweiketang.model.Fruits;
public class FruitsControllerTest implements Controller {
private FruitsService fruitsService = new FruitsService();
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
//模擬Service獲取水果商品列表
List<Fruits> fruitsList = fruitsService.queryFruitsList();
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相當(dāng)于request的setAttribut,在jsp頁(yè)面通過(guò)fruitsList獲取數(shù)據(jù)
modelAndView.addObject("fruitsList", fruitsList);
//指定視圖
modelAndView.setViewName("/WEB-INF/jsp/fruits/fruitsList.jsp");
return modelAndView;
}
//模擬Service的內(nèi)部類(lèi)
class FruitsService{
public List<Fruits> queryFruitsList(){
List<Fruits> fruitsList = new ArrayList<Fruits>();
Fruits apple = new Fruits();
apple.setName("蘋(píng)果");
Fruits banana = new Fruits();
apple.setName("香蕉");
Fruits pear = new Fruits();
apple.setName("梨");
fruitsList.add(apple);
fruitsList.add(banana);
fruitsList.add(pear);
return fruitsList;
}
}
}
示例中查詢到列表信息后創(chuàng)建了一個(gè) ModelAndView,將需要傳遞的數(shù)據(jù)通過(guò) ModelAndView 綁定到了對(duì)象中。又通過(guò) setViewName 方法指定了要跳轉(zhuǎn)的頁(yè)面。
0x07:編寫(xiě)實(shí)體類(lèi)代碼
實(shí)體類(lèi)很簡(jiǎn)單,一個(gè) get 和 set 方法即可,示例代碼如下:
package com.fageweiketang.model;
public class Fruits {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
0x08:編寫(xiě)視圖頁(yè)面
最后在 / WEB-INF/jsp/fruits 路徑下創(chuàng)建 fruitsList.jsp 文件即可,示例代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>水果列表</title>
</head>
<body>
<h3>水果列表</h3>
<table width="300px;" border=1>
<c:forEach items="${fruitsList }" var="fruit">
<tr>
<td>${fruit.name }</td>
</tr>
</c:forEach>
</table>
</body>
</html>
因?yàn)?springmvc.xml 中配置的處理器映射器是 BeanNameUrlHandlerMapping,在接收請(qǐng)求時(shí),會(huì)將 bean 的 name 作為 url 進(jìn)行查找,所以最后需要在 springmvc.xml 中配置一個(gè)可以被 url 映射的 Handler 的 bean,配置示例如下:
<bean name="/queryFruits.action" class="com.fageweiketang.controller.FruitsControllerTest"/>
啟動(dòng) tomcat 服務(wù)器,訪問(wèn) localhost/SpringMVC/queryFruits.action,結(jié)果如下:
“愛(ài)讀書(shū)”--給你講技術(shù)》,我來(lái)看書(shū),你來(lái)進(jìn)步,讓我們開(kāi)始吧!
書(shū)名為《Spring+MyBatis企業(yè)應(yīng)用實(shí)戰(zhàn)》,是本人在學(xué)習(xí)JavaEE框架時(shí)候的一本基礎(chǔ)書(shū)籍,本書(shū)對(duì)于SpringMVC和MyBatis框架及相關(guān)基礎(chǔ)知識(shí)講述的比較清晰,適合需要詳細(xì)學(xué)習(xí)Java框架的讀者。
本文內(nèi)容為本書(shū)第二章--第八章,記錄和總結(jié)了所有SpringMVC的知識(shí)
1.DispatcherServlet使用和配置
SpringMVC提供了一個(gè)名為org.springframework.web.servlet.DispatcherServlet的Servlet充當(dāng)前端控制器,所有請(qǐng)求驅(qū)動(dòng)都圍繞這個(gè)DispatcherServlet來(lái)分配請(qǐng)求。DispatcherServlet是一個(gè)Servlet類(lèi),需要在web.xml中完成配置。
(1)配置了啟動(dòng)時(shí)立即加載Servlet
(2)需要配置springmvc-config.xml配置文件
(3)配置了用當(dāng)前servlet處理所有請(qǐng)求URL
2.DispatcherServlet的分發(fā)原理
先看一下DispatcherServlet源碼中的方法
源碼如下:
initStrategies方法將在WebApplicationContext初始化后自動(dòng)執(zhí)行,自動(dòng)掃描上下文的Bean,根據(jù)名稱(chēng)或類(lèi)型匹配機(jī)制查找自定義的組件。如果沒(méi)有找到則會(huì)裝配一套默認(rèn)組件,默認(rèn)組件在DispatcherServlet.properties配置文件中。
配置文件如下:
如 果開(kāi)發(fā)者希望使用自定義類(lèi)型的組件,則只需要在Spring配置文件中配置自定義的Bean即可。MVC如果發(fā)現(xiàn)上下文中有用戶自定義的組件,就不會(huì)使用默認(rèn)組件。
1.在web.xml中定義前端控制器DispatcherServlet來(lái)攔截用戶請(qǐng)求
2.定義處理用戶請(qǐng)求的Handle類(lèi),可以實(shí)現(xiàn)Controller接口或使用@Controller注解
3.配置Handle,可采用xml文件或注解的方式
<!-- 配置Handle,映射/hello請(qǐng)求 -->
<bean name="/hello" class="org.fkit.controller.HelloController"/>
@Controller
public class HelloController{
@RequestMapping(value="/hello")
public ModelAndView hello(){
}
}
4.編寫(xiě)視圖資源,Handle處理用戶請(qǐng)求結(jié)束后,通常會(huì)返回一個(gè)ModelAndView對(duì)象,該對(duì)象包含返回的試圖名和模型。試圖名代表需要顯示的物理視圖資源;模型用于傳輸數(shù)據(jù)給視圖資源。
說(shuō)明如下:
(1)用戶向服務(wù)器發(fā)送請(qǐng)求,請(qǐng)求被Spring的前端控制器DispatcherServlet截獲
(2)DispatcherServlet對(duì)請(qǐng)求URL進(jìn)行解析,得到URI。然后根據(jù)URI,調(diào)用HandlerMapping獲得該Handler配置的所有相關(guān)對(duì)象,包括Handler對(duì)象以及Handler對(duì)象對(duì)應(yīng)的攔截器,會(huì)被封裝到一個(gè)HandlerExecutionChain對(duì)象中返回
(3)DispatcherServlet根據(jù)獲得的Handler,選擇一個(gè)HandlerAdapter。HandlerAdapter會(huì)被用于處理多種Handler,調(diào)用Handler實(shí)際處理請(qǐng)求的方法
(4)提取請(qǐng)求中的模型數(shù)據(jù),開(kāi)始執(zhí)行Handler。在填充Handler的入?yún)⑦^(guò)程中,根據(jù)配置spring會(huì)幫你實(shí)現(xiàn)消息轉(zhuǎn)換、數(shù)據(jù)轉(zhuǎn)換、數(shù)據(jù)格式化、數(shù)據(jù)驗(yàn)證
(5)Handler執(zhí)行完成后,向DispatcherServlet返回一個(gè)ModelAndView對(duì)象。ModelAndView對(duì)象中包含試圖名和模型
(6)根據(jù)返回的ModelAndView對(duì)象,選擇一個(gè)ViewResolver返回給DispatcherServlet
(7)ViewResolver結(jié)合Model和View來(lái)渲染視圖
(8)將試圖渲染結(jié)果返回給客戶端
1.@Controller注解
@Controller用于標(biāo)記一個(gè)類(lèi),使用它標(biāo)記的類(lèi)就是一個(gè)Controller對(duì)象,及一個(gè)控制器。Spring使用掃描機(jī)制查找應(yīng)用中所有基于注解的控制器類(lèi)。
@Controller
public class HelloController{
@RequestMapping(value="/hello")
public ModelAndView hello(){
}
}
使用注解前需要如下步驟:
<context:component-scan base-package="org.fkit.controller"/>
springmvc-config.xml常用配置
說(shuō)明如下:
(1)使用<context:component-scan>指定需要掃描的包
(2)<mvc:annotation-driven>會(huì)自動(dòng)注冊(cè)RequestMappingHandlerMapping和Request MappingHandlerAdapter兩個(gè)Bean,提供注解的必要支持
(3)<mvc:default-servlet-handler>是靜態(tài)資源處理器,SpringMVC會(huì)默認(rèn)捕獲所有請(qǐng)求,包括靜態(tài)資源請(qǐng)求。配置了當(dāng)前元素后靜態(tài)資源不會(huì)被DispatcherServlet處理
(4)視圖解析器InternalResourceViewResolver來(lái)解析視圖,將View呈現(xiàn)給用戶。配置的prefix屬性表示視圖的前綴,suffix表示視圖的后綴。
2.@RequestMapping注解
@RequestMapping注解可以用來(lái)注釋一個(gè)控制器類(lèi),所有方法都將映射為相對(duì)于類(lèi)級(jí)別的請(qǐng)求,表示該控制器處理的所有請(qǐng)求都被映射到value屬性所指示的路徑下。
上圖代表映射到如下路徑:
http://localhost:8080/user/register
http://localhost:8080/user/login
常用屬性如下:
3.請(qǐng)求處理方法可出現(xiàn)的參數(shù)類(lèi)型
所有參數(shù)Spring會(huì)自動(dòng)將值傳給方法
(1)HttpServletRequest參數(shù)
(2)HttpSession參數(shù)
(3)HttpServletResponse
(4)InputStream
(5)OutputStream
(6)Map
(7)Model
(8)ModelMap
(9)BindingResult
(10)WebRequest
4.請(qǐng)求處理方法可返回的類(lèi)型
下面做一些詳細(xì)說(shuō)明。
(1)Model和ModelMap
SpringMVC在調(diào)用處理方法之前會(huì)創(chuàng)建一個(gè)隱含的模型對(duì)象,作為模型數(shù)據(jù)的存儲(chǔ)容器。如果處理方法的參數(shù)為Model或ModelMap類(lèi)型,則SpringMVC會(huì)將隱含模型的引用傳遞給這些參數(shù)。在處理方法內(nèi)部,開(kāi)發(fā)者可以通過(guò)這個(gè)參數(shù)對(duì)象訪問(wèn)模型中的所有數(shù)據(jù),也可以向模型中添加新的數(shù)據(jù)。
(2)ModelAndView
控制器處理方法的返回值如果是ModelAndView,則既包含模型數(shù)據(jù),也包含視圖信息。
常用方法如下:
//添加模型數(shù)據(jù)
addObject(String key, Object value);
//設(shè)置視圖
setViewName(String name);
5.頁(yè)面轉(zhuǎn)發(fā)
(1)轉(zhuǎn)發(fā)到JSP
//默認(rèn)forward跳轉(zhuǎn)
return "main";
modelAndView.setViewName("main");
//重定向頁(yè)面
return "redirect:/main.jsp";
modelAndView.setViewName("redirect:/main.jsp");
(2)轉(zhuǎn)發(fā)到其他處理方法
//forward跳轉(zhuǎn)
return "forward:/main";
modelAndView.setViewName("forward:/main");
//重定向
return "redirect:/main";
modelAndView.setViewName("redirect:/main");
6.@RequestParam注解
@RequestParam用于將指定的請(qǐng)求參數(shù)賦值給方法中的形參。屬性如下:
name:指定請(qǐng)求參數(shù)綁定的名稱(chēng)
value:name屬性的別名
required:指定參數(shù)是否必須綁定
defaultValue:指定默認(rèn)值
7.@PathVariable注解
@PathVariable可以方便的獲得請(qǐng)求URL中的動(dòng)態(tài)參數(shù)。屬性如下:
舉例如下:
@RequestMapping(value="/pathVariableTest/{userId}")
public void pathVariableTest(@PathVariable Integer userId)
假如請(qǐng)求的URL為http://localhost:8080/pathVariableTest/1,則自動(dòng)將URL中模板變量{userId}綁定到通過(guò)@PathVariable注解的同名參數(shù)上,即userId變量將被賦值為1。
8.@RequestHeader注解
@RequestHeader注解用于將請(qǐng)求頭信息數(shù)據(jù)映射到功能處理方法的參數(shù)上。屬性如下:
舉例如下:
9.@CookieValue注解
@CookieValue注解用于將請(qǐng)求的Cookie數(shù)據(jù)映射到功能處理方法的參數(shù)上。屬性如下:
舉例如下:
10.@RequestAttribute注解
@RequestAttribute注解用于訪問(wèn)由請(qǐng)求處理方法、過(guò)濾器或攔截器創(chuàng)建的、預(yù)先存在于request作用域中的屬性,將該屬性轉(zhuǎn)換到目標(biāo)方法的參數(shù)。屬性如下:
舉例如下:
11.@SessionAttribute注解
@SessionAttribute注解用于訪問(wèn)由請(qǐng)求處理方法、過(guò)濾器或攔截器創(chuàng)建的、預(yù)先存在于session作用域中的屬性,將該屬性轉(zhuǎn)換到目標(biāo)方法的參數(shù)。屬性如下:
舉例如下:
12.@ModelAttribute注解
@ModelAttribute注解用于將請(qǐng)求參數(shù)綁定到對(duì)象。
舉例如下:
Form請(qǐng)求的參數(shù)值會(huì)自動(dòng)入?yún)⒌紷ModelAttribute注解的對(duì)象的同名屬性中
13.@RequestBody注解
@RequestBody注解用來(lái)處理content-type類(lèi)型為:application/json或application/xml的情況,將請(qǐng)求數(shù)據(jù)綁定到方法參數(shù)上。
舉例如下:
其中前臺(tái)向處理方法傳遞了Json格式的數(shù)據(jù),Json數(shù)據(jù)的key和Book屬性相對(duì)應(yīng)
14.@ResponseBody注解
@ResponseBody注解用于將請(qǐng)求處理方法返回的對(duì)象,轉(zhuǎn)換為指定格式后,寫(xiě)入到Response對(duì)象的body中。返回的數(shù)據(jù)不是HTML頁(yè)面,而是其他格式數(shù)據(jù),如JSON、XML時(shí)使用注解。
舉例如下:
上圖將List轉(zhuǎn)換為Json格式輸出
15.@RestController注解
@RestController注解相當(dāng)于同時(shí)使用了@Controller和@ResponseBody。用于RESTFUL風(fēng)格的請(qǐng)求處理方式。
SpringMVC提供的兩種異常處理方式:
//該方法處理程序執(zhí)行期間被拋出的異常,返回一個(gè)模型和視圖,視圖返回錯(cuò)誤處理頁(yè)面
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response)
通過(guò)springmvc-config.xml配置實(shí)現(xiàn)
<!-- p:defaultErrorView="error"表示所有沒(méi)有指定的異常都跳轉(zhuǎn)到異常處理頁(yè)面error
p:exceptionAttribute="ex"表示異常處理頁(yè)面中訪問(wèn)的異常對(duì)象變量名為ex -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
p:defaultErrorView="error" p:exceptionAttribute="ex">
<!-- exceptionMappings表示映射的異常,接受參數(shù)是一個(gè)Properties,key是異常類(lèi)名,value是處理異常的頁(yè)面 -->
<property name="exceptionMappings">
<props>
<prop key="IOException">ioerror</prop>
<prop key="SQLException">sqlerror</prop>
</props>
</property>
</bean>
@ExceptionHandler注解只在當(dāng)前類(lèi)生效
使用<context:component-scan>掃描到@ControllerAdvice注解后,會(huì)將注解修飾的類(lèi)內(nèi)部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法應(yīng)用到所有請(qǐng)求的異常處理上。
1.messageSource接口
SpringMVC不直接使用java.util.ResourceBundle,而是使用messageSource的Bean來(lái)配置國(guó)際化屬性文件。
上圖basenames指定了資源文件的名稱(chēng)
2.localeResolver接口
SpringMVC使用語(yǔ)言區(qū)域解析器來(lái)實(shí)現(xiàn)用戶選擇語(yǔ)言區(qū)域。提供了一個(gè)語(yǔ)言區(qū)域解析器接口LocaleResolver,實(shí)現(xiàn)類(lèi)包括:
AcceptHeaderLocaleResolver是默認(rèn)解析器,會(huì)讀取瀏覽器accept-language標(biāo)題,來(lái)確定使用哪個(gè)語(yǔ)言區(qū)域
3.message標(biāo)簽
SpringMVC顯示本地化消息使用message標(biāo)簽,使用前需先導(dǎo)入taglib標(biāo)簽庫(kù)。
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
message標(biāo)簽屬性如下:
4.AcceptHeaderLocaleResolver
實(shí)現(xiàn)步驟:
(1)創(chuàng)建資源文件
message_en_US.properties
message_zh_CN.properties
(2)在JSP中使用message標(biāo)簽輸出國(guó)際化消息
(3)在SpringMVC配置文件中加載國(guó)際化資源文件并配置
(4)可在controller中使用國(guó)際化
RequestContext requestContext = new RequestContext(request);
String username = requestContext.getMessage("username");
Spring MVC框架將ServletRequest對(duì)象及處理方法的參數(shù)對(duì)象傳遞給DataBinder,DataBinder調(diào)用ConversionService組件進(jìn)行數(shù)據(jù)類(lèi)型轉(zhuǎn)換、數(shù)據(jù)格式化工作,并將ServletRequest中的消息填充到參數(shù)對(duì)象中。然后再調(diào)用Validator組件對(duì)已經(jīng)綁定了請(qǐng)求消息數(shù)據(jù)的參數(shù)對(duì)象進(jìn)行數(shù)據(jù)合法性校驗(yàn),并最終生成 數(shù)據(jù)綁定結(jié)果BindingResult 對(duì)象。
實(shí)現(xiàn)步驟:
(1)創(chuàng)建表單并設(shè)置為multipart/form-data
(2)創(chuàng)建controller
SpringMVC會(huì)將上傳文件綁定到MultipartFile對(duì)象。MultipartFile提供了獲取上傳文件內(nèi)容、文件名等方法。
MultipartFile常用方法如下:
byte[] getBytes():獲取文件數(shù)據(jù)
String getContentType():獲取文件MIME類(lèi)型,如image/jpeg
InputStream getInputStream():獲取文件流
String getName(): 獲取表單中文件組件的名稱(chēng)
String getOriginalFilename():獲取上傳文件的原名
long getSize(): 獲取文件的字節(jié)大小,單位為byte
boolean isEmpty(): 是否有上傳的文件
void transferTo(File file):將上傳文件保存到一個(gè)目標(biāo)文件中
(3)在SpringMVC配置文件中增加文件上傳功能(配置MultipartResolver)
CommonsMultipartResolver必須依賴(lài)于Apach FileUpload組件,需要引入JAR包
實(shí)現(xiàn)步驟:
(1)在頁(yè)面中加入下載超鏈接,鏈接地址指向controller方法
(2)編寫(xiě)controller處理方法,用于文件下載
使用Apache FileUpload組件FileUtils讀取要下載的文件,并將其構(gòu)建成ResponseEntity對(duì)象返回。ResponseEntity對(duì)象可以方便的定義返回的BodyBuilder、HttpHeaders、HttpStatus。
BodyBuilder對(duì)象用來(lái)構(gòu)建返回的Body
HttpHeaders代表Http協(xié)議頭信息
HttpStatus代表Http協(xié)議的狀態(tài)
Interceptor攔截器的主要作用是攔截用戶的請(qǐng)求并進(jìn)行相應(yīng)的處理(比如通過(guò)攔截器來(lái)進(jìn)行用戶權(quán)限驗(yàn)證,或者用來(lái)判斷用戶是否已經(jīng)登錄等)
1.HandlerInterceptor接口
定義攔截器類(lèi)需要實(shí)現(xiàn)HandlerInterceptor接口或繼承抽象類(lèi)HandlerInterceptorAdapter
接口中的方法如下:
//該方法在請(qǐng)求處理之前被調(diào)用,可以做前置的初始化操作或者是對(duì)當(dāng)前請(qǐng)求的一個(gè)預(yù)處理,也可以判斷來(lái)請(qǐng)求是否要繼續(xù)進(jìn)行下去。當(dāng)返回值為false,表示請(qǐng)求結(jié)束,后續(xù)的Interceptor和Controller不會(huì)再執(zhí)行;當(dāng)返回值為true澤會(huì)繼續(xù)執(zhí)行
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
//該方法在controller方法被執(zhí)行,在視圖返回渲染前被調(diào)用。可以在這個(gè)方法中對(duì)處理后的ModelAndView進(jìn)行操作
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv)
//該方法將在整個(gè)請(qǐng)求結(jié)束后執(zhí)行,作用是進(jìn)行資源清理
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
實(shí)現(xiàn)步驟:
(1)定義攔截器類(lèi),實(shí)現(xiàn)接口和接口中的方法
(2)在配置文件中配置攔截器(springmvc-config.xml)
起HTML標(biāo)簽,前端工程師會(huì)非常的有發(fā)言權(quán),因?yàn)樵谄綍r(shí)開(kāi)發(fā)中一定會(huì)用到,可以說(shuō)是前端的入門(mén)必備知識(shí)。但往往在意更多的是頁(yè)面的渲染效果及交互方式,也就是頁(yè)面可見(jiàn)的部分,比如導(dǎo)航欄,菜單欄,列表,圖文等。
其實(shí)還有一些頁(yè)面上沒(méi)有呈現(xiàn)出來(lái)卻非常重要的標(biāo)簽,這些標(biāo)簽大部分在頁(yè)面的頭部head標(biāo)簽內(nèi),雖然在頁(yè)面上看不見(jiàn)摸不著,但如果在特定的場(chǎng)景下,會(huì)產(chǎn)生意想不到的效果。下面我將從交互優(yōu)化,性能優(yōu)化,搜索優(yōu)化三個(gè)方面并結(jié)合場(chǎng)景來(lái)聊聊被“忽視”的HTML標(biāo)簽。
交互優(yōu)化
meta 標(biāo)簽:自動(dòng)跳轉(zhuǎn)/刷新
假設(shè)要實(shí)現(xiàn)一個(gè)類(lèi)似自動(dòng)播放的頁(yè)面,首先我們第一反應(yīng)會(huì)想到用js定時(shí)器控制頁(yè)面跳轉(zhuǎn)來(lái)完成。但是其實(shí)有更加簡(jiǎn)便的方法,通過(guò)meta標(biāo)簽的refresh功能來(lái)實(shí)現(xiàn)。
<meta http-equiv="refresh" content="10; url=view2.html">
上面的代碼會(huì)在 10s 之后自動(dòng)跳轉(zhuǎn)到同域下的 view2.html 頁(yè)面。我們要實(shí)現(xiàn)自動(dòng)播放的功能,只需要在每個(gè)頁(yè)面的 meta 標(biāo)簽內(nèi)設(shè)置好下一個(gè)頁(yè)面的地址即可。
如果要實(shí)現(xiàn)定時(shí)刷新的功能,只要去除后面url即可:
<meta http-equiv="refresh" content="10">
注意,用meta標(biāo)簽實(shí)現(xiàn)刷新/跳轉(zhuǎn)的過(guò)程是不可取消的,所以需要手動(dòng)取消的還是得老老實(shí)實(shí)使用js的定時(shí)器,但是對(duì)于簡(jiǎn)單的定時(shí)刷新或跳轉(zhuǎn),還是可以去親自實(shí)踐meta的用法。
title 標(biāo)簽:消息提醒
消息提醒功能實(shí)現(xiàn)在HTML5標(biāo)準(zhǔn)發(fā)布之前,瀏覽器還沒(méi)有開(kāi)放圖標(biāo)閃爍、音頻播放,彈出系統(tǒng)消息之類(lèi)的api,只能借助其他非常規(guī)的手段,比如修改title 標(biāo)簽來(lái)達(dá)到類(lèi)似的效果。
下面的代碼通過(guò)定時(shí)修改title標(biāo)簽的內(nèi)容,實(shí)現(xiàn)了消息提醒的功能,可以讓用戶在瀏覽其他頁(yè)面時(shí)候,及時(shí)發(fā)現(xiàn)服務(wù)端返回的消息。
let messageNum = 1; // 消息條數(shù)
let count = 0; // 計(jì)數(shù)器
const msgInterval = setInterval(() => {
count = (count + 1) % 2;
if(messageNum === 0) {
// 通過(guò)DOM修改title
document.title += `當(dāng)前頁(yè)面`;
clearInterval(msgInterval);
return;
}
const pre = count % 2 ? `新消息(${msgNum})` : '';
document.title = `${pre}當(dāng)前頁(yè)面`;
}, 1000);
當(dāng)然,動(dòng)態(tài)修改title標(biāo)簽的用途不僅僅是消息提醒,還可以顯示一些異步進(jìn)行的任務(wù),比如下載進(jìn)度,上傳進(jìn)度等。
性能優(yōu)化
script 標(biāo)簽:調(diào)整加載順序提升渲染速度
不知道你們有沒(méi)有過(guò)這樣的體驗(yàn):當(dāng)在瀏覽器打開(kāi)某個(gè)頁(yè)面時(shí),發(fā)現(xiàn)頁(yè)面一直在loading轉(zhuǎn)圈,或者等了好長(zhǎng)的時(shí)間頁(yè)面才有響應(yīng)。這一現(xiàn)象,除了網(wǎng)絡(luò)網(wǎng)速的原因外,大多數(shù)都是由于頁(yè)面結(jié)構(gòu)設(shè)計(jì)不合理導(dǎo)致加載時(shí)間過(guò)長(zhǎng)。因此,如果想要提升頁(yè)面的渲染速度,就需要了解瀏覽器頁(yè)面的渲染過(guò)程是怎樣的,從根本上來(lái)解決問(wèn)題。
瀏覽器在加載頁(yè)面的時(shí)候會(huì)用到 GUI 渲染線程和 JavaScript 引擎線程。其中,GUI 渲染線程負(fù)責(zé)渲染瀏覽器界面 HTML 元素,JavaScript 引擎線程主要負(fù)責(zé)處理 JavaScript 腳本程序。由于 JavaScript 在執(zhí)行過(guò)程中還可能會(huì)改動(dòng)界面結(jié)構(gòu)和樣式,因此它們之間被設(shè)計(jì)為互斥的關(guān)系。也就是說(shuō),當(dāng) JavaScript 引擎執(zhí)行時(shí),GUI 線程會(huì)被掛起,等執(zhí)行完 JavaScript 的腳本程序后又會(huì)切換 GUI 線程繼續(xù)渲染頁(yè)面。
所以我們可以知道頁(yè)面渲染過(guò)程中包含了請(qǐng)求腳本文件以及執(zhí)行腳本文件的時(shí)間,但頁(yè)面的首次渲染可能并不需要執(zhí)行完全部的文件,這些請(qǐng)求和執(zhí)行文件的動(dòng)作反而延長(zhǎng)了用戶看到頁(yè)面的時(shí)間,從而降低了用戶體驗(yàn)。
為了快速將內(nèi)容呈現(xiàn)給用戶,減少用戶等待時(shí)間,可以借助script標(biāo)簽的3個(gè)屬性來(lái)實(shí)現(xiàn):
async:表示立即請(qǐng)求腳本文件,但不阻塞 GUI 渲染引擎,而是文件加載完畢后阻塞 GUI 渲染引擎并立即執(zhí)行文件內(nèi)容。
defer。立即請(qǐng)求腳本腳本,但不阻塞 GUI 渲染引擎,等到解析完 HTML 之后再執(zhí)行文件內(nèi)容。
HTML5 標(biāo)準(zhǔn) type,對(duì)應(yīng)值為“module”。讓瀏覽器按照 ECMA Script 6 標(biāo)準(zhǔn)將文件當(dāng)作模塊進(jìn)行解析,默認(rèn)阻塞效果同 defer,也可以配合 async 在請(qǐng)求完成后立即執(zhí)行。
所以可以得知,采用defer 屬性以及 type="module" 情況下能保證渲染引擎的優(yōu)先執(zhí)行,從而減少執(zhí)行文件內(nèi)容消耗的時(shí)間,讓用戶更快地看見(jiàn)頁(yè)面(即使這些頁(yè)面內(nèi)容可能并沒(méi)有完全地顯示)。除此外還要知道,當(dāng)渲染引擎解析 HTML 遇到 script 標(biāo)簽引入文件時(shí),會(huì)立即進(jìn)行一次渲染,這就是為什么會(huì)把引用JavaScript 代碼的 script 標(biāo)簽放入到 body 標(biāo)簽底部。
link 標(biāo)簽:通過(guò)預(yù)處理提升渲染速度
在我們對(duì)中大型項(xiàng)目進(jìn)行性能優(yōu)化時(shí),往往會(huì)對(duì)資源做減法(gzip壓縮,緩存等)或除法(按需打包,按需加載),可是如果能想到 link 標(biāo)簽的 rel 屬性值來(lái)進(jìn)行預(yù)加載,也能加快頁(yè)面的渲染速度。
dns-prefetch。當(dāng) link 標(biāo)簽的 rel 屬性值為“dns-prefetch”時(shí),瀏覽器會(huì)對(duì)某個(gè)域名預(yù)先進(jìn)行 DNS 解析并緩存。這樣,當(dāng)瀏覽器在請(qǐng)求同域名資源的時(shí)候,能省去從域名查詢 IP 的過(guò)程(DNS查詢),從而減少時(shí)間損耗。(注意:這個(gè)屬性還在實(shí)驗(yàn)階段,部分瀏覽器的部分版本支持)
preconnect。讓瀏覽器在一個(gè) HTTP 請(qǐng)求正式發(fā)給服務(wù)器前預(yù)先執(zhí)行一些操作,這包括 DNS 解析、TLS 協(xié)商、TCP 握手,通過(guò)消除往返延遲來(lái)為用戶節(jié)省時(shí)間。(注意:這個(gè)屬性還在實(shí)驗(yàn)階段,部分瀏覽器的部分版本支持)
prefetch/preload。兩個(gè)值都是讓瀏覽器預(yù)先下載并緩存某個(gè)資源,但不同的是,prefetch 可能會(huì)在瀏覽器忙時(shí)被忽略,而 preload 則是一定會(huì)被預(yù)先下載。
prerender。瀏覽器不僅會(huì)加載資源,還會(huì)解析執(zhí)行頁(yè)面,進(jìn)行預(yù)渲染。(注意:這個(gè)屬性還在實(shí)驗(yàn)階段,部分瀏覽器的部分版本支持)
搜索優(yōu)化
你所寫(xiě)的前端代碼,除了要讓瀏覽器更好執(zhí)行,有時(shí)候也要考慮更方便其他程序(如搜索引擎)理解。合理地使用 meta 標(biāo)簽和 link 標(biāo)簽,恰好能讓搜索引擎更好地理解和收錄我們的頁(yè)面。
meta 標(biāo)簽:提取關(guān)鍵信息
通過(guò) meta 標(biāo)簽可以設(shè)置頁(yè)面的描述信息,從而讓搜索引擎更好地展示搜索結(jié)果。
例如,在百度中搜索“淘寶”,就會(huì)發(fā)現(xiàn)網(wǎng)站的描述信息,這些描述信息就是通過(guò) meta 標(biāo)簽專(zhuān)門(mén)為搜索引擎設(shè)置的,目的是方便用戶預(yù)覽搜索到的結(jié)果。
為了讓搜索引擎更好地識(shí)別頁(yè)面,除了描述信息description之外還可以使用關(guān)鍵字,這樣即使頁(yè)面其他地方?jīng)]有包含搜索內(nèi)容,也可以被搜索到(當(dāng)然搜索引擎有自己的權(quán)重和算法,如果濫用關(guān)鍵字是會(huì)被降權(quán)的,比如 Google 引擎就會(huì)對(duì)堆砌大量相同關(guān)鍵詞的網(wǎng)頁(yè)進(jìn)行懲罰,降低它被搜索到的權(quán)重)。
當(dāng)我們搜索關(guān)鍵字“安全購(gòu)物”的時(shí)候搜索結(jié)果會(huì)顯示淘寶網(wǎng)的信息,雖然顯示的搜索內(nèi)容上并沒(méi)有看到“安全購(gòu)物”字樣,這就是因?yàn)樘詫毦W(wǎng)頁(yè)面中設(shè)置了這個(gè)關(guān)鍵字。
對(duì)應(yīng)代碼如下:
<meta content="淘寶,掏寶,網(wǎng)上購(gòu)物,C2C,在線交易,交易市場(chǎng),網(wǎng)上交易,交易市場(chǎng),網(wǎng)上買(mǎi),網(wǎng)上賣(mài),購(gòu)物網(wǎng)站,團(tuán)購(gòu),網(wǎng)上貿(mào)易,安全購(gòu)物,電子商務(wù),放心買(mǎi),供應(yīng),買(mǎi)賣(mài)信息,網(wǎng)店,一口價(jià),拍賣(mài),網(wǎng)上開(kāi)店,網(wǎng)絡(luò)購(gòu)物,打折,免費(fèi)開(kāi)店,網(wǎng)購(gòu),頻道,店鋪" name="keywords">
在實(shí)際工作中,推薦使用一些關(guān)鍵字工具來(lái)挑選,比如
Google Trends
https://trends.google.com/trends
站長(zhǎng)工具
https://data.chinaz.com/keyword/
link 標(biāo)簽:減少重復(fù)
有時(shí)候?yàn)榱擞脩粼L問(wèn)方便或者出于歷史原因,對(duì)于同一個(gè)頁(yè)面會(huì)有多個(gè)網(wǎng)址,又或者存在某些重定向頁(yè)面,比如:
- https://baidu.com/a.html
- https://baidu.com/detail?id=abcd
那么在這些頁(yè)面中可以這樣設(shè)置:
<link href="https://baidu.com/a.html" rel="canonical">
這樣可以讓搜索引擎避免花費(fèi)時(shí)間抓取重復(fù)網(wǎng)頁(yè)。不過(guò)需要注意的是,它還有個(gè)限制條件,那就是指向的網(wǎng)站不允許跨域。
當(dāng)然,要合并網(wǎng)址還有其他的方式,比如使用站點(diǎn)地圖,或者在 HTTP 請(qǐng)求響應(yīng)頭部添加 rel="canonical"。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。