Java设计模式之代理模式

Java设计模式之代理模式

代理模式属于结构型设计模式,代理模式是为一个类提供一个替身,以控制对这个类的访问,通过代理类访问被代理类。

代理模式的三种形式

(1):静态代理
(2):动态代理(JDK代理、接口代理)
(3):Cglib代理(动态代理,可以在内存中动态的创建对象)

实现静态代理UML类图

在这里插入图片描述

实现动态代理UML类图

在这里插入图片描述

一、 创建静态代理的步骤

(1):创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
(2):创建一个代理类PhoneProxy 并实现接口

(1)创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
/**
 * @author yly
 * @ClassName ChinaTelecom
 * @Date 2020/2/20 19:28
 * @Version 1.0
 **/
public interface operator {
   public void accessToTheNetwork();
}
/**
 * @author yly
 * @ClassName ChinaTelecom
 * @Date 2020/2/20 19:30
 * @Version 1.0
 **/
public class ChinaTelecom implements operator {
    @Override
    public void accessToTheNetwork() {
        System.out.println("中国电信");
    }
}
(2)创建一个代理类PhoneProxy 并实现operator 接口
/**
 * @author yly
 * @ClassName PhoneProxy
 * @Date 2020/2/20 19:31
 * @Version 1.0
 **/
public class PhoneProxy implements operator {

    private ChinaTelecom chinaTelecom;

    public PhoneProxy(ChinaTelecom chinaTelecom) {
        this.chinaTelecom = chinaTelecom;
    }

    @Override
    public void accessToTheNetwork() {
        System.out.println("代理开始");
        chinaTelecom.accessToTheNetwork();
        System.out.println("代理完成");
    }
}

(3)使用代理对象访问被代理的类
/**
 * @author yly
 * @ClassName demo
 * @Date 2020/2/20 19:33
 * @Version 1.0
 **/
public class demo {
    public static void main(String[] args) {
        PhoneProxy phoneProxy = new PhoneProxy(new ChinaTelecom());
        phoneProxy.accessToTheNetwork();
    }
}
(4)运行结果
代理开始
中国电信
代理完成

静态代理优点:

  • 不修改目标类功能的前提下,能通过代理对象对目标功能进行扩展。

静态代理的缺点:

  • 代理对象需要与被代理对象同时实现一样的接口,所以会有很多代理类
  • 接口增加方法,代理类与被代理类都需要修改代码

二、创建动态代理的步骤与静态代理一致

(1)创建一个operator 接口和实现operator 接口的实体类ChinaTelecom
/**
 * @author yly
 * @ClassName ChinaTelecom
 * @Date 2020/2/20 19:28
 * @Version 1.0
 **/
public interface operator {
   public void accessToTheNetwork();
   public void play(String name);
}
/**
 * @author yly
 * @ClassName ChinaTelecom
 * @Date 2020/2/20 19:30
 * @Version 1.0
 **/
public class ChinaTelecom implements operator {
    @Override
    public void accessToTheNetwork() {
        System.out.println("中国电信");
    }

    @Override
    public void play(String name) {
        System.out.println("玩游戏:"+name);
    }
}

(2)创建一个代理类PhoneProxy ,此处使用JDK动态代理,不需要实现接口
/**
 * @author yly
 * @ClassName PhoneProxy
 * @Date 2020/2/20 19:31
 * @Version 1.0
 **/
public class PhoneProxy {

    private Object object;

    public PhoneProxy(Object object) {
        this.object = object;
    }

    /**
     * @return
     * @CallerSensitive
     * public static Object newProxyInstance(ClassLoader loader, //指定当前目标对象使用的类加载器,获取加载器方法
     *                                       Class<?>[] interfaces,//目标对象实现的接口类型,使用泛型方法确认类型
     *                                       InvocationHandler h) //事情处理,执行目标对象方法时,触发事情处理器方法,将当前执行的目标对象方法作为参数传入
     * throws IllegalArgumentException
     * {}
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK动态代理开始");
                        Object returnVal = method.invoke(object, args);
                        System.out.println("JDK动态代理提交");
                        return returnVal;
                    }
                });
    }
}
(3)使用代理对象访问被代理的类
/**
 * @author yly
 * @ClassName demo
 * @Date 2020/2/21 0:49
 * @Version 1.0
 **/
public class demo {
    public static void main(String[] args) {
        //创建目标对象
        ChinaTelecom chinaTelecom = new ChinaTelecom();
        //创建代理对象
        operator proxyInstance = (operator)new PhoneProxy(chinaTelecom).getProxyInstance();

        //proxyInstance=class com.sun.proxy.$Proxy0内存中生成的代理对象
        System.out.println("proxyInstance="+proxyInstance.getClass());

        proxyInstance.play("王者荣耀");
    }
}
(4)运行结果
proxyInstance=class com.sun.proxy.$Proxy0
JDK动态代理开始
玩游戏:王者荣耀
JDK动态代理提交

三、Cglib代理

Cglib可以使用目标对象子类来进行代理,不需要实现接口,可以弥补静态代理和JDK动态代理的不足,因为静态代理和JDK动态代理都需要目标对象实现一个接口,如果目标对象只是一个单独的对象,没有实现接口,就可以使用Cglib代理。Cglib被广泛的用于aop框架中,例如Spring AOP。

Cglib代理模式注意事项

(1):需要引入cglib的jar文件
(2):在内存中动态创建子类,代理的类不能为final,否则报错。
(3):目标对象方法如果是final或者static时,不会执行目标对象额外的业务方法

Cglib实现步骤

(1):创建目标对象不需要实现接口
(2):创建代理类

(1)创建目标对象不需要实现接口
/**
 * @author yly
 * @ClassName ChinaTelecom
 * @Date 2020/2/21 10:56
 * @Version 1.0
 **/
public class ChinaTelecom {

    public final String play(String name) {
        System.out.println("动态代理cglib,不实现接口");
        return "玩游戏" + name;
    }
}
(2)创建代理类,实现MethodInterceptor接口,重写 intercept()方法
/**
 * @author yly
 * @ClassName PhoneProxy
 * @Date 2020/2/21 10:58
 * @Version 1.0
 **/
public class PhoneProxy implements MethodInterceptor {
    private Object object;

    public PhoneProxy(Object object) {
        this.object = object;
    }

    public Object getProxyInstance() {
        //创建一个工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(object.getClass());
        //设置回调函数
        enhancer.setCallback(this);

        return enhancer.create();
    }

    /**
     * 重写intercept方法,会调用目标对象的方法
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib代理开始");
        Object invoke = method.invoke(object, objects);
        System.out.println("Cglib代理结束");
        return invoke;
    }
}
(3)使用代理对象访问被代理的类
/**
 * @author yly
 * @ClassName demo
 * @Date 2020/2/21 11:06
 * @Version 1.0
 **/
public class demo {
    public static void main(String[] args) {
        ChinaTelecom chinaTelecom = new ChinaTelecom();
        ChinaTelecom proxyInstance =(ChinaTelecom) new PhoneProxy(chinaTelecom).getProxyInstance();
        String play = proxyInstance.play("王者荣耀");
        System.out.println(play);

    }
}
(4)运行结果
Cglib代理开始
动态代理cglib,不实现接口
Cglib代理结束
玩游戏王者荣耀

将目标方法play()用final修饰时,执行结果为

动态代理cglib,不实现接口
玩游戏王者荣耀

代理模式的使用场景

  • 防火墙代理(内网通过代理穿透防火墙,实现对公网的访问)
  • 远程代理(通过远程代理,可以将远程对象当本地对象调用)
  • 缓存代理(请求资源文件时,先到缓存拿数据,如果找到资源则返回,如果找不到资源,再从服务器拿资源)

使用静态代理实现缓存代理

(1)创建File接口
/**
 * @author yly
 * @ClassName Image
 * @Date 2020/2/10 19:02
 * @Version 1.0
 **/
public interface File {
   public File  resource();
}
(2)创建File的实现类ReadFile
/**
 * @author yly
 * @ClassName RealImage
 * @Date 2020/2/10 19:03
 * @Version 1.0
 **/
public class ReadFile implements File {

    private File file;

    private String fileName;

    public ReadFile(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public File resource() {
        System.out.println("缓存文件: " + fileName);
        return file;
    }

    public File getResource(String fileName){
        System.out.println("资源文件: " + fileName);
        return file = new ReadFile(fileName);
    }

}
(3)创建代理类ProxyFile
/**
 * @author yly
 * @ClassName ProxyImage
 * @Date 2020/2/10 19:05
 * @Version 1.0
 **/
public class ProxyFile implements File {

    private ReadFile readFile;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public File resource() {
        if(readFile == null){
            readFile = new ReadFile(fileName);
           return readFile.getResource(fileName);
        }
        return readFile.resource();
    }
}
(4)使用代理对象访问被代理的类
/**
 * @author yly
 * @ClassName ProxyPatternDemo
 * @Date 2020/2/10 19:06
 * @Version 1.0
 **/
public class ProxyPatternDemo {
    public static void main(String[] args) {
        File file = new ProxyFile("file.jpg");
        // 文件将从磁盘加载
        File resource = file.resource();
        System.out.println(resource);

        System.out.println("");
        // 文件从缓存加载
        File resource1 = file.resource();
        System.out.println(resource1);
    }
}
(5)运行结果
资源文件: file.jpg
com.java.designmode.impl.ReadFile@16d3586

缓存文件: file.jpg
com.java.designmode.impl.ReadFile@16d3586
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值