1. 程式人生 > >Java動態代理學習【Spring AOP基礎之一】

Java動態代理學習【Spring AOP基礎之一】

tor -1 我們 null exception 文件 cat static 一個

  Spring AOP使用的其中一個底層技術就是Java的動態代理技術。Java的動態代理技術主要圍繞兩個類進行的 

  java.lang.reflect.InvocationHandler
  java.lang.reflect.Proxy

  首先從代碼層面說明Java動態代理是如何實現的,

  業務邏輯接口:

  

/**
 * 創建一個人的接口,其中有一個吃的方法
 */
public interface Person {
    public void eat();
}

  創建一個實現該業務接口的類:

/**
 * 人接口的實現,實現吃飯的方法
 */
public class PersonImpl implements Person {
    @Override
    public void eat() {
        System.out.println("吃主食、吃菜、喝湯...");
    }
}

  此時,如果正常情況如果想要調用Person這個接口,直接new它的實現類然後調用eat方法即可,但是如果想要實現一個Person的代理,並且在eat方法前後進行一些工作就需要使用Proxy和InvovationHandler;

  InvocationHandler接口的實現類,(代理類中額外邏輯的實現就需要在這裏進行)

/**
 * 代理類的額外邏輯實現類
 */
public class InvocationHandlerImpl implements InvocationHandler {
    Person person;
    public InvocationHandlerImpl(Person person) {
        this.person = person;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equalsIgnoreCase("eat")) {
            System.out.println("洗手...");
            method.invoke(person, args);
            System.out.println("刷牙");
        }
        return person;
    }
}

  

  接下來就是使用Proxy生成代理類,Proxy需要業務邏輯接口,代理類額外實現邏輯InvocationHandler實現類

/**
 * 代理類產生並且測試
 */
public class ProxyGenerate {
    @Test
    public void proxyTest() {
        Person person = new PersonImpl();
        InvocationHandler invocationHandler = new InvocationHandlerImpl(person);
        Person personProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, invocationHandler);
        personProxy.eat();
    }
    

}

  

  輸出內容如下:

技術分享

  代理類已經成功的完成代理。

  AOP的功能就是在一個方法前、後、異常拋出等地方添加邏輯,與上面的過程是一樣的,所以這個技術就被用來實現Spring AOP的一個技術,但是這個只針對接口實現,如果想要給一個類添加AOP的邏輯,這個Proxy動態代理的技術暫時無法適用,Spring AOP適用了CGLIB,支持類的動態代理,但是不屬於這一次的討論範疇。

  知道怎麽用了之後是不是對Proxy這個感覺很神奇,想知道是如何實現的呢,可以深入了解一下newProxyInstance這個方法:

  技術分享

  找到上述內容,可以看到這個是動態生成代理類字節數組的方法,所以我們可以通過這個方法了解到動態生成的代理類的結構:

/**
 * 代理類產生並且測試
 */
public class ProxyGenerate {
    @Test
    public void proxyWatch() {
        byte[] classByte = ProxyGenerator.generateProxyClass("PersonDynamicProxy", new Class[]{Person.class});
        try {
            FileOutputStream var1 = new FileOutputStream("PersonDynamicProxy" + ".class");
            var1.write(classByte);
            var1.close();
        } catch (IOException var2) {
            throw new InternalError("I/O exception saving generated file: " + var2);
        }
    }
}

  執行上述代碼會生成動態代理對象的.class文件

技術分享

  反編譯(jd-gui,idea)之後就可以看到動態代理類的結構:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.smart.beanfactory.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class PersonDynamicProxy extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m2;

    public PersonDynamicProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void eat() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.smart.beanfactory.Person").getMethod("eat", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

  可以看出來,動態代理類首先通過反射的技術,將源接口中的方法均設置為其靜態屬性(Method),然後針對每個方法進行重寫。

  重寫的邏輯是使用invocationHandler接口的invoke方法實現,每個invoke方法在綁定相應的反射出來的靜態屬性即可。

  舉例,比如上面Person接口的eat被反射未m3的靜態屬性中,重寫eat()方法是調用方法為invoke(this, m3, args),其中invoke中會有額外添加的一些邏輯,

Java動態代理學習【Spring AOP基礎之一】