前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊一聊java中的代理模式

聊一聊java中的代理模式

作者头像
姜同学
发布2022-10-27 15:47:07
2710
发布2022-10-27 15:47:07
举报
文章被收录于专栏:姜同学
代码语言:javascript
复制
代理模式

我们都知道java中有23种设计模式,今天我们就来聊一聊这23种设计模式中的代理设计模式,首先代理模式其实就是控制对其他对象的访问,在访问其他对象之前我们可以进行一些其他的操作。其实代理一般分为一以下四类:

  • 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
  • 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

其实这几种设计模式通常用我们常说的动态代理全部都可以实现,下面我就引用一下虚拟代理的实例,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。

代码语言:javascript
复制
public interface Image {
    void showImage();
}
代码语言:javascript
复制
public class HighResolutionImage implements Image {

    private URL imageURL;
    private long startTime;
    private int height;
    private int width;

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public HighResolutionImage(URL imageURL) {
        this.imageURL = imageURL;
        this.startTime = System.currentTimeMillis();
        this.width = ;
        this.height = ;
    }

    public boolean isLoad() {
        // 模拟图片加载,延迟 3s 加载完成
        long endTime = System.currentTimeMillis();
        return endTime - startTime > ;
    }

    @Override
    public void showImage() {
        System.out.println("Real Image: " + imageURL);
    }
}
java
代码语言:javascript
复制
public class ImageProxy implements Image {

    private HighResolutionImage highResolutionImage;

    public ImageProxy(HighResolutionImage highResolutionImage) {
        this.highResolutionImage = highResolutionImage;
    }

    @Override
    public void showImage() {
        while (!highResolutionImage.isLoad()) {
            try {
                System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());
                Thread.sleep();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        highResolutionImage.showImage();
    }
}
java
代码语言:javascript
复制
public class ImageViewer {

    public static void main(String[] args) throws Exception {
        String image = "http://image.jpg";
        URL url = new URL(image);
        HighResolutionImage highResolutionImage = new HighResolutionImage(url);
        ImageProxy imageProxy = new ImageProxy(highResolutionImage);
        imageProxy.showImage();
    }
}
java
代码语言:javascript
复制
JDK中的动态代理

其实在jdk中的java.lang.reflect的包下就为我们内置了一种基与接口的动态代理模式proxy,其实这种动态代理模式也非常的好理解,我们用演员来举一个例子,演员出名了以后,就要给粉丝签签名,陪粉丝吃吃饭啦。这里模拟一个演员的接口

代码语言:javascript
复制
public interface Actor {
    /**
     * 给人签名
     */
    public void  signature();

    /**
     * 和粉丝吃饭
     */
    public void eat();
}
java

有一个叫做FBB的演员实现了演员这个接口

代码语言:javascript
复制
public class FBB implements Actor{

    public void signature() {

        System.out.println("给人签名...");
    }

    public void eat() {

        System.out.println("陪人吃饭...");
    }
}
java

后来FBB越来越火,想要和她要签名和吃饭的粉丝也越来越多,所以FBB就请来一个经纪人帮他拦一拦

代码语言:javascript
复制
public class Proxy {
    public static void main(String[] args) {
        final FBB fbb = new FBB();

        // FBB的代理类
        Actor proxyActor = (Actor) java.lang.reflect.Proxy.newProxyInstance(fbb.getClass().getClassLoader(), fbb.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("你谁呀!");

                if ("是一个真爱粉") {
                    return method.invoke(fbb);
                }
                return null;
            }
        });

        proxyActor.signature();
        proxyActor.eat();
    }
}

这样FBB每次就不用每次都为给谁签名和谁吃饭而发愁了,他的经纪人也就是JDK中内置的proxy就全权帮他搞定了,这里再介绍一下proxy的api: Proxy.newProxyInstance(三个参数) 参数含义: ClassLoader:和被代理对象使用相同的类加载器。 Interfaces:和被代理对象具有相同的行为。实现相同的接口。 InvocationHandler:如何代理。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 参数: proxy:代理对象的引用。不一定每次都用得到 method:当前执行的方法对象 args:执行方法所需的参数 **返回值:**当前执行方法的返回值 这就是JDk中自带的动态模式,但是这种代理模式是有bug的,如果FBB中存在接口中没有的方法,则就无法进行代理。所以这里在介绍由第三方类库Cglib提供的另外一种一种基于继承的动态代理方式。

代码语言:javascript
复制
Cglib中的动态代理

其实他和jdk中的静态代理非常相似,一般分为一下几5个步骤

代码语言:javascript
复制
public class cglibProxy {
    public static void main(String[] args) {
        final FBB fbb = new FBB();

        // 使用cglib动态代理fbb
        // 1.创建增强器
        Enhancer enhancer = new Enhancer();
        // 2.设定接口
        enhancer.setInterfaces(fbb.getClass().getInterfaces());
        // 3.设定父类
        enhancer.setSuperclass(fbb.getClass());
        // 4.为增强器设定回调函数
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("你谁呀!");
                return method.invoke(fbb);
            }
        });

        // 5.生成代理对象
        FBB cglib = (FBB) enhancer.create();

        cglib.eat();
        cglib.signature();

    }

因为Cglib提供的动态代理是基于继承的所以第二步设定接口一般可以不写。用过spring的也都知道,其实Cglib的这种动态代理其实就是Spring中Aop的底层的一种实现。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-06T,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档