4.java基礎

反射

是在運行狀態中,對於任意的一個類,都能夠知道這個類的所有屬性和方法,對任意一個對象都能夠通過反射機制調用一個類的任意方法,這種動態獲取類信息及動態調用類對象方法的功能稱爲java的反射機制。

我覺得上邊那些話很不好理解,也不夠清除,還是刨刨祖墳包。

在對象的創建過程中,要經歷如下幾步:

創建對象的時候首先會去先加載類信息進常量池。這樣我們以後使用該類信息都可以直接從常量池中讀取。

反射獲取Class對象有如下幾種反射:

        //method1
        try {
            Class class1 = Class.forName("java基礎.Students");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //method2
        Students students = new Students("CrazyChild",18);
        Class class2 = students.getClass();
        //method3
        Class class3 = Students.class;

反射就是我們通過上面三種方式從常量池中拿到類信息並且封裝成一個Class對象的過程(這個過程是由類加載器完成的)。

那麼拿到他我們可以乾點啥呢?可以爲所欲爲嗎?

先創建一個對象把。有幾種創建方式:

//通過Class.newInstance(),只適合無參
class2.newInstance();
//獲取構造器,通過構造函數執行帶參構造方法
 Constructor constructor = class2.getConstructor(String.class,Integer.class);
  Students stu = (Students) constructor.newInstance("heihei",10);

但是當使用第二種方式的時候,一定要注意,構造函數的參數必須是包裝類,不能用像int這樣,只能用Integer。

創建出對象就可以正常用了。我們也可以使用  方法.invoke(對象,參數)來調用這個函數。

  Students stu = (Students) constructor.newInstance("heihei",10);
  class3.getMethod("say").invoke(stu);

我們雖然可以通過getMethod獲得所有方法包括私有方法,但是我們並不能使用所有方法,因爲調用的時候會有權限檢查,在這裏就不深究了。

代理

代理可以分爲靜態代理、動態代理,動態代理又可以分爲 jvm的動態代理 和 cglib的動態代理。像spring框架的AOP的底層就使用了動態代理的技術。

動態代理實現有兩種方式1.反射生成匿名子類實現動態代理。2.直接添加字節碼創建。cglib只能通過添加字節碼的形式創建子類實現動態代理
那什麼是代理,其實代理是一種場景,就想相親前的互相打聽,男方 ,媒人 , 女方。男方剛一開始不能直接去找人家女方談情說愛,要通過媒人來進行溝通。當然這只是說一開始,等見過面後就用不着媒人。但是見面前還是要用媒人的。(感覺不是很恰當!)
大道理上講代理是一種軟件設計模式(場景),其目的是希望能做到代碼重用。具體上講,代理這種設計模式是通過不直接訪問被代理對象的方式,而訪問被代理對象的方法。(https://www.cnblogs.com/gonjan-blog/p/6685611.html

看起來很好理解,但是我覺得還是得刨一刨祖墳。

先看一個JDK生成匿名子類實現動態代理的例子:

//代理類
public class StuInvocationHandler<T> implements InvocationHandler {
    //invocationHandler持有的被代理對象
    T target;

    public StuInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * proxy:代表動態代理對象
     * method:代表正在執行的方法
     * args:代表調用目標方法時傳入的實參
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執行" +method.getName() + "方法");
        //代理過程中插入監測方法,計算該方法耗時
        Object result = method.invoke(target, args);
        return result;
    }
}
//被代理的對象
public class Students implements Person{
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public Students() {
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Students(String name,Integer age) {
        this.name=name;
        this.age = age;
    }

    public void say(){
        System.out.println(name+" "+age);
    }
    private void sing(){
        System.out.println("我是一隻小小鳥");
    }
}
//測試方法
public static void main(String[] args) {
        //創建一個實例對象,這個對象是被代理的對象
        Person zhangsan = new Students("張三",10);

        //創建一個與代理對象相關聯的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);

        //創建一個代理對象stuProxy來代理zhangsan,代理對象的每個執行方法都會替換執行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

        stuProxy.say();
    }
InvocationHandler接口:proxy代理實例的調用處理程序實現的一個接口,每一個proxy代理實例都有一個關聯的調用處理程序;在代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法。

Proxy:是用來創建一個被代理對象的類。

解釋一下下面這些代碼:

 Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

有三個參數,第一個參數是用來拿到類的加載器,第二個是要代理哪個對象(必須是接口),第三個參數是要交給哪個處理程序執行。

這個過程其實就是在動態的生成一個匿名子類。

在看一個通過添加字節碼的形式實現動態代理:

實體類和接口不變。

public class AddByteRroxy implements InvocationHandler {

    private Object target;

    public AddByteRroxy(Object target) {
        this.target = target;
    }

    public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result =method.invoke(target,args);
        System.out.println("after");
        return result;
    }
}
//測試方法
  public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Person  students = new AddByteRroxy(new Students()).getProxy();
        students.say();
    }

 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");這句話是至關重要的,他把生成的class文件保存到本地。

Cglib增強實現動態代理(需要使用cglib包):

public class MyAspect {
    
    public void before(){
        System.out.println("雞首2");
    }
    
    public void after(){
        System.out.println("牛後2");
    }

}
public class UserServiceImpl {

    public void addUser() {
        System.out.println("a_proxy.b_cglib addUser");
    }

    public void updateUser() {
        System.out.println("a_proxy.b_cglib updateUser");

    }

    public void deleteUser() {

        System.out.println("a_proxy.b_cglib deleteUser");
    }

}
public class MyBeanFactory {
    
    public static UserServiceImpl createService(){
        //1 目標類
        final UserServiceImpl userService = new UserServiceImpl();
        //2切面類
        final MyAspect myAspect = new MyAspect();
        // 3.代理類 ,採用cglib,底層創建目標類的子類
        //3.1 核心類
        Enhancer enhancer = new Enhancer();
        //3.2 確定父類
        enhancer.setSuperclass(userService.getClass());
        /* 3.3 設置回調函數 , MethodInterceptor接口 等效 jdk InvocationHandler接口
         *     intercept() 等效 jdk  invoke()
         *         參數1、參數2、參數3:以invoke一樣
         *         參數4:methodProxy 方法的代理
         *         
         * 
         */
        enhancer.setCallback(new MethodInterceptor(){

            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                
                //前
                myAspect.before();
                
                //執行目標類的方法
                Object obj = method.invoke(userService, args);
                // * 執行代理類的父類 ,執行目標類 (目標類和代理類 父子關係)
                methodProxy.invokeSuper(proxy, args);
                
                //後
                myAspect.after();
                
                return obj;
            }
        });
        //3.4 創建代理
        UserServiceImpl proxService = (UserServiceImpl) enhancer.create();
        
        return proxService;
    }

}

通過動態添加字節碼創建子類實現動態代理。

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