هذه المقالة تدور حول مشكلة صعبة في جافا. باستخدام مكتبة Java Core لتنفيذ أساليب AOP بسيطة وتحليل ومقارنة رمز المثيل. ما يلي هو كل المحتوى:
Spring هو إطار مفتوح المصدر شائع للغاية ، و AOP (البرمجة المقطعية) هو أحد أهم مفاهيم الربيع. من أجل فهم أفكار AOP وتعلمها بشكل أفضل ، يعد استخدام المكتبة الأساسية لتحقيقها في وقت واحد طريقة جيدة.
أولاً ، دعونا نقدم مفهوم AOP. AOP (البرمجة الموجهة نحو الجانب) ، أي البرمجة الموجهة نحو العرض. ما يسمى البرمجة الموجهة نحو العرض هي فكرة تصميم التعليمات البرمجية من منظور منطقة مستعرضة. تتمثل فكرة OOP التقليدية في استخدام ميراث التغليف وتعدد الأشكال لبناء علاقة هرمية رأسية ، ولكنها ليست مناسبة لتحديد العلاقات الأفقية. توفر AOP IDEA مكملة جيدة لهذا.
على سبيل المثال ، غالبًا ما يكون رمز إدارة السجل منتشراً أفقياً في العديد من مستويات الكائنات ، ولكن لا علاقة له بالوظائف الأساسية للكائنات المقابلة. هناك أيضًا العديد من الرموز المماثلة ، مثل التحقق من الإذن ، وإخراج التصحيح ، ومعالجة المعاملات ، وما إلى ذلك ، والتي هي أيضًا متماثلة. هذا ليس مفضيًا إلى إعادة استخدام الكود والإدارة.
في هذا الوقت ، ظهرت تقنية AOP. يستخدم تقنية "crosscutting" لاختراق عميق في كائن التغليف ، وتغليف السلوكيات الشائعة التي تؤثر على فئات متعددة في وحدة قابلة لإعادة الاستخدام ، وتسميتها "الجانب" ، أي التقطيع. يتم تغليف ما يسمى "القسم" ببساطة بالمنطق أو المسؤوليات التي لا ترتبط بالرجال ، ولكنها تسمى وحدة العمل بالاشتراك ، والتي تكون مريحة لتقليل الكود المكررة للنظام ، وتقليل الاقتران بين الوحدات النمطية ، والتوصيل إلى قابلية التشغيل والصيانة اللاحقة.
فكيف يتم تنفيذ AOP؟
الإجابة هي الوكيل الديناميكي (سيكون هناك فصل آخر عن الوكيل للحصول على التفاصيل ، لذلك لن أخوض في التفاصيل هنا). هناك طريقتان لتنفيذ الوكيل الديناميكي ، أحدهما هو الوكيل الديناميكي JDK ، والآخر هو الوكيل الديناميكي CGLIB.
ثم استخدم طريقتين لصنع كستناء بسيط.
دعنا نتصميم سيناريو أولاً ، لنفترض أن لدينا واجهة حوسبة icalculator وآلة حاسبة CalculatorImpl التي تنفذ هذه الواجهة.
الواجهة العامة icalculator {// الإضافة العملية العامة int add (int a ، int b) ؛ // الطرح العام int اطرح (int a ، int b) ؛ // multiply public int multiply (int a ، int b) ؛ // Defidation Public INT (int a ، int b) ؛} CANCLATORIMPLERS Public CATIOLIMPL تنفذ icalculator {Override public int add (int a ، int b) {return a + b ؛ } Override public int اطرح (int a ، int b) {return a - b ؛ } Override public int multiply (int a ، int b) {return a * b ؛ } Override public int define (int a ، int b) {return a / b ؛ }}كيفية تسجيل إجمالي عدد المرات التي يتم فيها استخدام طريقة الآلة الحاسبة دون تغيير الكود الداخلي لفئة الحاسبة الأصلية؟
مع الوكيل الديناميكي ، يكون الأمر بسيطًا جدًا. قم أولاً بإنشاء فئة وتنفيذ واجهة InvocationHandler ، وتجاوز طريقة الاستدعاء.
الطبقة العامة TestHandler تنفذ invocationHandler {private Object TargetObject ؛ int intimes. // ربط كائن مندوب وإرجاع كائن Public Public Comply Bind (Object TargetObject) {this.targetObject = targetObject ؛ return proxy.newproxyinstance (targetObject.getClass (). getClassloader () ، targetObject.getClass (). } Override الكائن العام استدعاء (وكيل الكائن ، طريقة الطريقة ، الكائن [] args) يلقي رمي {// افعل شيئًا قبل () ؛ نتيجة الكائن = method.invoke (TargetObject ، args) ؛ بعد()؛ نتيجة العودة } private void قبل () {system.out.println ("يمكننا أن نفعل شيئًا قبل حساب.") ؛ } private void بعد () {useTimes ++ ؛ System.out.println ("use:"+useTimes+"Times") ؛ }}على الرغم من أنه يبدو أن هناك الكثير من التعليمات البرمجية ، إلا أن الطريقة الرئيسية هي طريقة الاستدعاء. نتيجة الكائن = method.invoke (TargetObject ، args) ؛ يعادل الاستمرار في استخدام المعلمات الأصلية لتنفيذ الطريقة الأصلية. قبل وبعد هنا وظائف مخصصة ، والتي يمكن أن تفعل بعض الأشياء التي نريد القيام بها قبل وبعد تنفيذ رمز الكائن ، مثل عدد الاستخدام هنا.
في طريقة الربط ، يتم تمرير كائن الوكيل الهدف ويتم إرجاع مثيل فئة الوكيل. بعد ذلك ، دعونا نرى كيفية استخدام:
الفئة العامة testproxy {public static void main (string [] args) {testHandler proxy = new TestHandler () ؛ icalculator حساب = (icalculator) proxy.bind (New CalculatorImpl ()) ؛ int النتيجة = calculate.add (1،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = calculate.subtract (3،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = calculater.multiply (4،6) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = calculater.devide (6،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ }}نقوم أولاً بتحديد جهاز اختبار ، ثم نحصل على مثيل وكيل من خلال طريقة الربط ، ثم يمكننا استخدام هذا المثيل مباشرة. نتائج التشغيل كما يلي:
يمكننا أن نفعل شيئًا قبل حساب. المستخدمة: 1 النتيجة هي: 3 نستطيع أن نفعل شيئًا قبل حساب. المستخدمة: 2 النتيجة هي: 1 يمكن أن نفعل شيء قبل حساب. المستخدمة: 3 النتيجة هي: 24 We يمكن أن تفعل شيئًا قبل حساب. المستخدمة: 4 النتيجة هي: 3
وبهذه الطريقة ، نقوم بتنفيذ امتداد الكود دون تعديل الكود الداخلي لـ CalculatorImpl.
بعد ذلك ، استخدم CGLIB لتنفيذها مرة واحدة.
قم أولاً بإنشاء فئة لتنفيذ واجهة MethodInterceptor وتجاوز طريقة التقاطع. تشبه الرموز الأخرى استخدام وكيل JDK ، ولكن عملية الحصول على كائنات وكيل مختلفة.
الطبقة العامة cglibproxy تنفذ methodInterceptor {private int useTimes ؛ هدف الهدف الخاص ؛ الكائن العام getInstance (هدف الكائن) {this.target = target ؛ Enhancer Enhancer = New Enhancer () ؛ ensancer.SetSuperClass (this.target.getClass ()) ؛ ensancer.setCallback (هذا) ؛ إرجاع ensancer.create () ؛ } Override اعتراض الكائن العام (الكائن O ، طريقة الطريقة ، الكائنات [] ، methodproxy methodproxy) رمي رمي {قبل () ؛ نتيجة الكائن = methodproxy.invokesuper (O ، الكائنات) ؛ بعد()؛ نتيجة العودة } private void قبل () {system.out.println ("يمكننا أن نفعل شيئًا قبل حساب.") ؛ } private void بعد () {useTimes ++ ؛ System.out.println ("use:"+useTimes+"Times") ؛ }}اختبره:
الفئة العامة testcglibproxy {public static void main (string [] args) {cglibproxy cglibproxy = new cglibproxy () ؛ ICalCulator حساب = (icalculator) cglibproxy.getInstance (New CalculatorImpl ()) ؛ int النتيجة = calculate.add (1،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = calculate.subtract (3،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = حساب. multiply (4،6) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ النتيجة = حساب. devide (6،2) ؛ System.out.println ("النتيجة هي:"+نتيجة) ؛ }}نتائج التشغيل كما يلي:
يمكننا أن نفعل شيئًا قبل حساب. المستخدمة: 1 النتيجة هي: 3 نستطيع أن نفعل شيئًا قبل حساب. المستخدمة: 2 النتيجة هي: 1 يمكن أن نفعل شيء قبل حساب. المستخدمة: 3 النتيجة هي: 24 We يمكن أن تفعل شيئًا قبل حساب. المستخدمة: 4 النتيجة هي: 3
الآن نحصل على نفس النتيجة. (مطلوب حزمتان ، CGLIB-2.2.2.JAR ASM-3.3.JAR)
كلتا الطريقتين لها نقاط القوة الخاصة بها. يحتاج Proxy JDK إلى إعداد واجهة قبل تنفيذ الوكيل. هذا هو عيبها وصالحها. العيب هو أن هذا سيكون أكثر إثارة للقلق ، ولا يمكن أن يعتمد على تلك المغلفة بالفعل ولا تنفذ الواجهة. لا تتطلب طريقة وكيل CGLIB استخدام واجهات. ولكن بسبب هذا أيضًا ، فإن وكيل JDK يعترض فقط الطرق التي تشتتب على واجهات في الفصل ، بينما يعترض CGLIB جميع مكالمات الفئة. كلاهما له إيجابيات وسلبيات ، لذلك يجب تحليل ظروف محددة. في الربيع ، يتم استخدام وضعين وكيلين مختلطين.