java 的代理模式

代理模式

代理proxy是一種設(shè)計模式,提供對目標對象的另外的訪問模式。好處是可以擴展目標對象的功能。
在實際開發(fā)中,可以不修改別人已經(jīng)寫好的代碼,通過代理的方式來擴展功能。
生活中的例子:我們們要邀請明星,并不是直接聯(lián)系明星,而是通過經(jīng)紀人。

在這里插入圖片描述
關(guān)鍵點是代理對象對目標對象的擴展,并且會調(diào)用目標對象

1.靜態(tài)代理

靜態(tài)代理需要定義接口或者父類,被代理的對象和代理對象要實現(xiàn)相中的接口或者繼承相同的父類

/**
*接口
*/
public interface IUserDao{
  public void work();
}
/**
*接口實現(xiàn) 目標對象
*/
public class UserDao implements IUserDao{
    public void work(String begin){
            System.out.println("----"+begin+"點開始工作!----");
    }
}

/**
 * 代理對象,靜態(tài)代理
 */
 public class UserDaoProxy implements IUserDao{
    //接收保存目標對象
    private IUserDao userDao;
    public UserDaoProxy(IUserDao userDao){
        this.userDao=userDao;
  } 
  public void work() {
        System.out.println("---before--");
        userDao.work();//執(zhí)行目標對象的方法
        System.out.println("---after---");
    }
}

/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        //目標對象
        UserDao userDao= new UserDao();
        //代理對象,把目標對象傳給代理對象,建立代理關(guān)系
        UserDaoProxy proxy = new UserDaoProxy(userDao);
        proxy.work();//執(zhí)行的是代理的方法
    }
}

1、靜態(tài)代理可以做到在不修改目標對象的功能前提下,對目標功能擴展
2、缺點是由于目標對象和代理對象實現(xiàn)共同的接口,所有會有很多的代理類,一旦接口增加方法,目標對象和代理對象都要維護,維護成本大

動態(tài)代理

1、動態(tài)代理,代理對象不需要實現(xiàn)接口
2、代理對象的生成是利用JDK的API,在內(nèi)存中動態(tài)的構(gòu)建對象。需要指定創(chuàng)建代理或目標對象的接口的類型。

代理類所在的包:java.lang.reflect.Proxy,使用newProxyInstance方法,
該方法為靜態(tài)方法,有三個參數(shù)分別為

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

ClassLoader loader:用哪個類加載器去加載代理對象
Class<?>[] interfaces:動態(tài)代理類需要實現(xiàn)的接口
InvocationHandler h:動態(tài)代理方法在執(zhí)行時,會調(diào)用h里面的invoke方法去執(zhí)行。會把當前執(zhí)行目標對象的方法作為參數(shù)傳入

/**
*代理工廠類
*/
public class ProxyFactory{
        //維護目標對象
        private Object target;
        public ProxyFactory(Object target){
            this.target=target;
        }
        //給目標對象生成代理對象
    public Object getProxyInstance(){
            return Proxy.newProxyInstance(
                                target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) 
                                                                                throws Throwable {
                        System.out.println("---before--");
                        //執(zhí)行目標對象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("---after---");
                        return returnValue;
                    }
                                }
                        );
        }
}

/**
 * 測試類
 */
 public class Test {
        // 目標對象
    IUserDao target = new UserDao();
    // 給目標對象,創(chuàng)建代理對象
    IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
    // 執(zhí)行方法   
    proxy.work('9');
 }
結(jié)果
---before--
----9點開始工作!----
---after---
  • 動態(tài)代理的代理對象不需要實現(xiàn)接口,但是目標對象需要實現(xiàn)接口。

Cglib代理

  • JDK的動態(tài)代理需要目標對象實現(xiàn)接口,而Cglib能代理沒有實現(xiàn)接口的類。它可以在運行期擴展Java類與實現(xiàn)Java接口,提供方法的interception(攔截)。
  • Cglib包的底層是通過使用一個小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
  • Cglib動態(tài)構(gòu)建子類,代理的類不能為final,目標對象的方法如果為final/static,那么就不會被攔截,即不會執(zhí)行目標對象額外的業(yè)務(wù)方法
/**
 * 目標對象,沒有實現(xiàn)任何接口
 */
public class UserDao {
    public void work() {
        System.out.println("----開始工作!----");
    }
}
/**
 * Cglib子類代理工廠
 */
public class ProxyFactory implements MethodInterceptor{
        //維護目標對象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
        //給目標對象創(chuàng)建一個代理對象
    public Object getProxyInstance(){
        //1.工具類
        Enhancer en = new Enhancer();
        //2.設(shè)置父類
        en.setSuperclass(target.getClass());
        //3.設(shè)置回調(diào)函數(shù)
        en.setCallback(this);
        //4.創(chuàng)建子類(代理對象)
        return en.create();

    }
        @Override
    public Object intercept(Object obj, Method method, 
                Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("befor...");

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

        System.out.println("after...");

        return returnValue;
    }
}
/**
 * 測試類
 */
public class Test{

    @Test
    public void test(){
        //目標對象
        UserDao target = new UserDao();
        //代理對象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
        proxy.work();
    }
}

在Spring的AOP中:
如果加入容器的目標對象有實現(xiàn)接口,用JDK代理
如果目標對象沒有實現(xiàn)接口,用Cglib代理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容