รู้เบื้องต้นเกี่ยวกับ mybatis interceptor
MyBatis มีฟังก์ชั่นปลั๊กอิน แม้ว่ามันจะเรียกว่าปลั๊กอิน แต่จริงๆแล้วมันเป็นฟังก์ชัน interceptor แล้วตัวดักจับ mybatis ทำอะไร?
ไปที่เว็บไซต์อย่างเป็นทางการเพื่อดู:
MyBatis ช่วยให้คุณสกัดกั้นการโทร ณ จุดหนึ่งระหว่างการดำเนินการของคำสั่งที่แมป โดยค่าเริ่มต้นวิธีการเรียก mybatis อนุญาตให้ปลั๊กอินเพื่อสกัดกั้นรวมถึง:
เราได้เห็นวิธีการบางอย่างที่สามารถสกัดกั้นอินเทอร์เฟซของผู้ดำเนินการเช่นการอัปเดตการสืบค้นการกระทำการย้อนกลับและวิธีการอื่น ๆ รวมถึงวิธีการบางอย่างของอินเทอร์เฟซอื่น ๆ
รอ.
สรุปโดยรวมคือ:
การใช้ interceptors
Interceptor บทนำและการกำหนดค่า
ก่อนอื่นมาดูคำจำกัดความของอินเทอร์เฟซของ mybatis interceptor:
Interface Interceptor สาธารณะ {Object Intercept (การเรียกร้องการเรียกใช้) โยนได้ throwable; ปลั๊กอินวัตถุ (เป้าหมายวัตถุ); โมฆะ setProperties (คุณสมบัติคุณสมบัติ);}มันค่อนข้างง่ายมีเพียง 3 วิธี MyBatis ไม่มีคลาสการใช้งานอินเตอร์เฟส Interceptor โดยค่าเริ่มต้นและนักพัฒนาสามารถใช้ interceptors ที่ตอบสนองความต้องการของพวกเขา
นี่คือตัวอย่างของการสกัดกั้นจากเว็บไซต์ทางการ MyBatis:
@Intercepts ({@signature (type = executor.class, method = "update", args = {mappedStatement.class, object.class})}) คลาสสาธารณะตัวอย่าง Plassplugin ใช้ interceptor {การสกัดกั้นวัตถุสาธารณะ } ปลั๊กอินวัตถุสาธารณะ (เป้าหมายวัตถุ) {return plugin.wrap (เป้าหมายสิ่งนี้); } โมฆะสาธารณะ setProperties (คุณสมบัติคุณสมบัติ) {}}การกำหนดค่า XML ทั่วโลก:
<plugins> <plugin interceptor = "org.format.mybatis.cache.interceptor.examplePlugin"> </plugin> </plugins>
การสกัดกั้นวิธีการอัปเดตของอินเทอร์เฟซผู้ดำเนินการ (อันที่จริงมันเป็นการเพิ่มการลบและการปรับเปลี่ยนการดำเนินการของ SQLSession) วิธีการอัปเดตทั้งหมดที่เรียกใช้งานคอนเทคเตอร์จะถูกสกัดกั้นโดย interceptor
การวิเคราะห์ซอร์สโค้ด
มาวิเคราะห์ซอร์สโค้ดที่อยู่เบื้องหลังรหัสนี้
ก่อนอื่นให้เริ่มการวิเคราะห์จากไฟล์ source-> config:
XMLCONFIGBUILDER PARSES PLUGINELEMENT วิธีการส่วนตัวของ MyBatis Global Configuration File:
Private Void Pluginelement (Xnode Parent) พ่นข้อยกเว้น {ถ้า (parent! = null) {สำหรับ (xnode child: parent.getchildren ()) {string interceptor = child.getStringattribute ("interceptor"); คุณสมบัติคุณสมบัติ = child.getchildrenasproperties (); interceptor interceptorInstance = (interceptor) ResolveClass (interceptor) .newInstance (); InterceptorInstance.SetProperties (คุณสมบัติ); configuration.addinterceptor (interceptorinstance); -รหัสการแยกวิเคราะห์ที่เฉพาะเจาะจงนั้นค่อนข้างง่ายดังนั้นฉันจะไม่โพสต์ ส่วนใหญ่จะทำให้คลาสที่แสดงโดยแอตทริบิวต์ interceptor ในโหนดปลั๊กอินโดยการสะท้อน จากนั้นเรียกใช้เมธอด addInterceptor ของการกำหนดค่าคลาสการกำหนดค่าทั่วโลก
โมฆะสาธารณะ addInterceptor (interceptor interceptor) {interceptorchain.addinterceptor (interceptor); -Interceptorchain นี้เป็นคุณสมบัติภายในของการกำหนดค่าและประเภทของมันคือ interceptorchain ซึ่งเป็นห่วงโซ่การดักจับ มาดูคำจำกัดความของมันกันเถอะ:
คลาสสาธารณะ interceptorchain {รายการสุดท้ายส่วนตัว <Palceptor> interceptors = arrayList ใหม่ <Extceptor> (); ปลั๊กอินวัตถุสาธารณะ (เป้าหมายวัตถุ) {สำหรับ (interceptor interceptor: interceptors) {target = interceptor.plugin (เป้าหมาย); } คืนเป้าหมาย; } โมฆะสาธารณะ addInterceptor (interceptor interceptor) {interceptors.add (interceptor); } รายการสาธารณะ <Palceptor> getInterceptors () {return collections.unmodifiableList (interceptors); -ตอนนี้เราเข้าใจการแยกวิเคราะห์การกำหนดค่าการสกัดกั้นและความเป็นเจ้าของของตัวดักจับตอนนี้เรามองย้อนกลับไปว่าทำไมการสกัดกั้นวิธีการเหล่านี้
พารามิเตอร์สาธารณะ NEWPARAMETERHANDLER (MAPPEDSTATEMENT MAPPEDSTATEMENT, พารามิเตอร์ OBJECT PARAMETEROBJECT, BUNTSQL BUNTSQL) {พารามิเตอร์ parameterHandler พารามิเตอร์ hANDLER = MAPPEDSTATEMENT.GETLANG () CreateParameterHandler parameterHandler = (parameterHandler) interceptorchain.pluginall (parameterHandler); Return ParameterHandler;} ผลลัพธ์สาธารณะ ethandler newresultsethandler (ผู้ดำเนินการบริหาร, การแม็พสเตเมชั่นการแม็พสเตท, Rowbounds Rowbounds, parameterhandler parameterhandler, resulthandler resulthandler, boundsql boundsql) {ผลลัพธ์ ParameterHandler, Resulthandler, BoundSQL, Rowbounds); ResultsEthandler = (ResultsEthandler) Interceptorchain.pluginall (ผลลัพธ์ ethandler); Returne ResultsEthandler;} Public Pattracthandler NewStatementHandler (Executor Executor, MappedStatement MappedStatement, Object ParameterObject, Rowbounds Rowbounds, Resulthandler Resulthandler, Boundsql Boundsql) resulthandler, boundsql); คำชี้แจง handler = (attementhandler) interceptorchain.pluginall (attementhandler); return attementHandler;} ผู้ดำเนินการสาธารณะ newExecutor (ธุรกรรมธุรกรรม, executortype executortype, boolean autocommit) {executortype = executortype == null? DefaultExecutortype: Executortype; executortype = executortype == null? Executortype.simple: Executortype; ผู้บริหารผู้บริหาร; if (executortype.batch == executortype) {executor = new batchexecutor (สิ่งนี้, ธุรกรรม); } อื่นถ้า (executortype.reuse == executortype) {executor = ใหม่ reuseExecutor (สิ่งนี้, ธุรกรรม); } else {executor = new SimpleExecutor (สิ่งนี้, ธุรกรรม); } if (cacheenabled) {executor = new cachingExecutor (Executor, AutoCommit); } executor = (executor) interceptorchain.pluginall (ผู้ดำเนินการ); return executor;}วิธีการ 4 วิธีข้างต้นเป็นวิธีการกำหนดค่าทั้งหมด วิธีการเหล่านี้จะถูกดำเนินการในการดำเนินการของ mybatis (เพิ่มลบแก้ไขและสอบถาม) ลำดับของการดำเนินการคือ executor, parameterhandler, ผลลัพธ์ ethandler, attementhandler (ที่ parameterhandler และ resultethandler ถูกสร้างขึ้นเมื่อสร้างคำสั่ง handler [3 คลาสการใช้งานที่มีอยู่ callablestatementhandler, PreparedStatementHandler, simplestatementler]
หลังจาก 4 วิธีเหล่านี้อินสแตนซ์วัตถุที่เกี่ยวข้องพวกเขาจะเรียกวิธีการปลั๊กอินของ interceptorchain ดังที่ได้กล่าวไว้ก่อนหน้านี้ปลั๊กอินของ interceptorchain ได้รับการแนะนำซึ่งคือการสำรวจตัวดักจับทั้งหมดจากนั้นเรียกใช้วิธีการปลั๊กอินของแต่ละ interceptor หมายเหตุ: ค่าส่งคืนของวิธีการปลั๊กอินของ interceptor จะถูกกำหนดโดยตรงไปยังวัตถุต้นฉบับ
เนื่องจากสามารถสกัดกั้นคำแถลงได้อินเทอร์เฟซนี้ส่วนใหญ่เกี่ยวข้องกับการสร้างไวยากรณ์ SQL ตัวอย่างเช่นฟังก์ชั่นของการเพจสามารถนำไปใช้กับ interceptor คุณจะต้องประมวลผล SQL ในคลาสการใช้งานอินเตอร์เฟสของคำสั่ง Handler ในวิธีการปลั๊กอินของ interceptor และคุณสามารถใช้การสะท้อนกลับเพื่อใช้งานได้
MyBatis ยังมีคำอธิบายประกอบสำหรับ @Intercepts และ @Signature เกี่ยวกับตัวดักจับ ตัวอย่างของเว็บไซต์อย่างเป็นทางการคือการใช้คำอธิบายประกอบทั้งสองนี้รวมถึงการใช้คลาสปลั๊กอิน:
@OverridePublic Object Plugin (เป้าหมายวัตถุ) {return plugin.wrap (เป้าหมาย, สิ่งนี้);}มาวิเคราะห์ซอร์สโค้ดของ 3 "ชุดค่าผสมใหม่" เหล่านี้ ก่อนอื่นมาดูวิธีการห่อของคลาสปลั๊กอิน:
การห่อวัตถุสแตติกสาธารณะ (เป้าหมายวัตถุ, interceptor interceptor) {แผนที่ <คลาส <?>, ตั้งค่า <วิธีการ >> signatureMap = getSignatureMap (interceptor); คลาส <?> type = target.getClass (); คลาส <?> [] อินเทอร์เฟซ = getAllInterfaces (พิมพ์, signatureMap); if (interfaces.length> 0) {return proxy.newproxyinstance (type.getclassloader (), อินเตอร์เฟส, ปลั๊กอินใหม่ (เป้าหมาย, อินเตอร์เฟส, signatureMap)); } return target;}คลาสปลั๊กอินใช้อินเทอร์เฟซ InvocationHandler เห็นได้ชัดว่าเราเห็นว่าคลาสพร็อกซีแบบไดนามิกที่จัดทำโดย JDK นั้นถูกส่งคืนที่นี่ ลองผ่าวิธีอื่น ๆ ที่เรียกโดยวิธีนี้:
วิธี getSignaturemap:
แผนที่คงที่ส่วนตัว <คลาส <?>, ตั้งค่า <วิธีการ >> getSignatureMap (interceptor interceptor) {intercepts interceptsannotation = interceptor.getClass (). getannotation (intercepts.class); if (InterceptSannotation == null) {// ปัญหา #251 โยน pluginexception ใหม่ ("ไม่พบคำอธิบายประกอบ @Intercepts ใน interceptor" + interceptor.getClass (). getName ()); } Signature [] SIGS = InterceptSannotation.Value (); แผนที่ <คลาส <?>, ตั้งค่า <เมธอด >> signatureMap = hashmap ใหม่ <คลาส <?>, ตั้งค่า <วิธีการ >> (); สำหรับ (Signature SIG: SIGS) {SET <method> Methods = SignatureMap.get (sig.type ()); if (methods == null) {methods = new hashset <mith> (); signatureMap.put (sig.type (), วิธีการ); } ลอง {วิธีการ = sig.type (). getMethod (sig.method (), sig.args ()); วิธีการ ADD (วิธีการ); } catch (nosuchmethodexception e) {โยน pluginexception ใหม่ ("ไม่สามารถหาวิธีใน" + sig.type () + "ชื่อ" + sig.method () + "สาเหตุ:" + e, e); }} ส่งคืน signatureMap;}วิธี GetSignatureMap คำอธิบาย: ก่อนอื่นคุณจะได้รับคำอธิบายประกอบ @Interceptors ของคลาส Interceptor จากนั้นรับคอลเลกชัน @Signature Annotation ของคุณลักษณะของคำอธิบายประกอบนี้จากนั้นสำรวจคอลเลกชันนี้ เนื่องจากแอตทริบิวต์ @Signature ที่มีคำอธิบายประกอบโดย @Interceptors เป็นคุณสมบัติในที่สุดมันจะส่งคืนแผนที่ด้วยประเภทเป็นคีย์และค่าตามที่ตั้งไว้ <method>
@Intercepts ({@signature (type = executor.class, method = "update", args = {mappedStatement.class, object.class})})ตัวอย่างเช่นคำอธิบายประกอบ @Interceptors จะส่งคืนคีย์เป็นผู้ดำเนินการและค่าเป็นคอลเลกชัน (มีเพียงองค์ประกอบเดียวในคอลเลกชันนี้นั่นคืออินสแตนซ์วิธีการอินสแตนซ์วิธีการนี้คือวิธีการอัปเดตของอินเทอร์เฟซผู้บริหารและวิธีนี้มีพารามิเตอร์ของประเภท MappedStatement และ Object) อินสแตนซ์วิธีการนี้ได้มาจากวิธีการและแอตทริบิวต์ของ ARGS ของ @Signature หากพารามิเตอร์ ARGS ไม่สอดคล้องกับวิธีการประเภทประเภทประเภทจะมีการโยนข้อยกเว้น
วิธี getAllInterfaces:
คลาสสแตติกส่วนตัว <?> [] getAllInterfaces (คลาส <?> พิมพ์, แผนที่ <คลาส <?>, ตั้งค่า <เมธอด >> signatureMap) {set <class <? >> interfaces = new hashset <คลาส <? >> (); ในขณะที่ (type! = null) {สำหรับ (คลาส <?> c: type.getInterfaces ()) {ถ้า (signaturemap.containskey (c)) {interfaces.add (c); }} type = type.getSuperClass (); } return interfaces.toArray (คลาสใหม่ <?> [interfaces.size ()]);}เมธอด GetAllInterfaces คำอธิบาย: ตามเป้าหมายอินสแตนซ์เป้าหมาย (เป้าหมายนี้เป็นคลาสที่ mybatis interceptor สามารถสกัดกั้นตามที่กล่าวไว้ก่อนหน้านี้ผู้ดำเนินการ, พารามิเตอร์ handler, ผลลัพธ์ ethandler, attementhandler) และคลาสแม่
ดังนั้นฟังก์ชั่นของคลาสปลั๊กอินคือการได้รับแอตทริบิวต์ @Signature Array ของแอตทริบิวต์คำอธิบายประกอบตามคำอธิบายประกอบ @Interceptors จากนั้นใช้การสะท้อนเพื่อค้นหาวิธีการที่สอดคล้องกันตามประเภทวิธีการและ ARGS ของแต่ละ @Signature ในที่สุดขึ้นอยู่กับอินเทอร์เฟซที่ดำเนินการโดยวัตถุเป้าหมายที่เรียกว่าตัดสินใจว่าจะส่งคืนวัตถุพร็อกซีเพื่อแทนที่วัตถุเป้าหมายดั้งเดิมหรือไม่
ตัวอย่างเช่นในเว็บไซต์ทางการ MyBatis เมื่อการกำหนดค่าเรียกใช้เมธอด newExecutor การอัปเดต (MappedStatement MS, Methate Object Parameter) ของอินเตอร์เฟส Executor ถูกสกัดกั้นโดย interceptor ดังนั้นการสิ้นสุดจะถูกส่งกลับด้วยปลั๊กอินคลาสพร็อกซีไม่ใช่ผู้บริหาร เมื่อเรียกวิธีการด้วยวิธีนี้หากเป็นคลาสพร็อกซีมันจะถูกดำเนินการ:
วัตถุสาธารณะเรียกใช้ (พร็อกซีวัตถุวิธีเมธอด, วัตถุ [] args) พ่น throwable {ลอง {set <method> methods = signatureMap.get (method.getDeclaringClass ()); if (methods! = null && methods.contains (วิธีการ)) {return interceptor.intercept (การเรียกใช้ใหม่ (เป้าหมาย, วิธี, args)); } return method.invoke (เป้าหมาย, args); } catch (exception e) {โยน expsexutil.unwrapthrowable (e); -ถูกต้องหากพบวิธีการที่สอดคล้องกันและเป็นพร็อกซีวิธีการสกัดกั้นของอินเตอร์เฟส interceptor จะถูกดำเนินการ
คลาสการเรียกร้องนี้มีดังนี้:
การเรียกร้องระดับสาธารณะ {เป้าหมายวัตถุส่วนตัว; วิธีการส่วนตัว วัตถุส่วนตัว [] args; การเรียกใช้สาธารณะ (เป้าหมายวัตถุ, วิธีวิธี, วัตถุ [] args) {this.target = เป้าหมาย; this.method = วิธีการ; this.args = args; } วัตถุสาธารณะ getTarget () {return target; } วิธีการสาธารณะ getMethod () {วิธีการส่งคืน; } วัตถุสาธารณะ [] getArgs () {return args; } วัตถุสาธารณะดำเนินการต่อ () พ่น InvocationTargetException, ungleclAccessException {return method.invoke (เป้าหมาย, args); -วิธีกระบวนการคือการเรียกวิธีดั้งเดิม (ไม่มีพร็อกซี)
สรุป
ในบรรดา 3 วิธีที่จัดทำโดยอินเตอร์เฟส mybatis interceptor วิธีการปลั๊กอินถูกใช้ในกระบวนการก่อสร้างของโปรเซสเซอร์บางตัว (ตัวจัดการ) วิธีการสกัดกั้นใช้เพื่อจัดการการดำเนินการของคลาสพร็อกซี วิธีการ setProperties ใช้เพื่อตั้งค่าคุณสมบัติ interceptor
ในความเป็นจริงวิธีการที่ได้รับจากเว็บไซต์ทางการ MyBatis เพื่อใช้ @Interceptors และ @Signature Annotations และคลาสปลั๊กอินเพื่อจัดการกับตัวดักจับไม่จำเป็นต้องใช้โดยตรงในลักษณะนี้ นอกจากนี้เรายังสามารถละทิ้งคลาสทั้งสามนี้และดำเนินการโดยตรงตามประเภทของอินสแตนซ์เป้าหมายภายในวิธีการปลั๊กอิน
โดยรวมแล้ว mybatis interceptor ยังคงง่ายมาก ตัวดักจับตัวเองไม่ต้องการจุดความรู้มากเกินไป แต่การเรียนรู้การสกัดกั้นนั้นต้องการความคุ้นเคยกับแต่ละอินเทอร์เฟซใน mybatis เนื่องจากตัวดักจับเกี่ยวข้องกับจุดความรู้ของแต่ละอินเตอร์เฟส
สรุป
ข้างต้นเป็นการสำรวจหลักการของ mybatis interceptor ที่แนะนำโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับทุกคนในเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!