Spring AOP底層的那些事

問題1.什麼是AOP?
AOP:面向切面編程,聽這名字完全不明白他是幹什麼用的,我們知道OOP是面向對象編程,請看下面這個案例。


public interface StudentDAO {
    void save();
    void delete();
    void update();
    void select();
}


package com.ibuyi.free.Case_three;

public class StudentDAOImpl implements StudentDAO{
    @Override
    public void save() {
        System.out.println("學生保存方法....");
    }

    @Override
    public void delete() {
        System.out.println("學生刪除方法....");
    }

    @Override
    public void update() {
        System.out.println("學生修改方法....");
    }

    @Override
    public void select() {
        System.out.println("學生查詢方法....");
    }
}

package com.ibuyi.free.Case_three;

public class Person implements StudentDAO {

    @Override
    public void save() {
        System.out.println("普通人的保存");
    }

    @Override
    public void delete() {
        System.out.println("普通人的刪除");
    }

    @Override
    public void update() {
        System.out.println("普通人的修改");
    }

    @Override
    public void select() {
        System.out.println("普通人的查找");
    }
}

如果我們想要在save()方法執行之前,想要檢查一下執行該方法用戶是否具有權限,該怎麼做呢?
第一,我們可以在該類中直接寫一個權限檢驗的方法,然後再save()執行之前進行調用。
第二,我們可以寫一個頂層的父類,在父類中寫上權限檢驗的方法,這樣子類繼承父類以後就可以直接調用該方法了。

這兩個方法有幾個問題:試想一下,如果我們的項目有成千上百個方法在執行前需要檢驗權限,那麼我們不是要在每個方法執行前添加檢驗權限的方法?這是第一種方法的缺點,第二個方法雖然用繼承的方式減去一些不必要的重複代碼,但是我們依舊要在方法之前前調用。

這時候,就有厲害的人物提出了AOP,他是OOP的一個補充,那麼AOP到底能幹什麼呢?

問題2.AOP的作用?
AOP的底層原理是動態代理,請看下面代碼

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyProxy implements InvocationHandler {
    private StudentDAO studentDAO;
    public MyProxy(StudentDAO studentDAO){
        this.studentDAO=studentDAO;
    }

    public Object CreateProxy(){
      Object proxy= Proxy.newProxyInstance(studentDAO.getClass().getClassLoader(),studentDAO.getClass().getInterfaces(),this);
      return proxy;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("save".equals(method.getName())){
            //如果是保存方法
            System.out.println("保存之前要校驗權限");
            //執行完檢驗以後,繼續回到正常的邏輯
            return method.invoke(studentDAO,args);
        }

        return method.invoke(studentDAO,args);
    }

}

該類實現了InvocationHanlder,並重寫了Invoke的方法,該類的構造方法中,需要傳入一個對象,就是需要創建代理的對象,誰需要創建代理就傳入誰,CreateProxy中調用了JDK提供的創建動態代理的方法,invoke方法中,我們進行判斷,如果代理的方法名稱是save,我們就要執行權限校驗,然後返回正常的執行邏輯。

這樣做有什麼好處呢?

    @Test
    public void demo1(){
        //傳統方法
        StudentDAO studentdao=new StudentDAOImpl();
        StudentDAO proxy= (StudentDAO) new MyProxy(studentdao).CreateProxy();
        proxy.save();
        proxy.delete();
        proxy.select();
        proxy.update();


    }

    @Test
    public void demo2(){
        //這裏我們的普通人在執行保存之前也需要進行校驗,需要添加任何代碼嘛?
        StudentDAO studentdao=new Person();
        StudentDAO proxy= (StudentDAO)new MyProxy(studentdao).CreateProxy();
        proxy.save();
        proxy.delete();
        proxy.select();
        proxy.update();

    }

執行結果如下:
在這裏插入圖片描述
在這裏插入圖片描述

我們只是添加了一個代理,所有實現了StudentDAO接口的類,在執行save方法之前可以使用代理類進行權限檢驗。

這裏不得不提一下,對於不使用接口的業務類,JDK無法創建動態代理,那如果我們想要代理沒有實現接口的類該怎麼做呢?這就要看CGlib了!CGlib採用非常底層的字節碼技術,可以爲一個創建子類,解決無接口代理的問題,詳細使用請看下面

package com.ibuyi.free.Case_four;

public class Product {

    public void save() {
        System.out.println("商品的保存");
    }


    public void delete() {
        System.out.println("商品的刪除");
    }


    public void update() {
        System.out.println("商品的修改");
    }


    public void select() {
        System.out.println("商品的查找");
    }
}


public class MyCGProxy implements MethodInterceptor {
    private Product product;
    public MyCGProxy(Product product){
        this.product=product;
    }

    public Object createProxy(){
        //1.創建核心類
        Enhancer enhancer=new Enhancer();
        //2.設置
        enhancer.setSuperclass(product.getClass());
        //3.設置回調
        enhancer.setCallback(this);
        //4.創建代理
        Object proxy=enhancer.create();
        return proxy;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       if(method.getName().equals("save")){
           System.out.println("商品保存前需要校驗權限");
           return methodProxy.invokeSuper(o,objects);
       }
        return methodProxy.invokeSuper(o,objects);
    }
}

  @Test
    public void demo1(){
        Product product=new Product();
        MyCGProxy myCGProxy=new MyCGProxy(product);
        Product proxy= (Product) myCGProxy.createProxy();
        proxy.save();
        proxy.delete();
        proxy.select();
        proxy.update();
    }

就這樣,就算沒有接口的業務類也能實現代理!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章