Java的Struts2框架中攔截器使用的實(shí)例教程
1、攔截器小介
攔截器的功能類似于web.xml文件中的Filter,能對用戶的請求進(jìn)行攔截,通過攔截用戶的請求來實(shí)現(xiàn)對頁面的控制。攔截器是在Struts-core-2.2.3.jar中進(jìn)行配置的,原始的攔截器是在struts-default.xml中配置的,里面封存了攔截器的基本使用方法。
Struts2攔截器功能類似于Servlet過濾器。在Action執(zhí)行execute方法前,Struts2會首先執(zhí)行struts.xml中引用的攔截器,如果有多個(gè)攔截器則會按照上下順序依次執(zhí)行,在執(zhí)行完所有的攔截器的interceptor方法后,會執(zhí)行Action的execute方法。
Struts2的攔截器必須從com.opensymphoy.xwork2.interceptor.Interceptor中實(shí)現(xiàn)該接口,在被定義的攔截器中有下面三個(gè)方法需要被實(shí)現(xiàn):
void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception;
自定義的攔截器需要重寫上面三個(gè)方法。另外Struts2的攔截器配置文件struts.xml它是繼承了原始文件struts-default.xml文件的,這樣在相應(yīng)的<package>中就會自動擁有struts-default.xml中的所有配置信息了。具體代碼如下:
<package name="demo" extends="struts-default" > ... </package>
2、添加攔截器
想要使用攔截器必須要經(jīng)過配置,struts2采用的是映射的方法,所以想用使用某一個(gè)功能就必須在配置文件中配置,攔截器也不例外。所以必須在package中添加相應(yīng)的攔截器元素,同時(shí)將攔截器關(guān)聯(lián)相應(yīng)的class文件,這樣在執(zhí)行action前才會執(zhí)行相應(yīng)的攔截器,具體使用方法如下。
(1)添加配置文件struts.xml,并在該文件中添加攔截器
<package name="testLogin" namespace="/" extends="struts-default"> <!-- 攔截器 --> <interceptors> <interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor> </interceptors> <action name="demo" class="com.action.LoginAction"> <result name="error" type="redirect">/error.jsp</result> <result name="success">/success.jsp</result> <result name="checkError">/checkSession.jsp</result> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package>
上面的package中添加了一個(gè)名為myInterceptor的攔截器,并為該攔截器注冊了一個(gè)java類,該類名稱為MyInterceptor,并被封存在com.interceptor包中。另外還在該package中添加了相應(yīng)的action,在執(zhí)行該action前會首先執(zhí)行myInterceptor攔截器。
(2)編寫被注冊的攔截器類MyInterceptor,該類必須實(shí)現(xiàn)com.opensymphoy.xwork2.interceptor.Interceptor接口,并重寫相應(yīng)的方法
package com.interceptor; import java.util.Map; import com.entity.User; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class MyInterceptor implements Interceptor{ private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public void destroy() { // TODO Auto-generated method stub System.out.println("----destroy()----"); } @Override public void init() { // TODO Auto-generated method stub System.out.println("-----Init()-------"); } @Override public String intercept(ActionInvocation invocation) throws Exception { // TODO Auto-generated method stub System.out.println("----intercept()------"); Map<String, Object> session= invocation.getInvocationContext().getSession(); if(session.get("username")!=null){ System.out.println("登陸成功!"); //session.put("username",user.getUsername()); return invocation.invoke(); }else{ System.out.println("登陸失?。?); return "checkError"; } } }
(3)經(jīng)過前面兩步后,攔截器已經(jīng)配置完成,最后一部就是使用攔截器了,在顯示頁面上添加相應(yīng)的標(biāo)簽,并為標(biāo)簽指定上面創(chuàng)建的名為demo的action,然后執(zhí)行頁面即可在控制臺中打印出相應(yīng)的攔截器內(nèi)容。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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>Insert title here</title> </head> <body> <form action="demo"> 用戶名:<input type="text" name="username"><br> 密 碼:<input type="text" name="password"><br> <input type="submit" name="ok" value="提交"> </form> </body> </html>
打印輸出內(nèi)容:
分析輸出結(jié)果,程序編譯階段首先會去讀取配置文件struts.xml,在該配置文件action中順序查找是否添加了攔截器,如果添加了攔截器則根據(jù)攔截器名稱在<interceptors>中查找是否定義了該攔截器或者攔截器棧,如果發(fā)現(xiàn)定義的是攔截器則根據(jù)攔截器查找對應(yīng)的注冊的class,最后在包內(nèi)查找注冊的class并執(zhí)行相應(yīng)的init()方法;程序運(yùn)行階段的大致流程和編譯階段類似,用戶在前臺提交請求后,會按照注冊的action在struts.xml中查找與之相對應(yīng)的,如果查找到將會查找攔截器,沒有查找到的話會相應(yīng)的拋錯(cuò),最后執(zhí)行攔截器注冊類的intercept方法。
3、攔截器棧
攔截器同樣有棧的概念,它是將使用的攔截器定義到共有的狀態(tài)下來實(shí)現(xiàn)統(tǒng)一管理,這樣在package中就可以做到攔截器的共享了,大大便利了攔截器的使用。在一個(gè)package中往往會用到重復(fù)的interceptor,如果每次都在Action中添加interceptor-ref的話就會很麻煩,那么攔截器棧就是為了解決這個(gè)問題而產(chǎn)生的,具體配置如下:
<package name="testLogin" namespace="/" extends="struts-default"> <!-- 攔截器 --> <interceptors> <interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor> <!-- 定義公共的攔截器鏈,在action標(biāo)簽中只需要引用攔截器鏈 --> <interceptor-stack name="defaultstack1"> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <action name="demo" class="com.action.LoginAction"> <result name="error" type="redirect">/error.jsp</result> <result name="success">/success.jsp</result> <result name="checkError">/checkSession.jsp</result> <interceptor-ref name="defaultstack1"></interceptor-ref> </action> </package>
實(shí)例中使用了interceptor-stack來定義了一個(gè)名稱為defaultstack1的攔截器棧,在該棧中添加了要執(zhí)行的攔截器,把攔截器做了封裝,在Action中直接調(diào)用該攔截器棧即可,實(shí)現(xiàn)了攔截器棧的共享。
4、默認(rèn)攔截器棧
另外可以定義默認(rèn)的攔截器棧,即:如果某個(gè)Action中沒有定義攔截器,那么它會默認(rèn)執(zhí)行該公共的攔截器。它和interceptors標(biāo)簽屬于同一等級的,使用default-interceptor-ref定義。
<package name="testLogin" namespace="/" extends="struts-default"> <!-- 攔截器 --> <interceptors> <interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor> <!-- 定義公共的攔截器鏈,在action標(biāo)簽中只需要引用攔截器鏈 --> <interceptor-stack name="defaultinter"> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 定義默認(rèn)的攔截器棧,會自動注冊到action中 --> <default-interceptor-ref name="defaultinter"></default-interceptor-ref> <action name="demo" class="com.action.LoginAction"> <result name="error" type="redirect">/error.jsp</result> <result name="success">/success.jsp</result> <result name="checkError">/checkSession.jsp</result> </action> </package>
定義的默認(rèn)的攔截器棧只是在Action沒有指定攔截器的情況下才執(zhí)行自定義默認(rèn)的攔截器棧的,如果在Action中重定義了攔截器,那么它會覆蓋自定義默認(rèn)的攔截器棧的。
5、不執(zhí)行任何攔截器
還有一種情況是一個(gè)package中定義了默認(rèn)的攔截器,但是在編寫的某個(gè)Action中并不需要執(zhí)行任何攔截器,那么此時(shí)可以在相應(yīng)的Action中添加一個(gè)名稱為defaultStack的攔截器即可,它是系統(tǒng)默認(rèn)的攔截器,不會有任何操作。
<package name="testLogin" namespace="/" extends="struts-default"> <!-- 攔截器 --> <interceptors> <interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor> <!-- 定義公共的攔截器鏈,在action標(biāo)簽中只需要引用攔截器鏈 --> <interceptor-stack name="defaultinter"> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 定義默認(rèn)的攔截器棧,會自動注冊到action中 --> <default-interceptor-ref name="defaultinter"></default-interceptor-ref> <action name="demo" class="com.action.LoginAction"> <result name="error" type="redirect">/error.jsp</result> <result name="success">/success.jsp</result> <result name="checkError">/checkSession.jsp</result> <!-- 添加defaultStack保證不執(zhí)行攔截器 --> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package>
6、攔截方法
6.1 用法
上面的攔截器只是實(shí)現(xiàn)了對Action的攔截,其實(shí)攔截器的功能很強(qiáng)大,它也可以攔截相應(yīng)Action方法。和攔截Action不同的是想要攔截方法就必須繼承類MethodFilterInterceptor,該類封存在xwork-core.jar中,又一次證明了WebWork是Struts2的核心。另外還需要在配置文件中添加相應(yīng)的屬性來確定攔截的方法和不攔截的方法,具體配置方法如下:
<package name="testLogin" namespace="/" extends="struts-default"> <!-- 攔截器 --> <interceptors> <interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor> <!-- 定義公共的攔截器鏈,在action標(biāo)簽中只需要引用攔截器鏈 --> <interceptor-stack name="defaultinter"> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <action name="demo" class="com.action.LoginAction"> <result name="error" type="redirect">/error.jsp</result> <result name="success">/success.jsp</result> <result name="checkError">/checkSession.jsp</result> <!-- 在defaultStack中配置攔截方法,參數(shù)includeMethods中添加被攔截的方法名稱,excludeMethods中添加不需要攔截的名稱 --> <interceptor-ref name="defaultStack"> <param name="includeMethods">添加要攔截的方法名稱</param><!-- 攔截方法 --> <param name="excludeMethods">添加不需要攔截的方法名稱</param><!-- 不攔截方法 --> </interceptor-ref> </action> </package>
繼承MethodFilterInterceptor類的相應(yīng)攔截方法的類中的代碼:
package com.interceptor; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class inter extends MethodFilterInterceptor { @Override public String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("--intercept()--"); //獲取相應(yīng)的Session Map<String,Object> session=invocation.getInvocationContext().getSession(); Map request=(Map)ActionContext.getContext().get("request"); String username=(String)request.get("user.username"); if(session.get("username") != null){ String result=invocation.invoke(); System.out.println("--end()--"); return result; } } }
6.2 Demo
來看一個(gè)攔截方法的實(shí)例,并對結(jié)果進(jìn)行分析。下面的實(shí)例演示攔截方法的輸出結(jié)果,在實(shí)例中分別創(chuàng)建了一個(gè)loginAction類,添加Action要執(zhí)行的方法;Inter類,攔截器中重寫MethodFilterInterceptor方法,在控制臺中輸出是否對某個(gè)方法進(jìn)行攔截;login.jsp文件,添加三個(gè)按鈕,分別演示三個(gè)方法的執(zhí)行。
(1)struts.xml內(nèi)方法攔截器的定義,在package中定義了一個(gè)名稱為inter的攔截器,在攔截器中指定了參數(shù),includeMethods用來攔截Method1,excludeMethods中的Method2表示不攔截Methods2方法,具體配置如下代碼:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.action.extension" value=","></constant> <package name="login" extends="struts-default"> <interceptors> <interceptor name="inter" class="com.interceptor.inter"> <param name="includeMethods">Method1</param> <!-- 攔截Method1方法 --> <param name="excludeMethods">Method2</param> </interceptor> <interceptor-stack name="myInterceptor"> <interceptor-ref name="inter"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <action name="loginaction" class="com.action.loginAction"> <result name="success">success.jsp</result> <result name="error">error.jsp</result> <result name="cancel" type="redirectAction">Welcome</result> <interceptor-ref name="inter"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> </struts>
(2)loginAction類,配置login.jsp中的action,分別在該類中添加Method1-Method3三個(gè)方法,其中Method1被攔截,Method2和Method3不被攔截,最后我們查看輸出結(jié)果。
package com.action; import com.opensymphony.xwork2.ActionSupport; public class loginAction extends ActionSupport { @Override public String execute() throws Exception { if(this.username.equals("admin") && this.password.equals("admin")){ return "success"; }else if(this.username.equals("cancel") && this.password.equals("cancel")){ return "cancel"; }else{ return "error"; } } public void Method1(){ System.out.println("執(zhí)行方法:Method1"); } public void Method2(){ System.out.println("執(zhí)行方法:Method2"); } public void Method3(){ System.out.println("執(zhí)行方法:Method3"); } private String username; private String password; public String getUsername(){ return this.username; } public void setUsername(String username){ this.username=username; } public String getPassword(){ return this.password; } public void setPassword(String password){ this.password=password; } }
(3)inter類,繼承MethodFilterInterceptor類,用來實(shí)現(xiàn)攔截方法。重寫doIntercept方法,在該方法中添加攔截的相應(yīng)信息。
package com.interceptor; import java.util.Date; import java.util.Map; import com.action.loginAction; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; public class inter extends MethodFilterInterceptor { @Override protected String doIntercept(ActionInvocation invocation) throws Exception { // TODO Auto-generated method stub System.out.println("攔截器在Action執(zhí)行前攔截"+new Date()); String result=invocation.invoke(); //執(zhí)行Action方法 System.out.println("攔截器在Action執(zhí)行后攔截"+new Date()); return result; } }
(4)login.jsp,在jsp頁面上添加三個(gè)按鈕,分別演示三個(gè)方法,判斷攔截器對方法的攔截情況。三個(gè)按鈕在點(diǎn)擊后回發(fā)的action是在javascript中動態(tài)的進(jìn)行添加的,這樣做達(dá)到了一個(gè)form中執(zhí)行不同的action的方法,當(dāng)然還有其它的方法,將會在下篇文章中討論。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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>Insert title here</title> <script type="text/javascript"> //方法1,定義被攔截的方法的實(shí)例 function method1(){ var form=document.forms[0]; form.action="loginaction!Method1"; form.submit(); } //方法2,為按鈕2添加不攔截的方法 function method2(){ var form=document.forms[0]; form.action="loginaction!Method2"; form.submit(); } //方法3,為按鈕3添加不攔截的方法 function method3(){ var form=document.forms[0]; form.action="loginaction!Method3"; form.submit(); } </script> </head> <body> <form> 用戶名:<input type="text" name="username"><br> 密 碼:<input type="text" name="password"><br> <input type="submit" name="ok" value="按鈕1" onclick="method1()"> <input type="submit" name="ok1" value="按鈕2" onclick="method2()"> <input type="submit" name="ok2" value="按鈕3" onclick="method3()"> </form> </body> </html>
運(yùn)行完成后的頁面視圖:
(5)分析運(yùn)行結(jié)果,分別單擊按鈕1、2、3,在控制臺中輸出結(jié)果,按鈕1是綁定的method1,該方法在struts.xml中進(jìn)行了攔截如果結(jié)果正確的話會顯示被攔截的結(jié)果,而相應(yīng)的按鈕2和3只輸出運(yùn)行結(jié)果,因?yàn)樗鼈儧]有被攔截。那看下面的結(jié)果圖:
結(jié)果圖正好正是了我們的分析結(jié)果,按鈕1被攔截了,執(zhí)行了inter類中的doIntercept方法,二相應(yīng)的按鈕2和3沒有被攔截。也就是說,Method1被放到了方法攔截器的白名單內(nèi),執(zhí)行要攔截該方法;Method2被放到了攔截器黑名單內(nèi),不需要攔截該方法;Method3不做任何處理。
7、結(jié)語
對于攔截器的內(nèi)容就總結(jié)到這里,攔截器提供了很強(qiáng)大的功能,使得開發(fā)人員能夠在運(yùn)行時(shí)控制輸出結(jié)果,增加了編程的靈活性。另外對于任何理論性的東西都不要試圖去記憶,一定要理性的去分析,多多實(shí)踐,動手做幾個(gè)實(shí)例,分析結(jié)果更深刻的理解。
PS:Struts2(XWork)提供的攔截器的功能說明
攔截器 |
名字 |
說明 |
Alias Interceptor |
alias |
在不同請求之間將請求參數(shù)在不同名字件轉(zhuǎn)換,請求內(nèi)容不變 |
Chaining Interceptor |
chain |
讓前一個(gè)Action的屬性可以被后一個(gè)Action訪問,現(xiàn)在和chain類型的result(<result type=”chain”>)結(jié)合使用。 |
Checkbox Interceptor |
checkbox |
添加了checkbox自動處理代碼,將沒有選中的checkbox的內(nèi)容設(shè)定為false,而html默認(rèn)情況下不提交沒有選中的checkbox。 |
Cookies Interceptor |
cookies |
使用配置的name,value來是指cookies |
Conversion Error Interceptor |
conversionError |
將錯(cuò)誤從ActionContext中添加到Action的屬性字段中。 |
Create Session Interceptor |
createSession |
自動的創(chuàng)建HttpSession,用來為需要使用到HttpSession的攔截器服務(wù)。 |
Debugging Interceptor |
debugging |
提供不同的調(diào)試用的頁面來展現(xiàn)內(nèi)部的數(shù)據(jù)狀況。 |
Execute and Wait Interceptor |
execAndWait |
在后臺執(zhí)行Action,同時(shí)將用戶帶到一個(gè)中間的等待頁面。 |
Exception Interceptor |
exception |
將異常定位到一個(gè)畫面 |
File Upload Interceptor |
fileUpload |
提供文件上傳功能 |
I18n Interceptor |
i18n |
記錄用戶選擇的locale |
Logger Interceptor |
logger |
輸出Action的名字 |
Message Store Interceptor |
store |
存儲或者訪問實(shí)現(xiàn)ValidationAware接口的Action類出現(xiàn)的消息,錯(cuò)誤,字段錯(cuò)誤等。 |
Model Driven Interceptor |
model-driven |
如果一個(gè)類實(shí)現(xiàn)了ModelDriven,將getModel得到的結(jié)果放在Value Stack中。 |
Scoped Model Driven |
scoped-model-driven |
如果一個(gè)Action實(shí)現(xiàn)了ScopedModelDriven,則這個(gè)攔截器會從相應(yīng)的Scope中取出model調(diào)用Action的setModel方法將其放入Action內(nèi)部。 |
Parameters Interceptor |
params |
將請求中的參數(shù)設(shè)置到Action中去。 |
Prepare Interceptor |
prepare |
如果Acton實(shí)現(xiàn)了Preparable,則該攔截器調(diào)用Action類的prepare方法。 |
Scope Interceptor |
scope |
將Action狀態(tài)存入session和application的簡單方法。 |
Servlet Config Interceptor |
servletConfig |
提供訪問HttpServletRequest和HttpServletResponse的方法,以Map的方式訪問。 |
Static Parameters Interceptor |
staticParams |
從struts.xml文件中將<action>中的<param>中的內(nèi)容設(shè)置到對應(yīng)的Action中。 |
Roles Interceptor |
roles |
確定用戶是否具有JAAS指定的Role,否則不予執(zhí)行。 |
Timer Interceptor |
timer |
輸出Action執(zhí)行的時(shí)間 |
Token Interceptor |
token |
通過Token來避免雙擊 |
Token Session Interceptor |
tokenSession |
和Token Interceptor一樣,不過雙擊的時(shí)候把請求的數(shù)據(jù)存儲在Session中 |
Validation Interceptor |
validation |
使用action-validation.xml文件中定義的內(nèi)容校驗(yàn)提交的數(shù)據(jù)。 |
Workflow Interceptor |
workflow |
調(diào)用Action的validate方法,一旦有錯(cuò)誤返回,重新定位到INPUT畫面 |
Parameter Filter Interceptor |
N/A |
從參數(shù)列表中刪除不必要的參數(shù) |
Profiling Interceptor |
profiling |
通過參數(shù)激活profile |
相關(guān)文章
通過RedisTemplate連接多個(gè)Redis過程解析
這篇文章主要介紹了通過RedisTemplate連接多個(gè)Redis過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08java中棧和隊(duì)列的實(shí)現(xiàn)和API的用法(詳解)
下面小編就為大家?guī)硪黄猨ava中棧和隊(duì)列的實(shí)現(xiàn)和API的用法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05httpclient 請求http數(shù)據(jù),json轉(zhuǎn)map的實(shí)例
下面小編就為大家?guī)硪黄猦ttpclient 請求http數(shù)據(jù),json轉(zhuǎn)map的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12淺談Java中Lambda表達(dá)式的相關(guān)操作
java8新特性,Lambda是一個(gè)匿名函數(shù),類似Python中的Lambda表達(dá)式、js中的箭頭函數(shù),目的簡化操作,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Mybatis的mapper.xml中if標(biāo)簽test判斷的用法說明
這篇文章主要介紹了Mybatis的mapper.xml中if標(biāo)簽test判斷的用法說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06SpringBoot整合SpringSecurity實(shí)現(xiàn)權(quán)限控制之實(shí)現(xiàn)多標(biāo)簽頁
這篇文章主要介紹了SpringBoot整合SpringSecurity實(shí)現(xiàn)權(quán)限控制之實(shí)現(xiàn)多標(biāo)簽頁,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11