栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java基础:动态代理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java基础:动态代理

生活中顾客一般不会从厂家直接买东西,而是中间销售商做代理,从厂家拿到货,再买到顾客手中,而软件中也有代理模式,常见的UML示意图如下:

        

 需要注意的是一下几点

        1、用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。

        2、接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。

        3、代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。

        4、用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作

下面用示例详细说明。代理可以分为静态代理和动态代理。我们先从静态代理说起

静态代理

从我们平时的看电影举例。电影是电影公司委托给电影院播放的。 但是电影院播放电影时,可以买可乐爆米花,也可以再电影前后添加一些广告,用来增加收益。接下来我们用代码模拟一下

首先是创建一个接口,通用接口是代理模式实现的基础。我们创建一个命名为 Movie ,代表电影院的播放能力

package com.cc.user.myProxy;

public interface Movie {
    public void play();
}

然后我们创建一个接口的实现类,调用 play() 时,表示开始播放电影了。

package com.cc.user.myProxy;


public class CcMovie implements Cinema{
    @Override
    public void play() {
        System.out.println("Cc正在看犬夜叉");
    }
}

那么代理类呢,下面我们创建一个 Cinema 代理类,让他可以播广告,买爆米花,在调用 paly() 前后我们进行了一些买东西的操作

package com.cc.user.myProxy;


public class Cinema implements Movie {

    private CcMovie ccMovie;

    public Cinema(CcMovie ccMovie) {
        this.ccMovie = ccMovie;
    }

    @Override
    public void play() {
        this.attach("买可乐爆米花了");
        ccMovie.play();
        this.attach("买矿泉水啦");
    }

    public void attach(String msg){
        System.out.println(msg);
    }
}

再写一个测试类进行测试

package com.cc.user.myProxy;


public class Test {

    public static void main(String[] args) {
        CcMovie ccMovie = new CcMovie();
        Cinema cinema = new Cinema(ccMovie);
        cinema.play();
    }

}

控制台输出:

 可以看出代理模式在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加和增强。同时要注意,代理类和被代理类需要实现同一接口或者共同继承某类。因为类型等都是已经确定好的所以称为静态代理,下面我们进行动态代理的梳理,动态梳理分为两种,一种基于 JDK 的动态代理,另一种是基于 CGLIB 的动态代理

基于JDK的动态代理

动态代理相比较静态代理,可以在程序运行中自动在内存中创建一个实现 Movie 接口的代理。不用去定义 Cimema 类,因此被称为动态代理

我们以商场柜台做示例进行代码演示。

首先我们创建一个接口,可以理解为买东西的许可

package com.cc.user.myProxy;

public interface Licence {
    public void sell();
}

再创建一个类,maikele 意思就是买可乐

package com.cc.user.myProxy;


public class Maikele implements Licence {
    @Override
    public void sell() {
        System.out.println("我卖可乐");
    }
}

然后我们还需要一个柜台来卖,代码如下:

package com.cc.user.myProxy;

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


public class GuiTaiA implements InvocationHandler {

    private Object huowu;

    public GuiTaiA(Object huowu) {
        this.huowu = huowu;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("销售开始,柜台:" + this.getClass().getSimpleName());
        method.invoke(huowu, args);
        System.out.println("卖出去啦");
        return null;
    }
}

然后我们就可以开始往外卖东西了

import java.lang.reflect.Proxy;


public class Test {

    public static void main(String[] args) {
        Licence licence = new Maikele();
        InvocationHandler xiaoshou = new GuiTaiA(licence);
        Licence imp = (Licence) Proxy.newProxyInstance(Maikele.class.getClassLoader(), Maikele.class.getInterfaces(), xiaoshou);
        imp.sell();
    }
}

控制台输出:

JDK 动态代理 如示例所示需要知道两个类 1.InvocationHandler(接口)、2.Proxy(类)

GuiTaiA 并没有像静态代理那样实现一样的接口,而是实现了 InvocationHandler 接口。

Proxy:

        动态代理涉及的另外一个主要的类就是 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 创建的动态代理

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

参数详解:

        loader  : 类加载器

        interfaces  :代码要用来代理处理的接口

        h :一个 InvocationHandler 对象

InvocationHandler :  

        是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。 内部只是一个 invoke() 方法。

参数详解:

        proxy :代理对象

        method :代理对象的调用方法

        args :调用方法中的参数

Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

我们进一步演示。此时柜台A 我们不只让他卖可乐,还让他卖瓜子,啤酒,火腿肠

示例如下:

接口:

package com.cc.user.myProxy;

public interface OtherHuoWo {
    public void otherSell();
}

实现类 

package com.cc.user.myProxy;


public class MaiOther implements OtherHuoWo {
    @Override
    public void otherSell() {
        System.out.println("瓜子、啤酒、火腿肠啦");
    }
}

   测试:

package com.cc.user.myProxy;

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


public class Test {

    public static void main(String[] args) {
        Licence licence = new Maikele();
        InvocationHandler xiaoshou = new GuiTaiA(licence);
        Licence imp = (Licence) Proxy.newProxyInstance(Maikele.class.getClassLoader(), Maikele.class.getInterfaces(), xiaoshou);
        imp.sell();
        OtherHuoWo otherHuoWo = new MaiOther();
        GuiTaiA guiTaiA = new GuiTaiA(otherHuoWo);
        OtherHuoWo o = (OtherHuoWo) Proxy.newProxyInstance(MaiOther.class.getClassLoader(), MaiOther.class.getInterfaces(), guiTaiA);
        o.otherSell();
    }
}

控制台输出:

GuiTaiA 类我们没有经过任何处理。此时我们就可以很容易看出,GuiTaiA 利用反射可以代理多种类。spring 中的 aop 就是利用动态代理来实现的。下面我们梳理另一种代理方式。

基于CGLIB的动态代理

cglib:Code Generation Library,是一个强大的,高性能,高质量的Code生成类库,也是一个开源项目

首先我们引入CGLIB的jar包

        
            cglib
            cglib
            3.3.0
        

我们再创建一个目标类

package com.cc.user.myCglib;


public class Maijiu {

    public void sell(){
        System.out.println("我是卖酒的");
    }
}

实现 MethodInterceptor接口我们生成代理类

package com.cc.user.myCglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;


public class GuiTaiB implements MethodInterceptor {

    //维护目标对象
    private Class object;

    public GuiTaiB(Class object) {
        this.object = object;
    }

    //给目标对象创建代理对象
    public Object getProxyInstance(){
        //工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(object);
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("销售开始,柜台" + this.getClass().getSimpleName());
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("卖出去啦");
        return o1;
    }
    
    public GuiTaiB() {
    }
}

这个接口只有一个intercept()方法,这个方法有4个参数:

1)Object 表示增强的对象,即实现这个接口类的一个对象;

2)method 表示要被拦截的方法;

3)objects 表示要被拦截方法的参数;

4)methodProxy表示要触发父类的方法对象

测试类:

package com.cc.user.myCglib;

import net.sf.cglib.proxy.Enhancer;


public class Test {
    public static void main(String[] args) {
        GuiTaiB guiTaiB = new GuiTaiB(Maijiu.class);
        Maijiu proxy = (Maijiu) guiTaiB.getProxyInstance();
        proxy.sell();
    }
}

控制台输出:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/572801.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号