Spring AOP里的靜態代理和動態代理,你真的了解嘛?


什么是代理?

  為某一個對象創建一個代理對象,程序不直接用原本的對象,而是由創建的代理對象來控制原對象,通過代理類這中間一層,能有效控制對委托類對象的直接訪問,也可以很好地隱藏和保護委托類對象,同時也為實施不同控制策略預留了空間

什么是靜態代理?

  由程序創建或特定工具自動生成源代碼,在程序運行前,代理類的.class文件就已經存在

  通過將目標類與代理類實現同一個接口,讓代理類持有真實類對象,然后在代理類方法中調用真實類方法,在調用真實類方法的前后添加我們所需要的功能擴展代碼來達到增強的目的。

優點

  代理使客戶端不需要知道實現類是什么,怎么做,而客戶端只需知道代理即可

  方便增加功能,擴展業務邏輯

缺點

  代理類中常出現大量冗余的代碼,非常不利於擴展和維護

  如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的復雜度

案例演示

PayService.java(接口

package net.cybclass.sp.proxy;

public interface PayService {
    /**
     * 支付回調
     * @param outTradeNo 訂單號
     * @return
     */
    String callback(String outTradeNo);

    /**
     * 下單
     * @param userId 用戶id
     * @param productId 產品id
     * @return
     */
    int save(int userId,int productId);
}

PayServiceImpl.java(接口實現類)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
    public String callback(String outTradeNo) {
        System.out.println("目標類 PayServiceImpl 回調 方法 callback");
        return outTradeNo;
    }

    public int save(int userId, int productId) {
        System.out.println("目標類 PayServiceImpl 回調 方法 save");
        return productId;
    }
}

StaticProxyPayServiceImpl.java(接口實現類,靜態代理)

package net.cybclass.sp.proxy;

public class StaticProxyPayServiceImpl implements PayService{
    private PayService payService;
    public StaticProxyPayServiceImpl(PayService payService)
    {
        this.payService=payService;
    }
    public String callback(String outTradeNo) {
        System.out.println("StaticProxyPayServiceImpl callback begin");
        String result=payService.callback(outTradeNo);
        System.out.println("StaticProxyPayServiceImpl callback end");
        return result;
    }

    public int save(int userId, int productId) {
        System.out.println("StaticProxyPayServiceImpl save begin");
        int id = payService.save(userId, productId);
        System.out.println("StaticProxyPayServiceImpl save end");
        return id;
    }
}

演示

什么是動態代理?

  在程序運行時,運用反射機制動態創建而成,無需手動編寫代碼

    • JDK動態代理
    • CGLIB動態代理(原理:是對指定的業務類生成一個子類,並覆蓋其中的業務方法來實現代理)

jdk動態代理演示

定義一個類,去實現InvocationHandler這個接口,並車從寫invoke方法

//Object proxy:被代理的對象
//Method method:要調用的方法
//Object[] args:方法調用時所需要參數
public Object invoke(Object proxy, Method method, Object[] args){}

PayService.java(接口)

package net.cybclass.sp.proxy;

public interface PayService {
    /**
     * 支付回調
     * @param outTradeNo 訂單號
     * @return
     */
    String callback(String outTradeNo);

    /**
     * 下單
     * @param userId 用戶id
     * @param productId 產品id
     * @return
     */
    int save(int userId,int productId);
}

PayServiceImpl.java(接口實現類)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
    public String callback(String outTradeNo) {
        System.out.println("目標類 PayServiceImpl 回調 方法 callback");
        return outTradeNo;
    }

    public int save(int userId, int productId) {
        System.out.println("目標類 PayServiceImpl 回調 方法 save");
        return productId;
    }
}

JDKProxy.java(jdk動態代理類)

package net.cybclass.sp.proxy;

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

public class JDKProxy implements InvocationHandler {
    //目標類
    private Object targetObject;

    /**
     * 獲取代理對象
     * @param targetObject 目標類
     * @return
     */
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        //綁定關系,也就是和具體的那個實現類關聯
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(), this);
    }

    /**
     * JDK動態代理
     *
     * @param proxy  靜態代理對象
     * @param method 要調用的方法
     * @param args   方法調用時所需要參數
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try {
            System.out.println("通過JDK動態代理調用"+method.getName()+",打印日志 begin");
            result = method.invoke(targetObject, args);
            System.out.println("通過JDK動態代理調用"+method.getName()+",打印日志 end");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return result;
    }
}

演示

CGLIB動態代理演示

PayService.java(接口)

package net.cybclass.sp.proxy;

public interface PayService {
    /**
     * 支付回調
     * @param outTradeNo 訂單號
     * @return
     */
    String callback(String outTradeNo);

    /**
     * 下單
     * @param userId 用戶id
     * @param productId 產品id
     * @return
     */
    int save(int userId,int productId);
}

PayServiceImpl.java(接口實現類)

package net.cybclass.sp.proxy;

public class PayServiceImpl implements PayService{
    public String callback(String outTradeNo) {
        System.out.println("目標類 PayServiceImpl 回調 方法 callback");
        return outTradeNo;
    }

    public int save(int userId, int productId) {
        System.out.println("目標類 PayServiceImpl 回調 方法 save");
        return productId;
    }
}

CGLIBProxy.java(CGLIB動態代理類)

package net.cybclass.sp.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBProxy implements MethodInterceptor {
    //目標類
    private Object targetObject;
    //綁定關系
    public Object newProxyInstance(Object targetObject){
        this.targetObject=targetObject;
        Enhancer enhancer=new Enhancer();
        //設置代理類的父類(目標類)
        enhancer.setSuperclass(this.targetObject.getClass());
        //設置回調函數
        enhancer.setCallback(this);
        //創建子類(代理對象)
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result=null;
        try
        {
            System.out.println("通過CGLIB動態代理調用"+method.getName()+",打印日志 begin");
            result=methodProxy.invokeSuper(o,args);
            System.out.println("通過CGLIB動態代理調用"+method.getName()+",打印日志 end");
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
        return result;
    }
}

演示

總結

  動態代理與靜態代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理,解耦和易維護。

兩種動態代理的區別

  • JDK動態代理:要求目標對象實現一個接口,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的接口,這個時候就可以用CGLIB動態代理
  • JDK動態代理是自帶的,CGLIB需要引入第三方包
  • CGLIB動態代理,它是內存中構建一個子類對象從而實現對目標對象功能的擴展
  • CGLIB動態代理基於繼承來實現代理,所以無法對final類,private方法和static方法實現代理

Spring AOP中的代理使用的默認策略

  • 如果目標對象實現類接口,則默認采用JDK動態代理
  • 如果目標對象沒有實現接口,則采用CGLIB進行動態代理

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM