動態代理的介紹(非aop) 基於接口 基於子類 舉例說明

動態代理方式

動態代理:
特點:字節碼隨用隨創建,隨用隨加載
作用:不修改源碼的基礎上對方法增強
分類:
    基於接口的動態代理
    基於子類的動態代理

一、基於接口的動態代理

    基於接口的動態代理:
       涉及的類:Proxy
       提供者:JDK官方
    如何創建代理對象:
       使用Proxy類中的newProxyInstance方法
    創建代理對象的要求:
        被代理類最少實現一個接口,如果沒有則不能使用
    newProxyInstance方法的參數:
         ClassLoader:類加載器
               它是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。
               固定寫法:
                   代理是誰就寫誰的 XXX.getClass().getClassLoader()
 
         Class[]:字節碼數組
               它是用於讓代理對象和被代理對象有相同的方法
               固定寫法:
                   代理誰就寫誰的 XXX.getClass().getInterfaces()
 
         InvocationHandler:
               他是讓我們寫如何代理。一般是寫一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的
               此接口的實現類都是誰用誰寫。
 
               注意:如果我們的類不實現任何接口的時候,執行Client會報代理異常
                       不使用任何接口,proxy這種代理無法使用

舉例:賣東西
前提:有一個接口IProducer,一個實現類Producer(最後我會附上這部分的代碼(“動態代理介紹”)
IProducer proxyProducer爲動態代理對象,方法執行調用它

newProxyInstance方法
	被代理類最少實現一個接口,如果沒有則不能使用
	前兩步都是一個套路
		1.Class 加載代理對象
			代理XXX就寫XXX.getClass().getClassLoader()
		2.Class[]
			讓代理對象和被代理對象有相同的方法
			代理XXX就寫XXX.getClass().getClassInterfaces()
		下一步不同了
		3.InvocationHandler 調用處理程序
			有下面的方法
				這裏是Object類型,所以我們創建代理對象的時候記得強轉一下
		            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
						 * @param proxy     代理對象的引用
			             * @param method    當前執行的方法
			             * @param args   當前執行方法所需要的參數
			             * @return 和被代理對象方法有相同的返回值
			             	執行方法的一般寫法
			             		Object returnValue = null;
			             		returnValue=執行方法;
			             		return returnValue;

再正常執行方法

public class Client {
    public static void main(String[] args) {
        //匿名類訪問外部成員變量的時候,要求是最終的final
        //彈幕:局部變量隨着方法的調用而調用,隨着方法消失而消失,而對內存的內容不會立即消失,還會繼續引用局部變量
        final Producer producer = new Producer();
//返回的是Object類型 需要強轉一下
		IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
		        producer.getClass().getInterfaces(),
		        new InvocationHandler() {
		            /**匿名內部類
		             *
		             * 作用:執行被代理對象的任何接口方法都會經過該方法
		             * 方法參數的含義
		             * @param proxy     代理對象的引用
		             * @param method    當前執行的方法
		             * @param args   當前執行方法所需要的參數
		             * @return 和被代理對象方法有相同的返回值
		             * @throws Throwable
		             */
		            @Override
		            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		                Object returnValue = null;
		                //1.獲取方法執行的參數
		                Float money = (Float) args[0];
		                //2.判斷當前方法是不是銷售
		                if ("saleProduct".equals(method.getName())) {
		                	//執行操作
		                    returnValue = method.invoke(producer, money * 0.8f);
		                }
		                return returnValue;
		            }
		        });
		proxyProducer.saleProduct(1000f);

二、基於子類的動態代理

基於子類的動態代理:
    涉及的類:Enhancer
    提供者:第三方cglib庫
如何創建代理對象:
    使用Enhancer類中的create方法
創建代理對象的要求:
    被代理類不能是最終類
create方法的參數:
    Class:字節碼
        它是用於指定被代理對象的字節碼。
        XXX.getClass().getClassLoader();

    Callback:用於提供增強的代碼(和
        它是讓我們寫如何代理。我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
        此接口的實現類都是誰用誰寫。
        我們一般寫的都是該接口的子接口實現類:MethodInterceptor 方法攔截
 

舉例:賣東西
前提:有一個接口IProducer,一個實現類Producer(最後我會附上這部分的代碼(“動態代理介紹”)
Producer cglibProducer爲動態代理對象,方法執行調用它

被代理類不能是最終類(不能是final)
	create方法
			1.Class加載代理對象
				代理XXX就寫XXX.getClass().getClassLoader()
			2.Callback:用於提供增強的代碼
				一般寫的都是該接口的子接口實現類
				public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {}
					 * 執行被代理對象的任何方法都會經過該方法
		             * @param proxy
		             * @param method
		             * @param args
		             *      以上三個參數和基於接口的動態代理中invoke方法的參數是一樣的
		             * @param methodProxy:當前執行方法的代理對象(一般用不上8
				             	執行方法的一般寫法
				             		Object returnValue = null;
				             		returnValue=執行方法;
				             		return returnValue;

再正常執行方法

  public class Client {
    public static void main(String[] args) {
        //匿名類訪問外部成員變量的時候,要求是最終的final
        final Producer producer = new Producer();
  Producer cglibProducer=(Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 執行被代理對象的任何方法都會經過該方法
             * @param proxy
             * @param method
             * @param args
             *      以上三個參數和基於接口的動態代理中invoke方法的參數是一樣的
             * @param methodProxy:當前執行方法的代理對象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object returnValue = null;
                //1.獲取方法執行的參數
                Float money = (Float) args[0];
                //2.判斷當前方法是不是銷售
                if ("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money * 0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(1000f);

三、源碼及模塊結構

在這裏插入圖片描述
IProducer接口

public interface IProducer {
    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money);
}

Producer類

/**
 * 一個生產者
 */
public class Producer implements IProducer {
    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("銷售產品,並拿到錢:"+money);
    }
}

Proxy裏的Client(執行方法測試

public class Client {
    public static void main(String[] args) {
        final Producer producer = new Producer();
        //返回的是Object類型 需要強轉一下
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object returnValue = null;
                        //1.獲取方法執行的參數
                        Float money = (Float) args[0];
                        //2.判斷當前方法是不是銷售
                        if ("saleProduct".equals(method.getName())) {
                            //執行操作
                            returnValue = method.invoke(producer, money * 0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(1000f);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章