面向切面編程-AOP的介紹

AOP簡介

  1. AOP(Aspect-Oriented Programming, 面向切面編程): 是一種新的方法論, 是對傳統 OOP(Object-Oriented Programming, 面向對象編程) 的補充.。
  2. AOP 的主要編程對象是切面(aspect), 而切面模塊化橫切關注點.。
  3. 在應用 AOP 編程時, 仍然需要定義通用的系統功能, 但可以明確的定義這個功能在哪裏, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模塊化到特殊的對象(切面)裏。

 

  AOP的優點:

    •  日誌記錄的代碼和真正的業務邏輯代碼進行代碼分離 
    •  通用的系統功能(日誌記錄、權限校驗)進行了高度的模塊化 
    •  業務邏輯的功能變的更簡潔,僅僅包含業務邏輯的代碼
    •  AOP可以將系統功能(日誌記錄)與業務邏輯功能攪和到一起執行

 

 


 

 

 

AOP 中的專業術語

 

  • 切面(aspect):橫切邏輯被模塊化的特殊對象。即它是一個類 :如LogAspect
  • 通知(advice):切面中必須完成的工作。即它是類中的一個方法:如writeLog()
  • 目標類(target):被通知增強的對象
  • 代理類(proxy):向目標類應用通知增強之後產生的對象
  • 切入點(pointcut):切面中通知執行的“地點”的定義
  • 連接點(JoinPoint): 與切入點匹配的執行點:如目標類中的所有方法getUserId()

 

 


 

 

 

AOP的兩種底層實現方式

  代理:

代理設計模式的原理: 使用一個代理對象將原始對象包裝起來, 然後用該代理對象取代原始對象. 任何對原始對象的調用都要通過代理對象. 代理對象決定是否以及何時將方法調用轉到原始對象上。

 

 

 

      •     靜態代理:

爲每一個目標對象創建一個代理實現類的方式可以認爲就是靜態代理。

靜態代理的實現很簡單,但是會造成代理類的快速膨脹,每一個目標類,都需要創建一個代理類

複製代碼
 1 //靜態代理
 2 public class StaticProxyUserService implements UserService {
 3     //原始對象
 4     private UserService userService;
 5     
 6     public StaticProxyUserService(UserService userService) {
 7         this.userService = userService;
 8     }
 9     @Override
10     public User getById(String userId) {
11         System.out.println("執行權限校驗,日誌記錄.......");
12         return userService.getById(userId);
13     }
14     @Override
15     public boolean add(User user) {
16         System.out.println("執行權限校驗,日誌記錄.......");
17         return userService.add(user);
18     }
19 
20     @Override
21     public boolean delete(String userId) {
22         System.out.println("執行權限校驗,日誌記錄.......");
23         return userService.delete(userId);
24     }
25     @Override
26     public boolean update(User user) {
27         System.out.println("執行權限校驗,日誌記錄.......");
28         return userService.update(user);
29     }
30 }
複製代碼

 

 

      •    動態代理:

 

爲了解決靜態代理的缺點,就產生了動態代理:在系統運行時,動態生成一個持有原始對象,並實現代理接口的Proxy,同時 “植入”通用邏輯(日誌、權限等)。

動態代理可以實現靜態代理相同的功能,唯一的區別這些Proxy的創建都是自動的並且在系統運行時生成的。這樣就不需要對每一個原始對象來創建一個代理了。

 

 JDK動態代理

 

JDK內置的Proxy動態代理可以在運行時動態生成字節碼,而沒必要針對每個類編寫代理類。中間主要使用到了一個接口InvocationHandler與Proxy.newProxyInstance靜態方法。

 使用內置的Proxy(JDK動態代理)實現動態代理有一個問題:被代理的類必須實現接口,未實現接口則沒辦法完成動態代理。

如果項目中有些類沒有實現接口,則不應該爲了實現動態代理而刻意去抽出一些沒有實例意義的接口,通過cglib可以解決該問題。

 

 

    1.  創建maven工程並解決jdk版本及web.xml問題

    2.  導入jar包

複製代碼
  <properties>
      <spring-version>4.2.4.RELEASE</spring-version>
  </properties>
  
   <dependencies>
       <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>${spring-version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-core</artifactId>
           <version>${spring-version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-beans</artifactId>
           <version>${spring-version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-expression</artifactId>
           <version>${spring-version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-web</artifactId>
           <version>${spring-version}</version>
       </dependency>
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>3.1.0</version>
           <scope>provided</scope>
       </dependency>    
       <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring-version}</version>
        <scope>test</scope>
    </dependency>    
    <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.8</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring-version}</version>
    </dependency>    
   </dependencies>
   <build>
          <plugins>
          <!-- 設置jdk的編譯版本 -->
            <plugin>  
                        <groupId>org.apache.maven.plugins</groupId>  
                        <artifactId>maven-compiler-plugin</artifactId>  
                        <version>3.1</version>  
                        <configuration>  
                            <source>1.8</source>  
                            <target>1.8</target> 
                            <encoding>utf-8</encoding> 
                        </configuration>  
                    </plugin>  
        </plugins>
    </build>            
複製代碼

 

    3.  編寫切面類(封裝增強邏輯)

複製代碼
1 //切面:定義了增強的業務邏輯(權限驗證)
2 public class SecurityAspect {
3     //權限校驗的系統邏輯
4     public void checkPrivilege(){
5         System.out.println("我是權限校驗的方法,我需要在方法執行前進行執行");
6     }
7 }
複製代碼

 

    4.  創建代理對象

複製代碼
 1 public class ProxyFactory implements InvocationHandler{
 2     //目標類
 3     private Object target;
 4         
 5     //傳遞目標對象
 6     public ProxyFactory(Object target) {
 7         super();
 8         this.target = target;
 9     }
10 
11     public Object getProxy(){
12         /**
13          * loader:類加載器
14          * interfaces:目標實現類接口(jdk動態代理必須有接口)
15          * h:實現了InvocationHandle接口的類
16          */
17         return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
18                 target.getClass().getInterfaces(), this);
19     }
20 
21     @Override
22     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
23         //添加校驗權限的邏輯
24         SecurityAspect securityAspect = new SecurityAspect();        
25         //添加檢驗權限
26         securityAspect.checkPrivilege();        
27         //反射調用業務邏輯方法(目標類,參數)
28         Object result = method.invoke(target, args);
29         return result;
30     }
31 }
複製代碼

 

    5.  測試

複製代碼
1 public static void main(String[] args) {
2         //測試動態代理的執行
3         UserService target = new UserServiceImpl();        
4         //產生代理對象
5         UserService proxy = (UserService) new ProxyFactory(target).getProxy();        
6         //調用代理對象的業務方法
7         proxy.add();
8     }
複製代碼

 

 

 CGLIB動態代理

 

   CGLIB(Code Generation Library)是一個開源項目,是一個強大的,高性能,高質量的Code生成類庫,它可以在運行期擴展Java類與實現Java接口,通俗說cglib可以在運行時動態生成字節碼。

   使用cglib完成動態代理,大概的原理是:cglib繼承被代理的類(UserServiceImpl),重寫方法,織入通知,動態生成字節碼並運行,因爲是繼承所以final類是沒有辦法動態代理的。

 

 

      1.  定義目標類(不需要實現接口)

複製代碼
 1 /**
 2  * cglib的目標類
 3  * 沒有實現接口,只是一個業務類
 4  */
 5 public class UserServiceCglib {
 6     //切入點
 7     //業務邏輯方法
 8     public void add(){
 9         System.out.println("cglib的add方法被調用...");
10     }
11 }
複製代碼

    

      2.  定義切面類(增強邏輯類)

 

複製代碼
 1 /**
 2  * 增強邏輯類:日誌記錄切面
 3  */
 4 public class LogAspect {
 5     //通知
 6     //增強的業務邏輯
 7     public void log(){
 8         System.out.println("日誌記錄... ...");
 9     }
10 }
複製代碼

 

 

      3.  定義cglib動態代理生成器

 

複製代碼
 1 /**
 2  * cglib動態代理類生成器
 3  */
 4 public class CglibProxyFactory implements MethodInterceptor{
 5     //目標對象
 6     private Object target;
 7 
 8     //有參構造器
 9     public CglibProxyFactory(Object target) {
10         super();
11         this.target = target;
12     }
13         
14     //獲取代理類的方法
15     public Object getProxy(){
16         //調用cglib產生代理對象
17         Enhancer enhancer = new Enhancer();        
18         //設置父類的類型
19         enhancer.setSuperclass(target.getClass());        
20         //設置回調方法
21         enhancer.setCallback(this);
22         
23         //產生代理對象
24         Object proxy = enhancer.create();    
25         return proxy;
26     }
27 
28     //攔截業務方法的執行
29     @Override
30     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
31         //添加增強邏輯
32         //添加日誌
33         LogAspect logAspect = new LogAspect();
34         logAspect.log();
35         
36         //執行業務邏輯方法
37         Object result = methodProxy.invokeSuper(o, args);    
38         return result;
39     }    
40 }
複製代碼

 

      4.  測試

複製代碼
1 public static void main(String[] args) {
2         //創建目標對象
3         UserServiceCglib target = new UserServiceCglib();        
4         //獲取目標對象的代理對象
5         UserServiceCglib proxy = (UserServiceCglib) new CglibProxyFactory(target).getProxy();        
6         proxy.add();
7     }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章