Java设计模式-代理模式

本文深入探讨了代理模式的概念、优点及应用场景,包括动态代理、保护代理、远程代理等类型,通过抓娃娃机实例演示了静态代理与动态代理的实现,展示了如何通过代理模式提升代码的灵活性与可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

代理模式:

代理模式优点:

代理模式应用:

代理实例:

动态代理

         保护代理

远程代理

上述代码 GitHub 地址:https://github.com/baicun/designPatterns


代理模式:

为一个对象提供替身,进而可以真正的控制这个对象。


代理模式优点:

(1) 体现了 java 面向对象的编程思想;

(2) 在调用者和被调用者之间降低耦合度;

此外不同代理还有不同的优点:

(1) 动态代理:运行时动态的创建代理对象,通过反射机制,利用JDK提供的Proxy类。并将方法调用转发到指定类。

(2) 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。

(3) 远程代理:为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。

(4) 虚拟代理:创建一个消耗资源较多的对象代理服务,可以在一定程度上节省系统的运行开销。

eg: 微博图片在线加载类(传输图片视频等耗费资源,利用虚拟代理)

(5) 缓存代理:为某一操作的结果提供临时的缓存空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。

eg:微博内容加载完成以后,会调用缓存代理,使这部分数据能够保存。方便下次使用。


代理模式应用:

AOP;远程代理RMI;数据库连接...

类图:


代理实例:

简单示例:

//抓娃娃机接口
public interface DollMachine {
    String dollMachineState();
}
//抓娃娃机器接口实现类
public class DollMachineImpl implements DollMachine{
    @Override
    public String dollMachineState() {
        System.out.println("正在获取机器状态,请等待...");
        return "0";
    }
}

测试类:

public class MainTest {
    public static void main(String[] arg){
        //代理前
        DollMachine dollMachineA = new DollMachineImpl();
        String sa = dollMachineA.dollMachineState();
        System.out.println("代理前 机器抓娃娃机状态 = [" + sa + "]");
    }
}

开始代理:

创建一个类,实现基础接口,并且代理已经存在的实现类。

//代理实现类
public class ProxyDollMachine implements DollMachine{

    private DollMachineImpl dollMachineA;

    public ProxyDollMachine(){
        super();
        this.dollMachineA = new DollMachineImpl();
    }

    @Override
    public String dollMachineState() {
        before();
        String ret = dollMachineA.dollMachineState();
        after();
        return ret;
    }

    public void before(){
        System.out.println("代理对象开始");
    }
    public void after(){
        System.out.println("代理对象结束");
    }

}

测试:

public class MainTest {
    public static void main(String[] arg){
        //代理前
        DollMachine dollMachineA = new DollMachineImpl();
        String sa = dollMachineA.dollMachineState();
        System.out.println("代理前 机器抓娃娃机状态 = [" + sa + "]");
        //代理后
        DollMachine dollMachineB = new ProxyDollMachine();
        String sb = dollMachineB.dollMachineState();
        System.out.println("代理后 机器抓娃娃机状态 = [" + sb + "]");
    }
}

这就是spring项目中常见的一个AOP。通过代理我们可以对整个项目中的一些方法进行控制。

通过上面的实例,不难发现编码灵活性不足,当接口“DollMachine”增加一个方法,对应我们的代理类“ProxyDollMachine”也要做相应的改变,代码维护和拓展性比较差。为了解决这个缺陷,下面了解下动态代理!

动态代理

// 方法接口
public interface DollMachine {
    int sum() throws InterruptedException;
    String dollMachineState() throws InterruptedException;
}
//接口实现类
public class DollMachineImpl implements DollMachine {

    @Override
    public int sum() throws InterruptedException {
        System.out.println("返回机器中娃娃的数量");
        Thread.sleep(5000);
        return 100;
    }

    @Override
    public String dollMachineState() throws InterruptedException {
        System.out.println("返回机器的使用状态");
        Thread.sleep(100);
        return "0";
    }
}
// 代理接口的实现类对象
// JDK内置的动态代理Proxy只能代理接口
public class ProxyDollMachine implements DollMachine {

    private DollMachineImpl dollMachine;

    public ProxyDollMachine(){
        this.dollMachine=new DollMachineImpl();
    }

    @Override
    public int sum() throws InterruptedException {
        long start = System.currentTimeMillis();
        int sum = dollMachine.sum();
        long end = System.currentTimeMillis();
        System.out.println("调用耗时:"+(end-start));
        return sum;
    }

    @Override
    public String dollMachineState() throws InterruptedException {
        long start = System.currentTimeMillis();
        String machineState = dollMachine.dollMachineState();
        long end = System.currentTimeMillis();
        System.out.println("调用耗时:"+(end-start));
        return machineState;
    }
}
// 调用处理器,当代理对象调用代理方法的时候,注册在调用处理器中的invoke方法会自动调用。
// 该接口作为代理实例的调用处理者的公共父类,
public class DollMachineInvocationHandler implements InvocationHandler {

    //注册目标对象,该目标对象的方法执行invoke()方法
    private Object target;

    //构造方法
    public DollMachineInvocationHandler(Object target) {
        this.target = target;
    }

    // 方法说明
    // Object proxy:代理对象的引用,proxy变量中保存代理对象的内存地址(这个参数很少用)
    // Method method:目标对象的目标方法。
    // Object[] args:目标对象的目标方法执行的时候所需要实参。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        //执行目标对象中的方法
        Object retValue = null;
        retValue = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println("代理调用耗时:" + (end - start));
        return retValue;
    }
}

测试类:

public class MainTest {
    // JDK内置的动态代理Proxy只能代理接口
    //(如果既想代理接口又想代理抽象类需要使用第三方组件:例如 cglib)
    public static void main(String[] arg) throws InterruptedException {
        DollMachine target = new ProxyDollMachine();
        // 创建动态代理类和实例对象的方法
        /**
         * 第一个参数loader表示代理类的类加载器,
         * 第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),
         * 第三个参数h表示所指派的调用处理程序类。
         */
        DollMachine proxy = (DollMachine) Proxy.newProxyInstance(
                target.getClass().getClassLoader()
                ,new Class[]{DollMachine.class}
                ,new DollMachineInvocationHandler(target));

        // 调用方法
        proxy.dollMachineState();
        proxy.sum();
    }
}

动态代理灵活的实现对目标接口的代理处理,那么如果不同的权限可不可以有不同的处理方式呢?

继续了解保护代理 !

保护代理

控制对一个对象的访问权限,为不同用户不同方法提供不同级别的使用权限。

下面是在 DollMachineInvocationHandler.java 中做的修改。

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        //执行目标对象中的方法
        Object retValue = null;
        // 保护代理思想,针对不同的方法,不同的角色执行权限不同。
        // 当然也可以重新写一个 InvocationHandler 处理器,来区分不同的执行权限
        if(method.getName().equals("dollMachineState")){
            return null;
        }else{
            retValue = method.invoke(target, args);
        }
        long end = System.currentTimeMillis();
        System.out.println("代理调用耗时:" + (end - start));
        return retValue;
    }

远程代理

接口:

//服务端接口:想要实现服务端的方法接口
public interface MyRemote extends Remote {
    // 客户端和服务端首次通信
    String sayHello() throws RemoteException;
    // 查看机器状态
    String dollMachineState() throws RemoteException;
}

 实现类:

//服务端方法
public class MyRemoteSvcImpl extends UnicastRemoteObject implements MyRemote {
    //构造方法
    protected MyRemoteSvcImpl() throws RemoteException {
        super();
    }
    //客户端和服务端首次通信
    @Override
    public String sayHello() throws RemoteException{
        return "Hello World!";
    }

    //返回抓娃娃机器的状态
    @Override
    public String dollMachineState() throws RemoteException {
        return "1";
    }

}

代理类: 

public class ProxyRemote {
    // 代理服务端接口
    MyRemote service;
    public ProxyRemote() throws RemoteException, MalformedURLException, NotBoundException {
        //通过Naming.lookup方法解析ip 查找注册地址,返回
        service = (MyRemote) Naming.lookup("rmi://127.0.0.1:6600/RemoteHello");
    }

}

服务注册:

//RMI服务注册
public class Route {
    public static void main(String[] args) {
        try {
            MyRemote service=new MyRemoteSvcImpl();
            LocateRegistry.createRegistry(6600);
            //IP地址要写本机ip
            Naming.rebind("rmi://10.0.5.27:6600/RemoteHello", service);//绑定开启服务
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println( e.toString());
        }
    }
}

测试类:

public class MainTest {
    public static void main(String[] args) {
        try {
            ProxyRemote proxyRemote = new ProxyRemote();
            // 通过代理类获取代理接口
            String s=proxyRemote.service.sayHello();
            String status=proxyRemote.service.dollMachineState();
            System.out.println("首次通信:" + s);
            System.out.println("返回远程机器状态:" + status);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代码 GitHub 地址:https://github.com/baicun/designPatterns

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值