Spring MVC 中攔截器的使用示例詳解"攔截器基本配置"和 "攔截器高級配置"
1. Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置”
2. 攔截器
攔截器(Interceptor) 類似于過濾器(Filter)
Spring MVC 的攔截器作用是在請求到達控制器之前或之后進行攔截,可以對請求和響應(yīng)進行一些特定的處理。
攔截器可以用于很多場景下:
- 登錄驗證:對于需要登錄才能訪問的地址,使用攔截器可以判斷用戶是否已登錄,如果未登錄,則跳轉(zhuǎn)到登錄頁面。
- 權(quán)限校驗:根據(jù)用戶權(quán)限對部分網(wǎng)址進行訪問控制,拒絕未經(jīng)授權(quán)的用戶訪問。
- 請求日志:記錄請求信息,例如:請求地址,請求參數(shù),請求時間等,用于排查問題和性能優(yōu)化。
- 更改響應(yīng):可以對響應(yīng)的內(nèi)容進行修改,例如:添加頭信息,調(diào)整響應(yīng)內(nèi)容格式等。
攔截器和過濾器的區(qū)別在于它們的作用層面不同:
- 過濾器更注重在請求和響應(yīng)的流程中進行處理,可以修改請求和響應(yīng)的內(nèi)容,例如:設(shè)置編碼和字符集,請求頭,狀態(tài)碼等。
- 攔截器則更加側(cè)重于對控制器進行前置或后置處理,在請求到達控制器之前或之后進行特定的操作,例如:打印日志,權(quán)限驗證等。
Filter、Servlet、Interceptor、Controller的執(zhí)行順序:

3. Spring MVC 中的攔截器的創(chuàng)建和基本配置
3.1 定義攔截
實現(xiàn)org.springframework.web.servlet.HandlerInterceptor 接口,共有三個方法可以進行選擇性的實現(xiàn):

- preHandle( ):處理器方法調(diào)用之前執(zhí)行。只有該方法有返回值,返回值是布爾類型,true 表示放行,false 表示攔截 。
- postHandle( ):處理器方法調(diào)用之后執(zhí)行。
- afterCompletion( ):渲染完成后執(zhí)行。
3.2 攔截器基本配置
第一步:編寫攔截器,該攔截器要實現(xiàn)org.springframework.web.servlet.HandlerInterceptor 接口 。

package com.rainbowsea.springmvc.interceptors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor1's preHandle!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor1's postHandle!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor1's afterCompletion!");
}
}在 Spring MVC 中攔截器的基本配置有兩種方式:
- 第一種方式是:通過 xml 進行配置
- 第二種方式是:通過 @Component 注解 + xml 文件進行配置
需要注意的是:這個基本配置,默認(rèn)情況下是攔截所有請求的。
在 springmvc.xml 文件中進行如下配置:
<mvc:interceptors>
<bean class="com.powernode.springmvc.interceptors.Interceptor1"/>
</mvc:interceptors>
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 組件掃描-->
<context:component-scan
base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors"></context:component-scan>
<!-- 視圖解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用于視圖渲染的過程中,可以設(shè)置視圖渲染后輸出時采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多個視圖解析器,它來決定優(yōu)先使用哪個視圖解析器,它的值越小優(yōu)先級越高-->
<property name="order" value="1"/>
<!--當(dāng) ThymeleafViewResolver 渲染模板時,會使用該模板引擎來解析、編譯和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器負(fù)責(zé)根據(jù)模板位置、模板資源名稱、文件編碼等信息,加載模板并對其進行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--設(shè)置模板文件的位置(前綴)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--設(shè)置模板文件后綴(后綴),Thymeleaf文件擴展名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--設(shè)置模板類型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用于模板文件在讀取和解析過程中采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!-- 配置攔截器-->
<mvc:interceptors>
<!-- 基本配置,第一種方式
注意:基本配置,默認(rèn)情況下是攔截所有請求的-->
<bean class="com.rainbowsea.springmvc.interceptors.Interceptor1"></bean>
</mvc:interceptors>
</beans>編寫對應(yīng)的 Controller 控制器進行測試:

package com.rainbowsea.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller // 交給 Spring IOC 容器管理
public class IndexController {
@RequestMapping("/index")
public String toIndex() {
System.out.println("IndexController#toIndex() ---> 處理器方法執(zhí)行了");
return "index";
}
@RequestMapping("ok")
public String toOK() {
System.out.println("IndexController#OK() ---> 處理器方法執(zhí)行了");
return "ok";
}
}運行測試:


第二種方式是:通過 @Component 注解 + xml 文件進行配置
注意:同樣的,對于這種基本配置來說,攔截器是攔截所有請求的。
第二種方式的前提:
前提1:包掃描,在 spring mvc 中配置組件掃描

前提2:使用 @Component 注解進行對 編寫的攔截器類進行標(biāo)注即可。
兩個前提都搞定了,就可以在 spring mvc.xml 文件中進行配置了。

<mvc:interceptors>
<ref bean="interceptor1"/>
</mvc:interceptors>運行測試:

3.3 攔截器的高級配置
采用以上基本配置方式,攔截器是攔截所有請求路徑的。如果要針對某些路徑進行攔截,某些路徑不攔截,某些路徑攔截,可以采用高級配置:在 spring mvc.xml 文件當(dāng)中進行配置

以上的配置表示,除 /ok 請求路徑之外,剩下的路徑全部攔截。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 組件掃描-->
<context:component-scan
base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors"></context:component-scan>
<!-- 視圖解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用于視圖渲染的過程中,可以設(shè)置視圖渲染后輸出時采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多個視圖解析器,它來決定優(yōu)先使用哪個視圖解析器,它的值越小優(yōu)先級越高-->
<property name="order" value="1"/>
<!--當(dāng) ThymeleafViewResolver 渲染模板時,會使用該模板引擎來解析、編譯和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器負(fù)責(zé)根據(jù)模板位置、模板資源名稱、文件編碼等信息,加載模板并對其進行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--設(shè)置模板文件的位置(前綴)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--設(shè)置模板文件后綴(后綴),Thymeleaf文件擴展名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--設(shè)置模板類型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用于模板文件在讀取和解析過程中采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!-- 高級配置:指定一些路徑被攔截,一些路徑不攔截-->
<mvc:interceptors>
<mvc:interceptor>
<!-- /** 表示攔截所有路徑-->
<mvc:mapping path="/**"/>
<!-- /ok 請求路徑不攔截-->
<mvc:exclude-mapping path="/ok"/>
<!-- /index 請求路徑攔截-->
<!-- <mvc:mapping path="/index"/>-->
<!-- 設(shè)置對應(yīng)的那個攔截器-->
<ref bean="interceptor1"></ref>
</mvc:interceptor>
</mvc:interceptors>
</beans>運行測試:

4. Spring MVC中多個攔截器的執(zhí)行順序
這里我們?yōu)榱颂骄浚鄠€攔截器存在的時候的執(zhí)行順序,我們創(chuàng)建 3 個 攔截器。如下:
4.1 如果所有攔截器 preHandle( ) 方法 都返回 true時,多個攔截器的的執(zhí)行順序



配置多個攔截器

<mvc:interceptors>
<!-- 配置多個攔截器,這個是基本配置,默認(rèn)是所有請求都會進行攔截處理-->
<ref bean="interceptor1"></ref>
<ref bean="interceptor2"></ref>
<ref bean="interceptor3"></ref>
</mvc:interceptors><?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 組件掃描-->
<context:component-scan
base-package="com.rainbowsea.springmvc.controller,com.rainbowsea.springmvc.interceptors"></context:component-scan>
<!-- 視圖解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用于視圖渲染的過程中,可以設(shè)置視圖渲染后輸出時采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多個視圖解析器,它來決定優(yōu)先使用哪個視圖解析器,它的值越小優(yōu)先級越高-->
<property name="order" value="1"/>
<!--當(dāng) ThymeleafViewResolver 渲染模板時,會使用該模板引擎來解析、編譯和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器負(fù)責(zé)根據(jù)模板位置、模板資源名稱、文件編碼等信息,加載模板并對其進行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--設(shè)置模板文件的位置(前綴)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--設(shè)置模板文件后綴(后綴),Thymeleaf文件擴展名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--設(shè)置模板類型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用于模板文件在讀取和解析過程中采用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<mvc:interceptors>
<!-- 配置多個攔截器,這個是基本配置,默認(rèn)是所有請求都會進行攔截處理-->
<ref bean="interceptor1"></ref>
<ref bean="interceptor2"></ref>
<ref bean="interceptor3"></ref>
</mvc:interceptors>
</beans>如果所有攔截器 preHandle 都返回 true

按照 springmvc.xml文件中配置的順序,自上而下調(diào)用 preHandle:


4.2 如果其中一個攔截器 preHandle ( ) 方法,返回 false,多個攔截器的的執(zhí)行順序
Interceptor3 攔截器中的 preHandle()方法返回 false。其他兩個攔截器返回 true.

規(guī)則:只要有一個攔截器preHandle返回false,任何postHandle都不執(zhí)行。但返回false的攔截器的前面的攔截器按照逆序執(zhí)行afterCompletion。

只要有一個攔截器
preHandle()方法,返回false,則任何攔截器的postHandle()方法都不執(zhí)行。但返回 false 的攔截器的前面的攔截器按照逆序執(zhí)行afterCompletion。返回 false 攔截器,攔截住了,則其中的 Controllor控制器不執(zhí)行了,其中的 postHandle
一個也不會執(zhí)行。而對應(yīng)的 afterCompletion()方法,的執(zhí)行是按照配置攔截器(自上而下)的倒序執(zhí)行,但其中返回 false 的攔截器中的 afterCompletion()方法不會被執(zhí)行
只要有一個攔截器
preHandle返回false,任何postHandle都不執(zhí)行。但返回false的攔截器的前面的攔截器按照逆序執(zhí)行afterCompletion。只要有一個攔截器preHandle返回false,任何postHandle都不執(zhí)行。但返回false的攔截器的前面的攔截器按照逆序執(zhí)行afterCompletion。
5. 補充:源碼分析
5.1 方法執(zhí)行順序的源碼分析
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 調(diào)用所有攔截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 調(diào)用處理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 調(diào)用所有攔截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 處理視圖
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 渲染頁面
render(mv, request, response);
// 調(diào)用所有攔截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}5.2 攔截與放行的源碼分析
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 調(diào)用所有攔截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 如果 mappedHandler.applyPreHandle(processedRequest, response) 返回false,以下的return語句就會執(zhí)行
return;
}
}
}public class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
// 如果 interceptor.preHandle(request, response, this.handler) 返回 false,以下的 return false;就會執(zhí)行。
return false;
}
this.interceptorIndex = i;
}
return true;
}
}5.3 DispatcherServlet 和 HandlerExecutionChain 的部分源碼:
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 按照順序執(zhí)行所有攔截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 執(zhí)行處理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 按照逆序執(zhí)行所有攔截器的 postHanle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 處理視圖
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 渲染視圖
render(mv, request, response);
// 按照逆序執(zhí)行所有攔截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}public class HandlerExecutionChain {
// 順序執(zhí)行 preHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果其中一個攔截器preHandle返回false
// 將該攔截器前面的攔截器按照逆序執(zhí)行所有的afterCompletion
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
// 逆序執(zhí)行 postHanle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
// 逆序執(zhí)行 afterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}6. 總結(jié):
實現(xiàn)org.springframework.web.servlet.HandlerInterceptor 接口,共有三個方法可以進行選擇性的實現(xiàn):
- preHandle( ):處理器方法調(diào)用之前執(zhí)行。只有該方法有返回值,返回值是布爾類型,true 表示放行,false 表示攔截 。
- postHandle( ):處理器方法調(diào)用之后執(zhí)行。
- afterCompletion( ):渲染完成后執(zhí)行。
在 Spring MVC 中攔截器的基本配置有兩種方式:
- 第一種方式是:通過 xml 進行配置
- 第二種方式是:通過 @Component 注解 + xml 文件進行配置
- 對于這種基本配置來說,攔截器是攔截所有請求的。
攔截器的高級配置:采用以上基本配置方式,攔截器是攔截所有請求路徑的。如果要針對某些路徑進行攔截,某些路徑不攔截,某些路徑攔截,可以采用高級配置:在 spring mvc.xml 文件當(dāng)中進行配置
Spring MVC中多個攔截器的執(zhí)行順序:
如果所有攔截器 preHandle( ) 方法 都返回 true時,多個攔截器的的執(zhí)行順序:
按照 springmvc.xml文件中配置的順序,自上而下調(diào)用 preHandle:

如果其中一個攔截器 preHandle ( ) 方法,返回 false,多個攔截器的的執(zhí)行順序
只要有一個攔截器preHandle()方法,返回false,則任何攔截器的postHandle()方法都不執(zhí)行。但返回 false 的攔截器的前面的攔截器按照逆序執(zhí)行afterCompletion。攔截器源碼分析。
7. 最后:
到此這篇關(guān)于Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置”的文章就介紹到這了,更多相關(guān)Spring MVC 攔截器的使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用
這篇文章主要介紹了Http請求中Content-Type講解以及在Spring MVC中的應(yīng)用的相關(guān)資料,需要的朋友可以參考下2017-02-02
Java中float類型的范圍及其與十六進制的轉(zhuǎn)換例子
這篇文章主要介紹了Java中float類型的范圍及其與十六進制的轉(zhuǎn)換例子,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10
Jmeter中的timeshift()函數(shù)獲取當(dāng)前時間進行加減
這篇文章主要介紹了Jmeter中的timeshift()函數(shù)獲取當(dāng)前時間進行加減,TimeShift(格式,日期,移位,語言環(huán)境,變量)可對日期進行移位加減操作,本文給大家詳細(xì)講解,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10
thymeleaf實現(xiàn)前后端數(shù)據(jù)交換的示例詳解
Thymeleaf?是一款用于渲染?XML/XHTML/HTML5?內(nèi)容的模板引擎,當(dāng)通過?Web?應(yīng)用程序訪問時,Thymeleaf?會動態(tài)地替換掉靜態(tài)內(nèi)容,使頁面動態(tài)顯示,這篇文章主要介紹了thymeleaf實現(xiàn)前后端數(shù)據(jù)交換,需要的朋友可以參考下2022-07-07
JAVA時間戳-Calendar類使用(包括set,get,add方法)
這篇文章主要介紹了JAVA時間戳-Calendar類使用(包括set,get,add方法),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04
Java多維數(shù)組和Arrays類方法總結(jié)詳解
這篇文章主要介紹了Java多維數(shù)組和Arrays類方法總結(jié)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03

