大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 aop實現原理

aop實現原理

更新時間:2022-09-05 10:21:58 來源:動力節點 瀏覽918次

相信大家或多或少的了解過AOP,都知道它是面向切面編程,在網上搜索可以找到很多的解釋。這里我用一句話來總結:AOP是能夠讓我們在不影響原有功能的前提下,為軟件橫向擴展功能。那么橫向擴展怎么理解呢,我們在WEB項目開發中,通常都遵守三層原則,包括控制層(Controller)->業務層(Service)->數據層(dao),那么從這個結構下來的為縱向,它具體的某一層就是我們所說的橫向。我們的AOP就是可以作用于這某一個橫向模塊當中的所有方法。

講到動態代理就不得不說代理模式了,代理模式的定義:給某一個對象提供一個代理,并由代理對象控制對原對象的引用。代理模式包含如下角色:subject:抽象主題角色,是一個接口。該接口是對象和它的代理共用的接口; RealSubject:真實主題角色,是實現抽象主題接口的類; Proxy:代理角色,內部含有對真實對象RealSubject的引用,從而可以操作真實對象。代理對象提供與真實對象相同的接口,以便代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當于對真實對象進行封裝。如下圖所示:

那么代理又分為靜態代理和動態代理,這里寫兩個小的demo,動態代理采用的就是JDK代理。舉個例子就是現在一個班上的學生需要交作業,現在由班長代理交作業,那么班長就是代理,學生就是被代理的對象。

1.靜態代理

首先,我們創建一個Person接口。這個接口就是學生(被代理類),和班長(代理類)的公共接口,他們都有交作業的行為。這樣,學生交作業就可以讓班長來代理執行。

/**
 * Created by Mapei on 2018/11/7
 * 創建person接口
 */
public interface Person {
    //交作業
    void giveTask();
}

Student類實現Person接口,Student可以具體實施交作業這個行為。

/**
 * Created by Mapei on 2018/11/7
 */
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public void giveTask() {
        System.out.println(name + "交語文作業");
    }
}

StudentsProxy類,這個類也實現了Person接口,但是還另外持有一個學生類對象,那么他可以代理學生類對象執行交作業的行為。

/**
 * Created by Mapei on 2018/11/7
 * 學生代理類,也實現了Person接口,保存一個學生實體,這樣就可以代理學生產生行為
 */
public class StudentsProxy implements Person{
    //被代理的學生
    Student stu;
    public StudentsProxy(Person stu) {
        // 只代理學生對象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    //代理交作業,調用被代理學生的交作業的行為
    public void giveTask() {
        stu.giveTask();
    }
}

下面測試一下,看代理模式如何使用:

/**
 * Created by Mapei on 2018/11/7
 */
public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的學生林淺,他的作業上交有代理對象monitor完成
        Person linqian = new Student("林淺");
        //生成代理對象,并將林淺傳給代理對象
        Person monitor = new StudentsProxy(linqian);
        //班長代理交作業
        monitor.giveTask();
    }
}

運行結果:

這里并沒有直接通過林淺(被代理對象)來執行交作業的行為,而是通過班長(代理對象)來代理執行了。這就是代理模式。代理模式就是在訪問實際對象時引入一定程度的間接性,這里的間接性就是指不直接調用實際對象的方法,那么我們在代理過程中就可以加上一些其他用途。比如班長在幫林淺交作業的時候想告訴老師最近林淺的進步很大,就可以輕松的通過代理模式辦到。在代理類的交作業之前加入方法即可。這個優點就可以運用在spring中的AOP,我們能在一個切點之前執行一些操作,在一個切點之后執行一些操作,這個切點就是一個個方法。這些方法所在類肯定就是被代理了,在代理過程中切入了一些其他操作。

2.動態代理

動態代理和靜態代理的區別是,靜態代理的的代理類是我們自己定義好的,在程序運行之前就已經變異完成,但是動態代理的代理類是在程序運行時創建的。相比于靜態代理,動態代理的優勢在于可以很方便的對代理類的函數進行統一的處理,而不用修改每個代理類中的方法。比如我們想在每個代理方法之前都加一個處理方法,我們上面的例子中只有一個代理方法,如果還有很多的代理方法,就太麻煩了,我們來看下動態代理是怎么去實現的。

首先還是定義一個Person接口:

/**
 * Created by Mapei on 2018/11/7
 * 創建person接口
 */
public interface Person {
    //交作業
    void giveTask();
}

接下來是創建需要被代理的實際類,也就是學生類:

/**
 * Created by Mapei on 2018/11/7
 */
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public void giveTask() {
        System.out.println(name + "交語文作業");
    }
}

創建StuInvocationHandler類,實現InvocationHandler接口,這個類中持有一個被代理對象的實例target。InvocationHandler中有一個invoke方法,所有執行代理對象的方法都會被替換成執行invoke方法。

/**
 * Created by Mapei on 2018/11/7
 */
public class StuInvocationHandler<T> implements InvocationHandler {
    //invocationHandler持有的被代理對象
    T target;
    public StuInvocationHandler(T target) {
        this.target = target;
    }
    /**
     * proxy:代表動態代理對象
     * method:代表正在執行的方法
     * args:代表調用目標方法時傳入的實參
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執行" +method.getName() + "方法");
        Object result = method.invoke(target, args);
        return result;
    }
}

那么接下來我們就可以具體的創建代理對象了。

/**
 * Created by Mapei on 2018/11/7
 * 代理類
 */
public class ProxyTest {
    public static void main(String[] args) {
        //創建一個實例對象,這個對象是被代理的對象
        Person linqian = new Student("林淺");
        //創建一個與代理對象相關聯的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);
        //創建一個代理對象stuProxy來代理linqian,代理對象的每個執行方法都會替換執行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        //代理執行交作業的方法
        stuProxy.giveTask();
    }
}

我們執行代理測試類,首先我們創建了一個需要被代理的學生林淺,將林淺傳入stuHandler中,我們在創建代理對象stuProxy時,將stuHandler作為參數,那么所有執行代理對象的方法都會被替換成執行invoke方法,也就是說,最后執行的是StuInvocationHandler中的invoke方法。所以在看到下面的運行結果也就理所當然了。

那么到這里問題就來了,為什么代理對象執行的方法都會通過InvocationHandler中的invoke方法來執行,帶著這個問題,我們需要看一下動態代理的源碼,對他進行簡單的分析。

上面我們使用Proxy類的newProxyInstance方法創建了一個動態代理對象,看一下他的源碼:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
  }

然后,我們需要重點關注Class cl = getProxyClass0(loader, intfs)這句代碼,這里產生了代理類,這個類就是動態代理的關鍵,由于是動態生成的類文件,我們將這個類文件打印到文件中。

        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
        String path = "/Users/mapei/Desktop/okay/65707.class";
        try{
            FileOutputStream fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
            System.out.println("代理類class文件寫入成功");
        }catch (Exception e) {
            System.out.println("寫文件錯誤");
        }

對這個class文件進行反編譯,我們看看jdk為我們生成了什么樣的內容:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;  
  /**
  *注意這里是生成代理類的構造方法,方法參數為InvocationHandler類型,看到這,是不是就有點明白
  *為何代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
  *被代理對象的實例,就可以去調用真正的對象實例。
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }  
  //這個靜態塊本來是在最后的,我把它拿到前面來,方便描述
   static
  {
    try
    {
      //看看這兒靜態塊兒里面的住giveTask通過反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  } 
  /**
  * 
  *這里調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,并把m3傳了進去。
  *this.h.invoke(this, m3, null);我們可以對將InvocationHandler看做一個中介類,中介類持有一個被代理對象,在invoke方法中調用了被代理對象的相應方法。通過聚合方式持有被代理對象的引用,把外部對invoke的調用最終都轉為對被代理對象的調用。
  */
  public final void giveTask()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
}

在動力節點Java動態代理技術文檔中還有更多的知識等著大家去學習,感興趣的小伙伴可以了解一下。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 亚洲黄色录像 | 国产 在线 | 日韩 | 日日夜夜骑 | 亚洲精品亚洲九十七页 | 久久88 | 福利午夜国产网站在线不卡 | 天天射天天色天天干 | 一级毛片私人影院老司机 | 欧美视频一区二区三区 | 99视频在线观看视频一区 | 免费观看一级欧美大 | 久久动漫精品 | 欧美日韩亚洲成人 | 五月开心婷婷 | 狠狠色噜噜综合社区 | 国产视频一区二 | 又粗又大的机巴好爽视频视频 | 狼人香蕉香蕉在线视频播放 | 老司机深夜福利网站 | 香蕉在线观看999 | 欧美一级全部免费视频 | 日本视频中文字幕 | 国产日韩美国成人 | 不卡不卡 | 亚洲欧美视频一区二区三区 | 亚洲最新在线 | 热久久久 | 久久精品日本免费线 | 国产国拍亚洲精品福利 | 久久国产精品永久免费网站 | 精品国产一区二区三区免费 | 国产乱子伦手机在线 | 四虎影在永久地址在线观看 | xxxx性xx另类| 欧美激情精品久久久久久久九九九 | 美女精品久久久久久国产潘金莲 | 日韩精品中文字幕一区二区三区 | 免费欧洲毛片a级视频老妇女 | 久久精品视频一区 | 欧美一级录像 | 久久久久久午夜精品 |