靜態代理?動態代理?Cglib代理?

有不夠細緻的地方或者想討論的可以在評論區留言

 

生活中常見的示例

明星的經紀人,第三方無法直接與某明星接觸,只能聯繫經紀人,經紀人可以代爲某明星進行一些事情的處理

 

Java中代理常分以下三種

  • 靜態代理: 核心理解->持有target,編碼在target的方法前後進行處理

  • 動態代理:核心理解->代理對象proxy和被代理對象target要實現共同的接口

  • Cglib代理:核心理解->爲被代理對象target生成子類,子類即爲proxy

 

靜態代理

創建一個測試IUserDao

public interface IUserDao {
    public void save();
}

創建一個測試UserDao

public class UserDao implements IUserDao {
    public void save(){
        System.out.println("測試保存數據=======");
    }
}

創建靜態代理對象

public class StaticProxyUserDao implements IUserDao {

    //持有被代理對象
    private UserDao userDao;

    public StaticProxyUserDao(UserDao userDao){
        this.userDao = userDao;
    }

    @Override
    public void save() {

        System.out.println("目標方法執行前的處理。。。。。。。。。。");
        //執行被代理對象的方法
        userDao.save();

        System.out.println("目標方法執行後的處理。。。。。。。。。。。");
    }
}

總結

靜態代理,target和proxy都需要實現共同的接口,且proxy需要持有target,可以以構造器的方式。

 

動態代理

動態代理爲何代理對象proxy和被代理對象target要實現共同的接口?

 

先從源碼分析中查看JDK對動態代理的支持類java.lang.reflect.Proxy, 該類下有newProxyInstance()方法來創建代理對象,注意該方法中的第二個入參Class<?>[] interfaces 表示被代理類target的接口類型,因此需要target要實現某個接口

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
{
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

示例

在上一步靜態代理中已經創建了IUserDao和UserDao,可以複用

在次創建JDK動態代理​​​​​​​

public class JdkProxyFactory {

    private Object target;

    //構造方法注入target
    public JdkProxyFactory(Object target){
        this.target = target;
    }

    /**
     * 創建代理對象
     * @return
     */
    public Object getProxyInstance(){
        //使用Proxy的newProxyInstance方法
        return Proxy.newProxyInstance( 
                target.getClass().getClassLoader(),//target的類加載器
                target.getClass().getInterfaces(),//target的接口類型
                new InvocationHandler() { //InvocationHandler 很好理解,即在代理對象proxy中對target方法的額外處理
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("目標方法執行前的處理。。。。。。。。。。。。。。");

                        //執行目標對象的方法
                        Object returnValue = method.invoke(target, args);

                        System.out.println("目標方法執行後的處理。。。。。。。。。。");
                        return returnValue;
                    }
                });

    }

}

JDK動態代理測試

public class JdkProxyTest {

    public static void main(String[] args) {
        IUserDao target = new UserDao();
        System.out.println("userDao.getClass() = " + target.getClass());

        IUserDao proxy = (IUserDao) new JdkProxyFactory(target).getProxyInstance();
        System.out.println("proxy.getClass() = " + proxy.getClass());

        //調用代理對象的方法
        proxy.save();
    }
}

結果

​​​​​​​

userDao.getClass() = class com.coolcoding.boot.proxy.UserDao
proxy.getClass() = class com.sun.proxy.$Proxy0
目標方法執行前的處理。。。。。。。。。。。。。。
測試保存數據=======
目標方法執行後的處理。。。。。。。。。。

 

Cglib代理

核心理解:爲被代理對象target生成子類,底層使用字節碼處理框架ASM,有興趣可以自己深入研究ASM

 

創建代理對象​​​​​​​

/**
 * 首先要實現org.springframework.cglib.proxy.MethodInterceptor 
 * 
 * 
 * Cglib代理方式總結:
 * 1.目標類不能爲final,否則拋異常報錯
 * 2.目標類的方法如果爲final, static, 則子類無法重寫,則不會被代理對象攔截。
 *
 *
 * Create by Administrator on 2020/5/14 10:57
 */
public class CglibProxyFactory implements MethodInterceptor {

    private Object target;

    //通過構造器注入target
    public CglibProxyFactory(Object target){
        this.target = target;
    }

    @Override
    public Object intercept(Object o,
                            Method method,
                            Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        System.out.println("目標方法執行前的處理。。。。。。。。。。。。。。");
        Object returnValue = method.invoke(target, args);
        System.out.println("目標方法執行後的處理。。。。。。。。。。");
        return returnValue;
    }

    /**
     * 創建cglib代理對象
     * @return
     */
    public Object getProxyInstance(){
        //使用org.springframework.cglib.proxy.Enhancer
        Enhancer enhancer = new Enhancer();
        //設置父類類型 -> 對應核心理解中的爲target創建子類
        enhancer.setSuperclass(target.getClass());
        //設置回調方法,則調用時會觸發該類中的intercept方法
        enhancer.setCallback(this);
        //創建代理對象 即子類
        return enhancer.create();
    }
}

Cglib測試

UserDao2 與UserDao一致,唯一區別是UserDao2沒有實現接口,因爲Cglib不需要target實現接口

​​​​​​​

public class CglibTest {

    public static void main(String[] args) {

        //生成一個目標對象
        UserDao2 target = new UserDao2();
        System.out.println("userDao.getClass() = " + target.getClass());

        //創建代理對象
        UserDao2 proxy = (UserDao2) new CglibProxyFactory(target).getProxyInstance();
        System.out.println("proxy.getClass() = " + proxy.getClass());
        //執行代理對象的方法
        proxy.save();
    }
}

Cglib結果​​​​​​​​​​​​​​

userDao.getClass() = class com.coolcoding.boot.proxy.UserDao2
proxy.getClass() = class com.coolcoding.boot.proxy.UserDao2$$EnhancerByCGLIB$$74c2897e
目標方法執行前的處理。。。。。。。。。。。。。。
保存數據....cglib....
目標方法執行後的處理。。。。。。。。。。

Cglib總結:

  1. Cglib中target不能爲final類型,因爲final類型的類無法爲其創建子類

  2. target中的方法不能爲final或者static,雖然可以正常運行,但是由於方法無法被子類重寫,而不會被代理對象攔截。

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