设计模式(5)——代理模式


代理模式

定义:是一种结构型的代理模式。给某一个对象提供一个代理对象,由代理对象控制对原对象的引用。用户可通过远程代理对象来实现对远程原对象的操作。
模式角色

  • 抽象主题角色:声明了真实主题和代理主题的共同接口,保证了任何使用真实主题的地方都可以使用代理主题。客户端针对抽象主题进行编程。
  • 代理主题角色:内部包含着真实主题对象作为属性成员,用于调用真实主题对象中的操作。不仅如此,还会在此基础上执行一些代理主题独有的操作。
  • 真实主题角色:实现真实的业务操作。

优点

  1. 保护代理可以控制客户操作权限
  2. 虚拟代理使用小对象代表大对象,减少系统资源的消耗,提升运行速度。

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

代理模式类型

根据使用目的进行划分

列举常用几种

  • 远程代理,实现本地调用者与远程被调用者之间的正常交互。
  • 虚拟代理,先用小对象来代替资源消耗巨大的对象,真实对象只在需要的时候才被创建。
  • 保护代理,给被调用者提供访问控制,确认调用者的权限。
  • 智能引用代理,代理对象引用真实对象中的操作时,同时包含着一些代理独有的额外的操作。
  • 同步化代理:使得几个用户同时使用一个真实对象不会发生冲突。

根据实现方式进行划分

  • 静态代理
  • 动态代理

静态代理

实例说明

  1. 保护代理的静态实现

在这里插入图片描述

这里演示某一网站注册用户和未注册用户的权限管理。用户必须注册后才能进行一些功能的操作,否则只有查看功能权限。

AbstractPermission

/**
 * 抽象主题角色
 */
public interface AbstractPermission {
    /**
     * 查看帖子功能
     * 所有用户功能
     */
    void look();
    /**
     * 评论功能
     * 注册用户功能
     */
    void comment();
    /**
     * 发布帖子功能
     * 注册用户功能
     */
    void publish();
    /**
     * 注册功能
     * 未注册用户实现
     */
    void register(boolean flag);
}

RegisteredUser

public class RegisteredUser implements AbstractPermission {
    @Override
    public void look() {
        System.out.println("查看帖子");
    }

    @Override
    public void comment() {
        System.out.println("发出评论");
    }

    @Override
    public void publish() {
        System.out.println("发出帖子");
    }

    @Override
    public void register(boolean flag) {

    }
}

UnRegisteredUser

public class UnRegisteredUser implements AbstractPermission{
    private boolean flag=false;
    private AbstractPermission registerUser=new RegisterUser();
    @Override
    public void look() {
        registerUser.look();
    }

    @Override
    public void comment() {
        if(!flag)
        {
            System.out.println("无账号不能评论,是否立即注册!");
        }else
        {
            registerUser.comment();
        }
    }

    @Override
    public void publish() {
        if(!flag)
        {
            System.out.println("无账号不能发布帖子,是否立即注册!");
        }else
        {
            registerUser.publish();
        }
    }

    @Override
    public void register(boolean flag) {
        this.flag=flag;
    }
}

TestUser

public class TestUser {
    public static void main(String[] args) {
        AbstractPermission user=new UnRegisteredUser();
        user.look();
        user.publish();
        user.comment();
        System.out.println("注册一个账号");
        user.register(true);
        user.look();
        user.publish();
        user.comment();
        /*结果如下:
        查看帖子
		无账号不能发布帖子,是否立即注册!
		无账号不能评论,是否立即注册!
		注册一个账号
		查看帖子
		发出帖子
		发出评论
        *
        */
    }
}

动态代理

如果是静态代理的话,真实角色必须事先存在,一个真实主题角色必须对应一个代理主题角色,且真实主题角色作为代理主题角色的内部成员属性,这将导致系统中类的个数急剧增加。所以动态代理解决了事先不知道真实主题角色的情况下,如何使用代理主题角色的问题。

JDK

使用java.lang.reflect包中的类和接口实现动态代理功能

1、 InvocationHander

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

proxy表示目标类
method表示目标类中的目标方法
args表示目标方法中的入参

其中invoke()方法中表示代理对象需要执行的功能代码,包括
(1)执行目标类中的方法
(2)功能增强,在目标方法调用时,增加一些额外的功能

2、 Method

目标类中的方法对象,可通过反射机制获得

Class clazz = A.class;
// 方法名 方法参数类型 
Method method = clazz.getMethod("ok",String.class);

getMethod(String name, Class<?>... parameterTypes)
作用:获取类对象中的某个方法
参数:(1)目标方法名称 (2)目标方法的参数类型,可变长度类型

得到目标方法对象后,执行该目标方法

method.invoke(new A(),"bai");

invoke(Object obj, Object... args)
参数:(1)目标对象 (2)目标方法的参数值,可变长度类型

3、 Proxy

内部提供了 newProxyInstance() 用于创建代理对象

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

loader:目标对象的类的类加载器
interfaces:目标对象的类实现的接口
h:代理对象完成的功能,自行定义

实例讲解

步骤如下

  1. 创建接口,定义抽象方法
  2. 创建目标类,实现接口
  3. 创建InvocationHandler接口的实现类,在invoke方法中实现代理类的功能
    (1)调用目标方法
    (2)增强功能
  4. 使用Proxy获得代理对象
public interface AbstractPermission {
    /**
     * 查看帖子功能
     * 所有人权限
     */
    void look();
    /**
     * 评论功能
     * VIP可用
     */
    void comment();
    /**
     * 发布帖子功能
     * VIP可用
     */
    void publish();

}
public class RegisteredUser implements AbstractPermission {
    @Override
    public void look() {
        System.out.println("查看帖子");
    }

    @Override
    public void comment() {
        System.out.println("发出评论");
    }

    @Override
    public void publish() {
        System.out.println("发出帖子");
    }
}
public class ProxyHandler implements InvocationHandler {

    private boolean isRegister = false;

    private Object targetObj;
	
	// 动态传入目标对象
    public ProxyHandler(Object obj){
        this.targetObj = obj;
    }

    public void register(){
        this.isRegister = true;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if(!isRegister){
            System.out.println("未注册,无权限执行");
            return null;
        }

        return method.invoke(targetObj,args);
    }
}

public class TestUser {

    public static void main(String[] args) {

        // 1、创建目标对象
        AbstractPermission user = new RegisteredUser();

        // 2、InvocationHandler 实现代理对象增强功能
        InvocationHandler h = new ProxyHandler(user);

        // 3、创建代理对象
        AbstractPermission proxyObj = (AbstractPermission)Proxy.newProxyInstance(user.getClass().getClassLoader(),
                                        user.getClass().getInterfaces(),h);

        proxyObj.publish();

        System.out.println("----------------进行注册---------------");

        ((ProxyHandler) h).register();

        proxyObj.publish();

    }
}

在这里插入图片描述
思考:如何对目标对象中的不同方法做不同的代理增强?

CGLib

cglib是第三方工具库,创建代理对象

实例讲解

参考书籍:
设计模式.刘伟.胡志刚.郭克华.清华大学出版社

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值