1. 程式人生 > >靜態代理、動態代理

靜態代理、動態代理

== getclass 切面 接口 tor 7月 第一個 proxy 字節碼

  代理模式最大的優勢就是能夠解耦,在spring中也是廣泛使用。spring中一個重要的特性就是aop,aop是個啥東西呢?其實很簡單,比如現在有個業務方法,那這個業務方法很重要,涉及到非常重要的業務數據,那對於廣大企業應用來說,為了以後能夠及時的定位問題,需要記錄相關入參以及出參到日誌表。

技術分享圖片

但是對於企業應用來說,需要記錄日誌的地方應該是蠻多的,如果每個方法中都手動的去寫這些記錄日誌的東西,就會特別的冗余,那使用代理模式就可以解決。

一、靜態代理

  1、User接口

package com.ty.staticProxy;

/**
 * @author Taoyong
 * @date 2018年7月1日
 * 天下沒有難敲的代碼!
 
*/ public interface User { /* * 業務邏輯接口 */ public void work(String workName); }

  

  2、UserImpl實現類

package com.ty.staticProxy;

/**
 * @author Taoyong
 * @date 2018年7月1日
 * 天下沒有難敲的代碼!
 */
public class UserImpl implements User {

    /*
     * 實際業務邏輯實現方法
     */
    @Override
    public void
work(String workName) { System.out.println("我是做" + workName + "的"); } }

  3、代理類

package com.ty.staticProxy;

/**
 * @author Taoyong
 * @date 2018年7月1日
 * 天下沒有難敲的代碼!
 * 此類為代理類,並且實現業務邏輯類接口,接收一個實際業務處理對象
 */
public class ProxyUser implements User {

    private User user;
    
    public ProxyUser(User user) {
        
this.user = user; } @Override public void work(String workName) { /* * 調用實際業務邏輯處理前可以定制化一些功能 */ System.out.println("工作前先放松放松============="); /* * 調用實際業務邏輯處理方法 */ user.work(workName); /* * 調用實際業務邏輯處理後也可以定制一些功能 */ System.out.println("工作後還是要放松放松============="); } }

  4、StaticProxyDemo

package com.ty.staticProxy;

/**
 * @author Taoyong
 * @date 2018年7月1日
 * 天下沒有難敲的代碼!
 */
public class StaticProxyDemo {

    public static void main(String[] args) {
        User proxyUser = new ProxyUser(new UserImpl());
        proxyUser.work("java開發");
    }
}

運行結果:

工作前先放松放松=============
我是做java開發的
工作後還是要放松放松=============

二、動態代理

在java中,實現動態代理主要有兩種方式,一種是jdk動態代理,一種是cglib

1、jdk動態代理

User以及UserImpl跟上面一致

a、UserDynamicProxy

package com.ty.dynamic;

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

/**
 * @author Taoyong
 * @date 2018年7月2日
 * 天下沒有難敲的代碼!
 */
public class UserDynamicProxy implements InvocationHandler {

    private User user;
    
    public UserDynamicProxy(User user) {
        this.user = user;
    }

    /*
     * jdk動態代理基於接口,其中proxy好像沒啥卵用、method代表當前被代理對象的實際調用方法、args則代表方法參數
     * 由invoke方法對被代理對象進行相關的增強
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("工作前放松放松========");
        method.invoke(user, args);
        System.out.println("工作後也要放松放松===========");
        return null;
    }

}

b、DynamicProxyDemo

package com.ty.dynamic;

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

/**
 * @author Taoyong
 * @date 2018年7月2日
 * 天下沒有難敲的代碼!
 */
public class DynamicProxyDemo {

    public static void main(String[] args) {
        User user = new UserImpl();
        InvocationHandler h = new UserDynamicProxy(user);
        /*
         * 使用Proxy的靜態方法是生成代理類的核心。
         * 一共有三個參數:
         * 1、第一個參數是被代理類的類加載器,通過此類加載器將代理類加載入jvm中;
         * 2、第二個參數則是被代理類所實現的所有接口,需要所有的接口的目的是創建新的代理類實現被代理類的所有接口,保證被代理類所有方法都能夠
         * 被代理。其實代理的核心就是新創建一個類並實例化對象,去集成被代理對象所有功能的同時,再加入某些特性化的功能;
         * 3、第三個參數則是真正的擴展,使用動態代理的主要目的就是能夠對原方法進行擴展,尤其是對於大部分方法都具有的重復方法(例如記錄日誌),
         * 可以理解為面向切面編程中的增強.
         */
        User proxy = (User) Proxy.newProxyInstance(User.class.getClassLoader(), user.getClass().getInterfaces(), h);
        /*
         * 在調用生成的代理類對象後,調用原方法後,該method對象以及參數等會被傳入到InvocationHandler的invoke方法中,由InvocationHandler的
         * invoke方法對被代理類對象進行增強。
         */
        proxy.work("敲代碼");
        proxy.eat("吃大餐");
        
    }
}

2、cglib代理

jdk動態代理的缺點就是必須基於接口,沒有接口就無法實現代理,而cglib則是使用繼承的方式去生成代理類,使用範圍更廣

a、添加cglib.jar、asm.jar(註意jar包版本)

使用cglib前必須進行導包,並且版本如果過低會導致報錯

技術分享圖片

b、UserImpl(使不使用接口都可以)

package com.ty.dynamic.cglib;

/**
 * @author Taoyong
 * @date 2018年7月1日
 * 天下沒有難敲的代碼!
 */
public class UserImpl {
    
    /*
     * 實際業務邏輯實現方法
     */
    public void work(String workName) {
        System.out.println("我是做" + workName + "的");
    }

    
}

c、CglibDynamicProxy

package com.ty.dynamic.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * @author Taoyong
 * @date 2018年7月2日
 * 天下沒有難敲的代碼!
 */
public class CglibDynamicProxy implements MethodInterceptor {

    /*
     * 實際的增強
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("這是前處理==============");
        methodProxy.invokeSuper(obj, objects);        
        System.out.println("這是前處理==============");
        return null;
    }

}

d、CglibProxyDemo

package com.ty.dynamic.cglib;

import net.sf.cglib.proxy.Enhancer;

/**
 * @author Taoyong
 * @date 2018年7月2日
 * 天下沒有難敲的代碼!
 */
public class CglibProxyDemo {

    public static void main(String[] args) {
        /*
         * 創建字節碼增強器
         */
        Enhancer enhancer = new Enhancer();
        /*
         * 被代理類設置為字節碼增強器父類,cglib使用的是繼承方式去創建代理類
         */
        enhancer.setSuperclass(UserImpl.class);
        /*
         * 設置字節碼增強器回調方法
         */
        enhancer.setCallback(new CglibDynamicProxy());
        /*
         * 創建代理實例
         */
        UserImpl userImpl = (UserImpl) enhancer.create();
        userImpl.work("敲代碼");
    }
}

靜態代理、動態代理