自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

AOP面向切面編程

開(kāi)發(fā) 后端
AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。它是一種新的方法論,它是對(duì)傳統(tǒng)OOP編程的一種補(bǔ)充。

AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。它是一種新的方法論,它是對(duì)傳統(tǒng)OOP編程的一種補(bǔ)充。

OOP是關(guān)注將需求功能劃分為不同的并且相對(duì)獨(dú)立,封裝良好的類,并讓它們有著屬于自己的行為,依靠繼承和多態(tài)等來(lái)定義彼此的關(guān)系;AOP是希望能夠?qū)⑼ㄓ眯枨蠊δ軓牟幌嚓P(guān)的類當(dāng)中分離出來(lái),能夠使得很多類共享一個(gè)行為,一旦發(fā)生變化,不必修改很多類,而只需要修改這個(gè)行為即可。

AOP是使用切面(aspect)將橫切關(guān)注點(diǎn)模塊化,OOP是使用類將狀態(tài)和行為模塊化。在OOP的世界中,程序都是通過(guò)類和接口組織的,使用它們實(shí)現(xiàn)程序的核心業(yè)務(wù)邏輯是十分合適。但是對(duì)于實(shí)現(xiàn)橫切關(guān)注點(diǎn)(跨越應(yīng)用程序多個(gè)模塊的功能需求)則十分吃力,比如日志記錄,驗(yàn)證。

  1. /*計(jì)算器接口*/ 
  2. public interface Calculator  
  3. {  
  4.     public double add(double num1, double num2) throws Exception;  
  5.     public double sub(double num1, double num2) throws Exception;  
  6.     public double div(double num1, double num2) throws Exception;  
  7.     public double mul(double num1, double num2) throws Exception;  
  1. /*計(jì)算器接口的實(shí)現(xiàn)類*/ 
  2. public class ArithmeticCalculator implements Calculator  
  3. {  
  4.     @Override 
  5.     public double add(double num1, double num2)  
  6.     {  
  7.         double result = num1 + num2;  
  8.         return result;  
  9.     }  
  10.  
  11.     @Override 
  12.     public double sub(double num1, double num2)  
  13.     {  
  14.         double result = num1 - num2;  
  15.         return result;  
  16.     }  
  17.  
  18.     /*示意代碼 暫時(shí)不考慮除數(shù)0的情況*/ 
  19.     @Override 
  20.     public double div(double num1, double num2)  
  21.     {  
  22.         double result = num1 / num2;  
  23.         return result;  
  24.     }  
  25.  
  26.     @Override 
  27.     public double mul(double num1, double num2)  
  28.     {  
  29.         double result = num1 * num2;  
  30.         return result;  
  31.     }  

大多數(shù)應(yīng)用程序都有一個(gè)通用的需求,即在程序運(yùn)行期間追蹤正在發(fā)生的活動(dòng)。為了給計(jì)算機(jī)添加日志功能,ArithmeticCalculator類改變?nèi)缦拢?/p>

  1. /*計(jì)算器接口的實(shí)現(xiàn)類,添加記錄日志功能*/ 
  2. public class ArithmeticCalculator implements Calculator  
  3. {  
  4.     @Override 
  5.     public double add(double num1, double num2)  
  6.     {  
  7.         System.out.println("the method [add()]"+"begin with args ("+num1+","+num2+")");  
  8.         double result = num1 + num2;  
  9.         System.out.println("the method [add()]"+"end with result ("+result+")");  
  10.           
  11.         return result;  
  12.     }  
  13.  
  14.     @Override 
  15.     public double sub(double num1, double num2)  
  16.     {  
  17.         System.out.println("the method [sub()]"+"begin with args ("+num1+","+num2+")");  
  18.         double result = num1 - num2;  
  19.         System.out.println("the method [sub()]"+"end with result ("+result+")");  
  20.           
  21.         return result;  
  22.     }  
  23.  
  24.     /*示意代碼 暫時(shí)不考慮除數(shù)0的情況*/ 
  25.     @Override 
  26.     public double div(double num1, double num2)  
  27.     {  
  28.         System.out.println("the method [div()]"+"begin with args ("+num1+","+num2+")");  
  29.         double result = num1 / num2;  
  30.         System.out.println("the method [div()]"+"end with result ("+result+")");  
  31.           
  32.         return result;  
  33.     }  
  34.  
  35.     @Override 
  36.     public double mul(double num1, double num2)  
  37.     {  
  38.         System.out.println("the method [mul()]"+"begin with args ("+num1+","+num2+")");  
  39.         double result = num1 * num2;  
  40.         System.out.println("the method [mul()]"+"end with result ("+result+")");  
  41.           
  42.         return result;  
  43.     }  

若ArithmeticCalculator規(guī)定只能計(jì)算正數(shù)時(shí),又需要添加參數(shù)驗(yàn)證方法:

  1. /*計(jì)算器接口的實(shí)現(xiàn)類,添加記錄日志功能*/ 
  2. public class ArithmeticCalculator implements Calculator  
  3. {  
  4.     @Override 
  5.     public double add(double num1, double num2) throws Exception  
  6.     {  
  7.         this.argsValidatior(num1);  
  8.         this.argsValidatior(num2);  
  9.           
  10.          /*同上*/ 
  11.     }  
  12.  
  13.     @Override 
  14.     public double sub(double num1, double num2) throws Exception  
  15.     {  
  16.         this.argsValidatior(num1);  
  17.         this.argsValidatior(num2);  
  18.           
  19.          /*同上*/ 
  20.     }  
  21.  
  22.     /*示意代碼 暫時(shí)不考慮除數(shù)0的情況*/ 
  23.     @Override 
  24.     public double div(double num1, double num2) throws Exception  
  25.     {  
  26.         this.argsValidatior(num1);  
  27.         this.argsValidatior(num2);  
  28.           
  29.          /*同上*/ 
  30.     }  
  31.  
  32.     @Override 
  33.     public double mul(double num1, double num2) throws Exception  
  34.     {  
  35.         this.argsValidatior(num1);  
  36.         this.argsValidatior(num2);  
  37.           
  38.         /*同上*/ 
  39.     }  
  40.       
  41.     private void argsValidatior(double arg)throws Exception  
  42.     {  
  43.         if(arg < 0)  
  44.             throw new Exception("參數(shù)不能為負(fù)數(shù)");  
  45.     }  

上面的程序一個(gè)很直觀的特點(diǎn)就是,好多重復(fù)的代碼,并且當(dāng)加入越來(lái)越多的非業(yè)務(wù)需求(例如日志記錄和參數(shù)驗(yàn)證),原有的計(jì)算器方法變得膨脹冗長(zhǎng)。這里有一件非常痛苦的事情,無(wú)法使用原有的編程方式將他們模塊化,從核心業(yè)務(wù)中提取出來(lái)。例如日志記錄和參數(shù)驗(yàn)證,AOP里將他們稱為橫切關(guān)注點(diǎn)(crosscutting concern),它們屬于系統(tǒng)范圍的需求通常需要跨越多個(gè)模塊。

在使用傳統(tǒng)的面向?qū)ο蟮木幊谭绞綗o(wú)法理想化的模塊化橫切關(guān)注點(diǎn),程序員不能不做的就是將這些橫切關(guān)注點(diǎn)放置在每一個(gè)模塊里與核心邏輯交織在一起,這將會(huì)導(dǎo)致橫切關(guān)注點(diǎn)在每一個(gè)模塊里到處存在。使用非模塊化的手段實(shí)現(xiàn)橫切關(guān)注將會(huì)導(dǎo)致,代碼混亂,代碼分散,代碼重復(fù)。你想想看如果日志記錄需要換一種顯示方式,那你要改多少代碼,一旦漏掉一處(概率很高),將會(huì)導(dǎo)致日志記錄不一致。這樣的代碼很維護(hù)。種種原因表明,模塊只需要關(guān)注自己原本的功能需求,需要一種方式來(lái)將橫切關(guān)注點(diǎn)沖模塊中提取出來(lái)。

忍無(wú)可忍的大牛們提出了AOP,它是一個(gè)概念,一個(gè)規(guī)范,本身并沒(méi)有設(shè)定具體語(yǔ)言的實(shí)現(xiàn),也正是這個(gè)特性讓它變的非常流行,現(xiàn)在已經(jīng)有許多開(kāi)源的AOP實(shí)現(xiàn)框架了。本次不是介紹這些框架的,我們將不使用這些框架,而是使用底層編碼的方式實(shí)現(xiàn)最基本的AOP解決上面例子出現(xiàn)的問(wèn)題。AOP實(shí)際是GoF設(shè)計(jì)模式的延續(xù),設(shè)計(jì)模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說(shuō)也是這種目標(biāo)的一種實(shí)現(xiàn)。AOP可以使用"代理模式"來(lái)實(shí)現(xiàn)。

代理模式的原理是使用一個(gè)代理將對(duì)象包裝起來(lái),然后用該代理對(duì)象取代原始的對(duì)象,任何對(duì)原始對(duì)象的調(diào)用首先要經(jīng)過(guò)代理。代理對(duì)象負(fù)責(zé)決定是否以及何時(shí)將方法調(diào)用信息轉(zhuǎn)發(fā)到原始對(duì)象上。與此同時(shí),圍繞著每個(gè)方法的調(diào)用,代理對(duì)象也可以執(zhí)行一些額外的工作??梢钥闯龃砟J椒浅_m合實(shí)現(xiàn)橫切關(guān)注點(diǎn)。

由于本人只了解Java,所以姑且認(rèn)為代理模式有兩種實(shí)現(xiàn)方式,一種是靜態(tài)代理、另一種是動(dòng)態(tài)代理。他們的區(qū)別在于編譯時(shí)知不知道代理的對(duì)象是誰(shuí)。在模塊比較多的系統(tǒng)中,靜態(tài)代理是不合適也非常低效的,因?yàn)殪o態(tài)代理需要專門為每一個(gè)接口設(shè)計(jì)一個(gè)代理類,系統(tǒng)比較大成百上千的接口是很正常的,靜態(tài)代理模式太消耗人力了。動(dòng)態(tài)代理是JDK所支持的代理模式,它可以非常好的實(shí)現(xiàn)橫切關(guān)注點(diǎn)。

  1. /*使用動(dòng)態(tài)代理需要實(shí)現(xiàn)InvocationHandler接口*/ 
  2. public class ArithmeticCalculatorInvocationHandler implements InvocationHandler  
  3. {  
  4.     /*要代理的對(duì)象,動(dòng)態(tài)代理只有在運(yùn)行時(shí)才知道代理誰(shuí),所以定義為Object類型,可以代理任意對(duì)象*/ 
  5.     private Object target = null;  
  6.       
  7.     /*通過(guò)構(gòu)造函數(shù)傳入原對(duì)象*/ 
  8.     public ArithmeticCalculatorInvocationHandler(Object target)  
  9.     {  
  10.         this.target = target;  
  11.     }  
  12.  
  13.     /*InvocationHandler接口的方法,proxy表示代理,method表示原對(duì)象被調(diào)用的方法,args表示方法的參數(shù)*/ 
  14.     @Override 
  15.     public Object invoke(Object proxy, Method method, Object[] args)  
  16.             throws Throwable  
  17.     {  
  18.         /*原對(duì)象方法調(diào)用前處理日志信息*/ 
  19.         System.out.println("the method ["+method.getName()+"]"+"begin with args ("+Arrays.toString(args)+")");  
  20.           
  21.         Object result = method.invoke(this.target, args);  
  22.           
  23.         /*原對(duì)象方法調(diào)用后處理日志信息*/ 
  24.         System.out.println("the method ["+method.getName()+"]"+"end with result ("+result+")");  
  25.           
  26.         return result;  
  27.     }  
  28.       
  29.     /*獲取代理類*/ 
  30.     public Object getProxy()  
  31.     {  
  32.         return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.getClass().getInterfaces(), this);  
  33.     }  

場(chǎng)景類調(diào)用:

  1. public class Client  
  2. {  
  3.     public static void main(String[] args) throws Exception  
  4.     {  
  5.         /*獲得代理*/ 
  6.         Calculator arithmeticCalculatorProxy = (Calculator)new ArithmeticCalculatorInvocationHandler(  
  7.                  new ArithmeticCalculator()).getProxy();  
  8.  
  9.         /*調(diào)用add方法*/ 
  10.         arithmeticCalculatorProxy.add(1010);  
  11.     }  

控制臺(tái)的輸出:

  1. the method [add]begin with args ([10.010.0])  
  2. the method [add]end with result (20.0

可以看到使用動(dòng)態(tài)代理實(shí)現(xiàn)了橫切關(guān)注點(diǎn)。

若需要添加參數(shù)驗(yàn)證功能,只需要再創(chuàng)建一個(gè)參數(shù)驗(yàn)證代理即可:

  1. public class ArithmeticCalculatorArgsInvocationHandler implements 
  2.         InvocationHandler  
  3. {  
  4.     /*要代理的對(duì)象,動(dòng)態(tài)代理只有在運(yùn)行時(shí)才知道代理誰(shuí),所以定義為Object類型,可以代理任意對(duì)象*/ 
  5.     private Object target = null;  
  6.       
  7.     /*通過(guò)構(gòu)造函數(shù)傳入原對(duì)象*/ 
  8.     public ArithmeticCalculatorArgsInvocationHandler(Object target)  
  9.     {  
  10.         this.target = target;  
  11.     }  
  12.  
  13.     /*InvocationHandler接口的方法,proxy表示代理,method表示原對(duì)象被調(diào)用的方法,args表示方法的參數(shù)*/ 
  14.     @Override 
  15.     public Object invoke(Object proxy, Method method, Object[] args)  
  16.             throws Throwable  
  17.     {  
  18.         System.out.println("begin valid method ["+method.getName()+"] with args "+Arrays.toString(args));  
  19.           
  20.         for(Object arg : args)  
  21.         {  
  22.             this.argValidtor((Double)arg);  
  23.         }  
  24.           
  25.         Object result = method.invoke(this.target, args);  
  26.           
  27.         return result;  
  28.     }  
  29.       
  30.     /*獲取代理類*/ 
  31.     public Object getProxy()  
  32.     {  
  33.         return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);  
  34.     }  
  35.       
  36.     private void argValidtor(double arg) throws Exception  
  37.     {  
  38.         if(arg < 0)  
  39.             throw new Exception("參數(shù)不能為負(fù)數(shù)!");  
  40.     }  

場(chǎng)景類調(diào)用:

  1. public class Client  
  2. {  
  3.     public static void main(String[] args) throws Exception  
  4.     {  
  5.         /*獲得代理*/ 
  6.         Calculator arithmeticCalculatorProxy = (Calculator)new ArithmeticCalculatorInvocationHandler(  
  7.                  new ArithmeticCalculator()).getProxy();  
  8.           
  9.         Calculator argValidatorProxy = (Calculator)new ArithmeticCalculatorArgsInvocationHandler(arithmeticCalculatorProxy).getProxy();  
  10.  
  11.         /*調(diào)用add方法*/ 
  12.         argValidatorProxy.add(1010);  
  13.     }  

 控制臺(tái)輸出:

  1. begin valid method [add] with args [10.010.0]  
  2. the method [add]begin with args ([10.010.0])  
  3. the method [add]end with result (20.0

輸入一個(gè)負(fù)數(shù)數(shù)據(jù):

  1. public class Client  
  2. {  
  3.     public static void main(String[] args) throws Exception  
  4.     {  
  5.         /*獲得代理*/ 
  6.         Calculator arithmeticCalculatorProxy = (Calculator)new ArithmeticCalculatorInvocationHandler(  
  7.                  new ArithmeticCalculator()).getProxy();  
  8.           
  9.         Calculator argValidatorProxy = (Calculator)new ArithmeticCalculatorArgsInvocationHandler(arithmeticCalculatorProxy).getProxy();  
  10.  
  11.         /*調(diào)用add方法*/ 
  12.         argValidatorProxy.add(-1010);  
  13.     }  

控制臺(tái)輸出:

  1. begin valid method [add] with args [-10.010.0]  
  2. Exception in thread "main" java.lang.Exception: 參數(shù)不能為負(fù)數(shù)!  
  3.     at com.beliefbetrayal.aop.ArithmeticCalculatorArgsInvocationHandler.argValidtor(ArithmeticCalculatorArgsInvocationHandler.java:46)  
  4.     at com.beliefbetrayal.aop.ArithmeticCalculatorArgsInvocationHandler.invoke(ArithmeticCalculatorArgsInvocationHandler.java:29)  
  5.     at $Proxy0.add(Unknown Source)  
  6.     at com.beliefbetrayal.aop.Client.main(Client.java:14

 


不知道你有沒(méi)有使用過(guò)Struts2,這個(gè)結(jié)構(gòu)和Struts2的攔截器非常相似,一個(gè)個(gè)Action對(duì)象好比我們的原對(duì)象業(yè)務(wù)核心,一個(gè)個(gè)攔截器好比是這里的代理,通用的功能實(shí)現(xiàn)成攔截器,讓Action可以共用,Struts2的攔截器也是AOP的優(yōu)秀實(shí)現(xiàn)。

原文鏈接:http://www.cnblogs.com/beliefbetrayal/archive/2012/02/03/2337522.html

【編輯推薦】

  1. Spring事務(wù)配置的五種方式
  2. Spring聲明性事務(wù)常見(jiàn)問(wèn)題分析
  3. Java中Class對(duì)象詳解
  4. Java API設(shè)計(jì)清單
  5. Java遠(yuǎn)程方法調(diào)用RMI
責(zé)任編輯:林師授 來(lái)源: 信仰や欺騙的博客
相關(guān)推薦

2009-08-24 09:46:40

面向切面編程AOP

2013-09-17 10:37:03

AOPAOP教程理解AOP

2023-11-07 16:00:25

面向切面編程開(kāi)發(fā)

2023-10-20 09:32:25

Java技術(shù)

2011-04-26 09:33:04

SpringAOP

2024-05-21 09:55:43

AspectOrientedAOP

2023-11-30 08:00:54

面向?qū)ο?/a>面向切面

2024-04-10 08:59:39

SpringAOP業(yè)務(wù)

2010-04-26 08:53:06

面向方面編程.NET

2009-06-22 11:27:59

反向控制原理面向切面編程Spring

2009-06-22 15:10:00

java 編程AOP

2021-10-27 07:15:37

SpringAOP編程(

2024-06-11 00:04:00

對(duì)象AdvisorAdvice

2015-09-07 09:13:31

ios教學(xué)

2013-07-30 09:42:41

實(shí)現(xiàn)編程接口編程對(duì)象編程

2021-07-14 14:27:01

AndroidAOPhugo

2019-11-29 16:21:22

Spring框架集成

2015-10-09 13:54:14

切面編程錯(cuò)誤處理機(jī)制

2025-02-17 09:32:18

2022-07-30 23:41:53

面向過(guò)程面向?qū)ο?/a>面向協(xié)議編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)