JAVA實習找工作——反射機制和動態代理

引言

在反射出現之前,當我們有一個java類之後,我們在類的外部是不能使用類中的私有結構的,比如說不能調用private的構造器,private的方法等等。但是出現了反射之後,我們就可以調用這個類的任何結構,包括私有的。

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionTest {

    @Test
    public void test1(){
        Person p = new Person("tom",23);
        p.show();
    }


    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.獲取類對象
        Class<Person> personClass = Person.class;
        //2.獲取類對象的構造器
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        Object tom = constructor.newInstance("Jerry", 23);
        //3.獲取類方法
        Method show = personClass.getDeclaredMethod("show");
        show.invoke(tom);

        //反射的特殊功能,可以調用私有的類結構
        Constructor<Person> cons1 = personClass.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person wanglei = cons1.newInstance("wanglei");
        wanglei.show();


    }

}

反射和單例模式

單例模式私有化了構造器,希望這個類的實例只需要創建一個就可以了。到那時反射是解決能不能用的問題,單例模式是建議你的這張寫法,他們是不衝突的。

關於java.lang.Class的理解

Class作爲反射的源頭

(1)類的加載過程:程序經過javac.exe編譯之後會生成一個或多個字節碼文件.class結尾,每一個java類對應一個字節碼文件,通過java.exe解釋運行某個包含有mian方法的字節碼文件。相當於把這個字節碼文件加載到內存中,此過程稱爲類的加載。記載到內存中的類稱之爲運行時類,這個運行時類作爲Class的一個實例。換句話說Class的實例就對應着一個運行時類。

獲取Class對象的四種方式

//方式一:調用運行時類的屬性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通過運行時類的對象,調用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        //方式三:調用Class的靜態方法:forName(String classPath)
        Class clazz3 = Class.forName("com.atguigu.java.Person");
//      clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        //方式四:使用類的加載器:ClassLoader  (瞭解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
        System.out.println(clazz4);

        System.out.println(clazz1 == clazz4);

三步:

加載:將類的class文件加入到內存,並創建Class對象,此過程由類加載器完成

鏈接:確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。

初始化:在編譯生成class文件時,編譯器會產生兩個方法加於class文件中,一個是類的初始化方法clinit, 另一個是實例的初始化方法init。clinit指的是類構造器,主要作用是在類加載過程中的初始化階段進行執行,執行內容包括靜態變量初始化和靜態塊的執行。

 

 

引言

代理模式是23種設計模式中的一種,是比較重要的知識,在Spring框架中比較重要的AOP(Aspect Oriented Programing)也是基於動態代理實現的。

代理的理解

關於代理的理解,我舉個例子說明:20年前,我們想要購買一臺聯想電腦的方式是:我們去聯想的工廠,工廠賣給我們電腦,並且提供售後的服務。

在這幾十年的發展中,出現了一些經銷商,他們從聯想工廠進貨,然後賣給我們,電腦壞了我們也先去經銷商的店裏找他,然後他再去找工廠,於是我們和工廠的聯繫其實就中斷了。

2020年,我們想要買一臺筆記本電腦,會直接去天貓、蘇寧、京東等大型的經銷商那裏去買一臺電腦,出現問題就7天無理由退貨。

這裏我們就是客戶端Client,那些經銷商也叫做代理商,聯想工廠就叫做被代理對象。這樣的過程就是代理。

動態代理

動態代理的特點:字節碼隨用隨創建,隨用隨加載

動態代理的作用:在不修改源碼的基礎上增強方法

動態代理的分類:2類,一個是基於接口的動態代理,一個是基於子類的動態代理

基於接口的動態代理

由JDK提供的Proxy類實現,要求被代理類至少實現一個接口。實現過程如下

(1)首先創建Lenovo類並且實現一個接口

package com.alibaba200408.動態代理;

public class Lenovo implements ILenovo {
    @Override
    public void sale(Double money) {
        System.out.println("拿到"+money+"元,電腦發貨");
    }

    @Override
    public void afterService(Double money) {
        System.out.println("拿到"+money+"元,售後服務開始");

    }
}

代碼很簡單,只涉及了2個方法,一個是銷售電腦,一個是維修電腦。

接口的代碼如下

package com.alibaba200408.動態代理;

public interface ILenovo {

    void sale(Double money);

    void afterService(Double money);
}

(2)實例化一個動態代理的對象,通過代理對象調用方法

package com.alibaba200408.動態代理;

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

public class Client {
    public static void main(String[] args) {

        //聯想廠家
        ILenovo lenovo = new Lenovo();


        //創建代理對象
        ILenovo proxyInstance = (ILenovo) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(),
                lenovo.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * invoke方法的作用是調用任何被代理對象的方法都會被這個invoke方法攔截
                     *  proxy:表示當前代理對象的引用
                     *  method:表示當前正在執行的方法
                     *  args:表示當前執行的方法的參數
                     *
                     *  返回值就是當前方法的返回值
                     */

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增強的代碼
                        Double money = (Double) args[0];
                        if ("sale".equals(method.getName())){
                            method.invoke(lenovo, money * 0.8);
                        }else {
                            method.invoke(lenovo,args);
                        }

                        return null;
                    }
                });
           
        //使用代理對象
        proxyInstance.afterService(10000.0);

    }
}

基於子類的動態代理

這種實現方式要求被代理類不能是final的,因爲final修飾的類不能有子類,其次這種方式需要第三方的cglib。

基於子類的動態代理
    涉及到的類Enhancer
    使用Enhancer中的create()方法
要求:被代理類不能是最終類

參數一:被代理對象的字節碼
參數二:callback用於寫提供增強的代碼,我們一般使用它的實現類MethodInterceptor


//生成代理對象
Product proxy = Enhancer.create(product.class,new MethodInterceptor(){
    @Override
    public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
               //提供增強的代碼
               Double money = (Double) args[0];
               if ("sale".equals(method.getName())){
                   method.invoke(lenovo, money * 0.8);
               }else {
                   method.invoke(lenovo,args);
               }

               return null;

    }
});

 

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