1

设计模式——代理模式_大鱼的技术博客_51CTO博客

 2 years ago
source link: https://blog.51cto.com/learningfish/5398750
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
  • 代理模式有不同的形式,主要有三种静态代理、动态代理(JDK 代理、接口代理)和Cglib 代理(可以在内存动态的创建对象,而不需要实现接口,他是 属于动态代理的范畴)

UML类图

设计模式——代理模式_java

类图解析:

Subject:通过接口或抽象类声明真实角色实现的业务方法。

Proxy:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

RealSubject:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

背景介绍:老师生病了,需要另一名老师帮其代课

设计模式——代理模式_代理模式_02

ITeacherDao接口类

public interface ITeacherDao {

    /**
     * 老师上课
     */
    void teach();
}

TeacherDao实现类

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("开始上课...");
    }
}

TeacherDaoProxy代理类

public class TeacherDaoProxy implements ITeacherDao{
    // 被代理对象
    private ITeacherDao iTeacherDao;

    public TeacherDaoProxy(ITeacherDao iTeacherDao) {
        this.iTeacherDao = iTeacherDao;
    }
    
    public TeacherDaoProxy() {
        try {
            iTeacherDao =(ITeacherDao) this.getClass().getClassLoader().loadClass("com.xy.staticproxy.TeacherDao").newInstance();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }


    /**
     * 上课前
     */
    public void preTeach() {
        System.out.println("你们老师生病了, 我来代一节课...");
    }

    @Override
    public void teach() {
        preTeach();
        iTeacherDao.teach();
        afterTeach();
    }


    /**
     * 上课后
     */
    private void afterTeach() {
        System.out.println("下课了, 同学们有缘再见...");
    }
}

Client测试类

public class Client {
    public static void main(String[] args) {
        // 生病的老师, 被代理对象
        ITeacherDao patientTeacher = new TeacherDao();

        // 新老师, 代理对象
        //ITeacherDao proxyTeacher = new TeacherDaoProxy(patientTeacher);
        TeacherDaoProxy proxyTeacher = new TeacherDaoProxy();
        
        // 老师代课
        proxyTeacher.teach();
    }
}

实现效果:

设计模式——代理模式_目标对象_03

静态代理优缺点

  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类一旦接口增加方法,目标对象与代理对象都要维护

动态代理模式的基本介绍

  • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象动态代理也叫做: JDK代理、接口代理。
设计模式——代理模式_java_04

ITeacherDao接口类

public interface ITeacherDao {

    /**
     * 老师上课
     */
    void teach();
}

TeacherDao实现类

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("开始上课...");
    }
}

ProxyFactory代理类

public class ProxyFactory{
    // 目标对象
    private Object target;

    // 构造器,对target进行初始化
    public ProxyFactory(Object target) {
        this.target = target;
    }


    /**
     * 方法执行前
     */
    public void preMethod() {
        System.out.println("方法执行前...");
    }

    /**
     * 方法执行后
     */
    private void afterMethod() {
        System.out.println("方法执行后...");
    }

    // 给目标对象生成代理对象
    public Object getProxyInstance() {

        /**
         * ClassLoader loader, 指定类加载器
         * Class<?>[] interfaces,目标对象(被代理对象),使用泛型方式确认类型
         * InvocationHandler h 事件处理执行目标对象的方法时,会触发时间处理器的方法
         * 会把当前执行的目标对象方法作为参数传入
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    preMethod();
                    // 反射机制调用目标对象方法
                    Object invoke = method.invoke(target, args);
                    afterMethod();
                    return invoke;
                });
    }
}

Test用于测试动态代理效果

public interface Test {
    void test();
}

Client测试类

public class Client {
    public static void main(String[] args) {
        // 被代理对象
        TeacherDao target = new TeacherDao();

        // 创建代理对象
        ITeacherDao proxyITeacher = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
        proxyITeacher.teach();

        System.out.println("-------------");

        Test test = (Test)new ProxyFactory((Test) () ->
                System.out.println("this is a test")
        ).getProxyInstance();
        test.test();
    }
}

测试结果:

设计模式——代理模式_代理模式_05

cglib代理

Cglib代理模式的基本介绍

  • 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,即Cglib代理

  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截

  • 在AOP编程中如何选择代理模式:

    • 目标对象需要实现接口,用JDK代理
    • 目标对象不需要实现接口,用Cglib代理
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

设计模式——代理模式_java_06

导入cglib依赖jar包

设计模式——代理模式_目标对象_07
`ProxyFactoryd代理工厂

public class ProxyFactory implements MethodInterceptor {
    //  维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }


    /**
     * 返回一个target代理对象
     * @return
     */
    public Object getProxyInstance() {
        // 1、创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(target.getClass());
        // 3、设置回调函数
        enhancer.setCallback(this);
        // 4、返回子类,即代理对象
        return enhancer.create();
    }

    /**
     * 方法执行前
     */
    public void preMethod() {
        System.out.println("方法执行前...");
    }

    /**
     * 方法执行后
     */
    private void afterMethod() {
        System.out.println("方法执行后...");
    }


    // 重写拦截方法,调用目标方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        preMethod();
        // 使用反射
        //Object value = method.invoke(target, objects);
        
        // 使用fastClass
        Object value = methodProxy.invokeSuper(o, objects);
        afterMethod();
        return value;
    }
}

TeacherDao和Test(被代理类)

public class TeacherDao {
    public void teach() {
        System.out.println("老师授课中...");
    }
}

class Test{
    public void test() {
        System.out.println("this is a teat...");
    }
}

Client测试类

public class Client {
    public static void main(String[] args) {
        // 被代理类
        TeacherDao target = new TeacherDao();

        // 创建对象代理
        TeacherDao proxyTeacher =(TeacherDao) new ProxyFactory(target).getProxyInstance();

        // 执行方法
        proxyTeacher.teach();

        System.out.println("----------");

        // 测试类
        ((Test)new ProxyFactory(new Test()).getProxyInstance()).test();
    }
}

测试结果:

设计模式——代理模式_目标对象_08

几种常见的代理模式介绍——几种变体

  • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
  • 缓存代理:比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
  • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
  • 同步代理:主要使用在多线程编程中,完成多线程间同步工作

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK