ما هو AOP
يمكن القول أن AOP (البرمجة الموجهة نحو الجانب ، والبرمجة الموجهة نحو الجانب) هي ملحق وتحسين OOP (البرمجة الموجهة للكائنات). يقدم OOP مفاهيم مثل التغليف والميراث وتعدد الأشكال لإنشاء تسلسل هرمي كائن لمحاكاة مجموعة من السلوكيات العامة. عندما نحتاج إلى إدخال السلوك العام للأشياء المتناثرة ، يبدو OOP عاجزًا. أي أن OOP يتيح لك تحديد العلاقات من أعلى إلى أسفل ، ولكنه غير مناسب لتحديد العلاقات من اليسار إلى اليمين. على سبيل المثال ، وظيفة التسجيل. غالبًا ما يكون رمز السجل مبعثرًا أفقيًا عبر جميع مستويات الكائنات دون أي علاقة بالوظائف الأساسية للكائن الذي ينتشر إليه. وينطبق الشيء نفسه على الأنواع الأخرى من التعليمات البرمجية ، مثل الأمان ومعالجة الاستثناءات والشفافية. هذا النوع من الكود غير ذي صلة المبعثر في كل مكان يسمى رمز قطع المتقاطع. في تصميم OOP ، يسبب الكثير من تكرار الكود ، والذي لا يفضي إلى إعادة استخدام كل وحدة.
على العكس من ذلك ، تستخدم تقنية AOP تقنية تسمى "التبادل المتقاطع" لتشريح داخل الكائن المغطى وتغليف السلوكيات الشائعة التي تؤثر على فئات متعددة في وحدة قابلة لإعادة الاستخدام وتسميتها "جانب" ، أي ما يسمى "الجوانب". تقليل الاقتران بين الوحدات ، وتعزيز قابلية التشغيل في المستقبل وقابلية الصيانة. AOP يمثل علاقة أفقية. إذا كانت "الكائن" عبارة عن أسطوانة مجوفة ، فإن تغليف خصائص وسلوك الكائن ؛ ثم طريقة البرمجة الموجهة نحو الجانب تشبه شفرة حادة ، وقطع هذه الأسطوانات المجوفة للحصول على معلومات داخلية. قسم القطع هو ما يسمى "الوجه". ثم أعادت هذه المقاطع المقطوعة بأيديها الذكية دون ترك أي أثر.
باستخدام تقنية "Crosscutting" ، يقسم AOP نظام البرمجيات إلى جزأين: القلق الأساسي والقلق المتقاطع. العملية الرئيسية لمعالجة الأعمال هي التركيز الأساسي ، والجزء الذي لا علاقة له به هو التركيز المستعرض. تتمثل إحدى خصائص المخاوف المتقاطعة في أنها تحدث في كثير من الأحيان في مخاوف متعددة ، وتشبه بشكل أساسي في كل مكان. على سبيل المثال ، مصادقة الإذن وتسجيلها ومعالجة المعاملات. يتمثل دور AOP في فصل المخاوف المختلفة في النظام وفصل المخاوف الأساسية عن المخاوف المتقاطعة. كما قال آدم ماجي ، المعماري الأول في Avanade ، فإن الفكرة الأساسية لـ AOP هي "فصل منطق الأعمال في التطبيق عن الخدمات الشائعة التي تدعمه".
تنقسم التكنولوجيا المتمثلة في تطبيق AOP بشكل أساسي إلى فئتين: الأول هو استخدام تقنية الوكيل الديناميكي ، واستخدام طريقة اعتراض الرسائل لتزيين الرسالة لاستبدال تنفيذ سلوك الكائن الأصلي ؛ والآخر هو استخدام طريقة النسيج الثابت لإدخال بناء جملة معين لإنشاء "وجوه" ، بحيث يمكن للمترجم نسج رمز مرتبط بـ "الوجوه" أثناء التجميع.
سيناريوهات استخدام AOP
يتم استخدام AOP لتغليف المخاوف المتقاطعة ، والتي يمكن استخدامها في السيناريوهات التالية:
أذونات المصادقة
التخزين المؤقت للتخزين المؤقت
سياق تمرير المحتوى
معالجة الخطأ
تحميل كسول
تصحيح الأخطاء
قطع الأشجار والتتبع والتوصيف والمراقبة
تحسين الأداء
الثبات
تجميع الموارد
التزامن
المعاملات
مفاهيم AOP ذات الصلة
الجانب: نموذج للتركيز الذي قد يزيد من كائنات متعددة. تعد إدارة المعاملات مثالًا جيدًا على المخاوف المتقاطعة في تطبيقات J2EE. يتم تنفيذ الجانب باستخدام مستشار أو اعتراض الربيع.
JoinPoint: نقطة واضحة أثناء تنفيذ البرنامج ، مثل استدعاء الطريقة أو استثناء محدد يتم طرحه.
النصيحة: الإجراءات التي يؤديها إطار AOP في نقطة اتصال محددة. تشمل أنواع مختلفة من الإخطارات "حول" ، "قبل" و "رميات" الإخطارات. وتناقش أنواع الإخطار أدناه. تستخدم العديد من أطر عمل AOP ، بما في ذلك Spring ، اعتراضات كنماذج للإخطار للحفاظ على نقاط اتصال سلسلة اعتراضية "مستديرة". يتم تعريف أربعة نصائح في الربيع: beforeadvice ، بعد الراغاف ، رمي وديناميكينتروديفايس
PointCut: يحدد مجموعة من نقاط الاتصال التي سيتم تشغيل الإخطار. يجب أن يسمح إطار AOP للمطورين بتحديد نقاط الدخول: على سبيل المثال ، باستخدام تعبيرات منتظمة. يحدد Spring واجهة PointCut ، والتي يتم استخدامها لدمج MethodMatcher و ClassFilter ، والتي يمكن فهمها بوضوح من خلال الاسم. يتم استخدام MoneyMatcher للتحقق مما إذا كان يمكن استخدام طريقة الفئة المستهدفة لتطبيق هذا الإشعار ، بينما يتم استخدام ClassFilter للتحقق مما إذا كان ينبغي تطبيق PointCut على الفئة المستهدفة.
مقدمة: أضف طريقة أو حقل إلى الفصل الذي يتم إخطاره. يسمح الربيع بإدخال واجهات جديدة لأي كائن يتم إخطاره. على سبيل المثال ، يمكنك تبسيط التخزين المؤقت باستخدام مقدمة تمكن أي كائن من تنفيذ واجهة ISModified. لاستخدام مقدمة في فصل الربيع ، يمكنك استخدام مفوض الإبلاغ الإبلاغ لتنفيذ الإخطارات ، واستخدام DefaultIntroductionAdvisor لتكوين الواجهة لتنفيذ نصيحة وطبقة الوكيل.
الكائن الهدف: كائن يحتوي على نقطة الاتصال. المعروف أيضا باسم كائن مخطط أو وكيل. بوجو
AOP Proxy: كائن تم إنشاؤه بواسطة إطار AOP ، يحتوي على إشعارات. في الربيع ، يمكن أن يكون وكيل AOP وكيلًا ديناميكيًا لـ JDK أو وكيل CGLIB.
النسيج: تجميع لإنشاء كائن مخلل. يمكن القيام بذلك في وقت الترجمة (على سبيل المثال باستخدام برنامج التحويل البرمجي الجانبي) أو في وقت التشغيل. الربيع ، مثل أطر عمل Java AOP النقية الأخرى ، يكمل النسيج في وقت التشغيل.
مكونات الربيع AOP
يسرد مخطط الفصل التالي مكونات AOP الرئيسية في الربيع
كيفية استخدام الربيع aop
يمكن استخدام الربيع AOP في ملفات التكوين أو طرق البرمجة.
يمكن تنفيذ التكوين من خلال ملف XML ، وهناك حوالي أربع طرق:
1. تكوين proxyfactorybean ، واضحة تعيين المستشارين ، والنصيحة ، والهدف ، إلخ.
2. تكوين AutoProxyCreator. وبهذه الطريقة ، لا يزال الحبة المحددة تستخدم كما كان من قبل ، ولكن ما تحصل عليه من الحاوية هو في الواقع كائن وكيل.
3. تكوين من خلال <aop: config>
4. تكوين من خلال <aOP: SideJ-Autoproxy> ، واستخدم التعليقات التوضيحية SideSJ لتحديد الإخطارات ونقاط الدخول
يمكنك أيضًا استخدام ProxyFactory مباشرة لاستخدام الربيع AOP برمجيًا. من خلال الطرق التي توفرها ProxyFactory ، يمكنك تعيين الكائنات المستهدفة والمستشارين والتكوينات الأخرى ذات الصلة ، وأخيراً الحصول على كائن الوكيل من خلال طريقة getProxy ().
أمثلة محددة للاستخدام يمكن أن تكون Google. تم حذفه هنا
جيل من كائن الوكيل الربيعي AOP
يوفر Spring طريقتين لإنشاء كائنات وكيل: JDKProxy و CGLIB. يتم تحديد الطريقة المحددة للتوليد بواسطة aopproxyfactory استنادًا إلى تكوين كائن advisedsupport. تتمثل السياسة الافتراضية في استخدام تقنية الوكيل الديناميكي JDK إذا كانت الفئة المستهدفة واجهة ، وإلا استخدم CGLIB لإنشاء الوكيل. دعونا ندرس كيف يستخدم Spring JDK لإنشاء كائنات وكيل. يتم وضع رمز التوليد المحدد في فئة JDKDynamicaopproxy ، ويتم إضافة الكود ذي الصلة مباشرة:
/** * <ol> * <li> احصل على الواجهة ليتم تنفيذها بواسطة فئة الوكيل. بالإضافة إلى التكوين في الكائن المشورة ، ستتم إضافة SpringProxy أيضًا ، نصح (ePaque = false) * <li> تحقق مما إذا كانت هناك واجهة تحدد المساواة أو hashcode في الواجهة التي تم الحصول عليها أعلاه * <li> classy. (logger.isdebugenabled ()) {logger.debug ("إنشاء الوكيل الديناميكي JDK: مصدر الهدف هو" +this.advised.getTargetSource ()) ؛ } class [] proxiedInterfaces = aopproxyutils.completeproxiedInterfaces (this.advised) ؛ findDefinedEqualsandhashCodemethods (ProxiedInterfaces) ؛ return proxy.newproxyinstance (classloader ، proxiedInterfaces ، this) ؛ } ثم هذا في الواقع واضح جدا. لقد كتبت التعليقات بوضوح ولن أكررها مرة أخرى.
السؤال التالي هو ، يتم إنشاء كائن الوكيل ، كيف يتم نسج سطح القطع؟
نحن نعلم أن InvOcketHandler هو جوهر الوكيل الديناميكي JDK ، وسيتم تفويض طريقة COLLES لكائنات الوكيل التي تم إنشاؤها إلى طريقة InvocationHandler.invoke (). من خلال توقيع jdkdynamicaopproxy ، يمكننا أن نرى أن هذه الفئة تقوم بالفعل بتنفيذ InvocationHandler. دعونا نلقي نظرة على كيفية نسج الربيع AOP في القسم من خلال تحليل طريقة Invoke () المنفذة في هذه الفئة.
PublicObject Invoke (وكيل الكائن ، طريقة الطريقة ، الكائن [] args) RisrowShrowable {methodInvocation invocation = null ؛ Object OldProxy = null ؛ Boolean setProxyContext = false ؛ TargetSource TargetSource = this.advised.targetSource ؛ فئة TargetClass = فارغة ؛ الهدف الهدف = فارغ ؛ جرب {// eqauls () طريقة ، الكائن الهدف لا ينفذ هذه الطريقة إذا (! this.equalsdiveed && aoputils.isefequalsmethod (method)) {return (args (args [0])؟ boolean.true: boolean.false) ؛ } // method hashcode () ، لا يقوم الكائن الهدف بتنفيذ هذه الطريقة إذا (! this.hashCodEdefined && aoputils.ishashcodemethod (method)) {return newInteger (hashcode ()) ؛ }. aoputils.invokejoinpointusingreflection (هذا. } الكائن Retval = null ؛ if (this.advised.exposeproxy) {// جعل الاحتجاج متاحًا. Oldproxy = aopContext.setCurrentProxy (proxy) ؛ setProxyContext = true ؛ } // احصل على هدف فئة الكائن الهدف = TargetSource.getTarget () ؛ if (target! = null) {targetClass = target.getClass () ؛ } // احصل على قائمة اعتراضية يمكن تطبيقها على سلسلة قائمة الطريقة هذه = this.advised.getInterceptorSandDynamicInterceadvice (الطريقة ، TargetClass) ؛ // إذا لم يكن هناك إشعار يمكن تطبيقه على هذه الطريقة (اعتراض) ، فإن طريقة استدعاء الانعكاس المباشر هذه. invoke (الهدف ، args) if (chain.isempty ()) {retval = aoputils.invokejoinpointusingingerflection (الهدف ، الطريقة ، args) ؛ } else {// إنشاء methodInvocation invocation = newReflectiveMethodInvocation (الوكيل ، الهدف ، الطريقة ، args ، targetclass ، سلسلة) ؛ retval = invocation.proceed () ؛ } // قيمة إرجاع التدليك إذا لزم الأمر. if (retval! = null && retval == target && method.getReturnType (). isInstance (proxy) &&! RawTargetAccess.class.isassignableFrom (method.getDeclaringclass ())) Notethat لا يمكننا المساعدة إذا كان الهدف يحدد // مرجع إلى نفسه كائن إرجاع. Retval = proxy ؛ } إرجاع Retval ؛ } أخيرًا {if (target! = null &&! targetSource.isStatic ()) {// يجب أن يكون قد جاء من targetSource. TargetSource.ReleAsetarget (Target) ؛ } if (setProxyContext) {// استعادة الوكيل القديم. AopContext.setCurrentProxy (Oldproxy) ؛ }}} يمكن وصف العملية الرئيسية لفترة وجيزة على أنها: الحصول على سلسلة الإخطار التي يمكن تطبيقها على هذه الطريقة (سلسلة اعتراضية). إذا كان هناك ، قم بتطبيق الإخطار وتنفيذ JoinPoint ؛ إذا لم يكن هناك ، فأعكس بشكل مباشر نقطة Joinpoint. المفتاح هنا هو كيفية الحصول على سلسلة الإخطار وكيفية تنفيذها. دعنا نحللها واحدة تلو الأخرى.
بادئ ذي بدء ، من الكود أعلاه ، يمكننا أن نرى أن سلسلة الإخطار يتم الحصول عليها من خلال طريقة المشورة. دعونا نلقي نظرة على تنفيذ هذه الطريقة:
القائمة العامة <Object> getInterceptorSAndDynamicInterceadvice (طريقة الطريقة ، Class TargetClass) {methodCacheKeyCachekey = جديد methodCacheKey (method) ؛ قائمة <Object> cached = this.methodcache.get (cachekey) ؛ if (cached == null) {cached = this.AdvisorChainFactory.getInterceptorSandDynamicInterceadvice (هذه ، الطريقة ، targetclass) ؛ this.methodcache.put (cachekey ، in cached) ؛ } returncached ؛ } يمكن أن نرى أن أعمال الاستحواذ الفعلية يتم في الواقع من قبل المستشارين. GetInterceptorsAndDynamicInterceadvice () طريقة ، وسيتم تخزين النتائج التي تم الحصول عليها.
دعنا نحلل تنفيذ هذه الطريقة أدناه:
/*** احصل على قائمة المستشارين من تكوين مثيل التكوين المقدم وتجتاز هؤلاء المستشارين. إذا كانت مقدمة ، فاحدد ما إذا كان يمكن تطبيق هذا المستشار على Class TargetClass المستهدف. إذا كان ذلك بمثابة pointCutAdvisor ، فحدد * ما إذا كان يمكن تطبيق هذا المستشار على طريقة طريقة الهدف. يتم تحويل المستشار الذي يلبي الشروط إلى قائمة اعتراضية من خلال المستشار. */ publiclist getInterceptorsAndDynamicInterceDceadvice (المشورة للتكوين ، methodMethod ، Class TargetClass) {// هذا أمر صعب ... يتعين علينا معالجة المقدمات أولاً ، // لكننا نحتاج إلى الحفاظ على الطلب في القائمة النهائية. قائمة InterceptorList = new ArrayList (config.getAdvisors (). الطول) ؛ // تحقق مما إذا كان مقدمة adductionAdvisor boolean hasintroductions = hasmatchingintroductions (config ، targetclass) ؛ // في الواقع ، يتم تسجيل سلسلة من المستشارين هنا لتحويل المستشار إلى methodInterceptor advisorAdregistry Recistry = GlobalAdvisorAdapterRegistry.getInstance () ؛ Advisor [] Advisors = config.getAdvisors () ؛ لـ (int i = 0 ؛ i <advisors.length ؛ i ++) {Advisor Advisor = Advisors [i] ؛ if (مستشار مثيل pointCutAdvisor) {// أضفه بشكل مشروط. PointCutAdvisor PointCutAdvisor = (pointCutAdvisor) مستشار ؛ if (config.isprefiltered () || pointCutAdvisor.getPointCut (). // تحقق مما إذا كان نقطة المستشار الحالي يمكن أن تتطابق مع الطريقة الحالية methodmatcher mm = pointCutAdvisor.getPointCut (). getMethodMatcher () ؛ إذا كانت (MethodMatchers.matches (MM ، الطريقة ، TargetClass ، hasintroductions))) {if (mm.isruntime ()) {// إنشاء مثيل جديد في طريقة getInterceptors () // ليست مشكلة نقوم عادة بتخزين Cache. لـ (intj = 0 ؛ j <interceptors.length ؛ j ++) {interceptorList.add (interceptoranddynamethodmatcher (اعتراضات [j] ، mm)) ؛ }} else {interceptorList.addall (arrays.aslist (interceptors)) ؛ }}}} آخر إذا (مستشار مثيل مقدمة adductionAdvisor) {introductionAdvisor ia = (IntroductionAdvisor) Advisor ؛ if (config.isprefiltered () || ia.getClassFilter (). Matches (targetClass)) {Interceptor [] interceptors = registry.getInterceptors (Advisor) ؛ InterceptorList.addall (Arrays.aslist (Interceptors)) ؛ }} else {interceptor [] interceptors = registry.getInterceptors (Advisor) ؛ InterceptorList.addall (Arrays.aslist (Interceptors)) ؛ }} إرجاع interceptorList ؛ } بعد تنفيذ هذه الطريقة ، يتم تحويل جميع المستشارين الذين تم تكوينهم في المشورة والتي يمكن تطبيقها على نقاط الاتصال أو يتم تحويل الفئات المستهدفة إلى مفهوم MethodInterce.
بعد ذلك ، دعونا نلقي نظرة على كيفية عمل سلسلة اعتراضية تم الحصول عليها.
if (chain.isempty ()) {retval = aOpUtils.invokeJoInPointusingReplection (الهدف ، الطريقة ، args) ؛ } else {// إنشاء methodInvocation invocation = newReflectiveMethodInvocation (الوكيل ، الهدف ، الطريقة ، args ، targetclass ، سلسلة) ؛ retval = invocation.proceed () ؛ } من هذا الرمز ، يمكننا أن نرى أنه إذا كانت سلسلة التقاطع التي تم الحصول عليها فارغة ، فسيتم استدعاء الطريقة المستهدفة بشكل مباشر. بخلاف ذلك ، سيتم إنشاء MethodInvocation ، وسيتم استدعاء طريقة العملية الخاصة به ، وسيتم تنفيذ سلسلة التقاطع. لنلقي نظرة على الرمز المحدد
الكائن العام متابع () يلقي رمي {// نبدأ بفهرس -1 والزيادة في وقت مبكر. if (this.currentInterceptorIndEx == this.InterceptorSandDynamicMethodMatchers.size ()- 1) {// إذا تم الانتهاء من تعاون ، تنفيذ إرجاع joinpoint invokejoinpoint () ؛ } اعتراض الكائنات processionAdvice = this.InterceptorSandDynamicMethodMatchers.get (++ this.currentInterceptorIndex) ؛ // إذا كنت ترغب في مطابقة JoinPoint ديناميكيًا إذا كان (InterceptorInterceptionAdvice مثيلًا لـ InterceptorandDynamicMethodMatcher) {// تقييم مطابقة الطريقة الديناميكية هنا: سيتم تقييم الجزء الثابت بالفعل // تم تقييمه للمطابقة. InterceptorandDynamicMethodMatcher DM = (interceptoranddynamichodmatcher) اعتراضية // المباراة الديناميكية: ما إذا كانت معلمات وقت التشغيل تلبي شروط المطابقة إذا (dm.MethodMatcher.matches (this.method ، this.targetClass ، this.arguments)) {// تنفيذ counterpor returndm.interceptor.invoke (هذا) ؛ } آخر {// عندما تفشل المطابقة الديناميكية ، تخطي intercetpor الحالي واتصل بإجراء إرجاع التقاطع التالي () ؛ }} آخر {// إنه اعتراض ، لذلك نحن فقط استدعاء: تم تقييم pointcutwill // بشكل ثابت قبل بناء هذا الكائن. // تنفيذ إرجاع intercetpor الحالي ((methodInterceptor) اعتراضية processionadvice) .invoke (هذا) ؛ }}الرمز بسيط نسبيًا ، لذلك لن أخوض في التفاصيل هنا.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.