设计模式之代理模式

Posted binaway

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之代理模式相关的知识,希望对你有一定的参考价值。

什么是代理模式?

代理模式的定义为:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

 

类图结构

技术分享图片

 

代理的几种实现方式

代理模式的实现方式可以分为静态代理和动态代理两大类。Java中动态代理的实现方式又分为两种,jdk动态代理和cglib动态代理。

 

1、静态代理

/**
 * 接口
 */
interface Subject {
    public void dosomething();
}

/**
 * 真实主题类
 */
class RealSubject implements Subject {
    // 真实主题类的核心操作
    @Override
    public void dosomething() {
        System.out.println("真实主题需要做的事情");
    }
}

/**
 * 代理类
 */
class ProxySubject implements Subject {
    // 真实主题类作为代理类的一个成员属性
    private RealSubject realSubject;
    
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
    
    // 整个业务的全部操作由代理类实现
    @Override
    public void dosomething() {
        this.doBefore();
        this.realSubject.dosomething();
        this.doAfter();
    }
    public void doBefore() {
        System.out.println("做准备工作");
    }
    public void doAfter() {
        System.out.println("做结束工作");
    }
}

public class TestProxy {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject subject = new ProxySubject(realSubject);
        subject.dosomething();
    }
}

静态代理优缺点

优点:实现了在不修改目标对象代码的情况下,可以对目标对象进行功能补充,这也是代理模式的精髓。

缺点:每个代理类只能负责一个特定的主题类的代理操作,如果主题类很多的话,则必须编写大量的代理类。

 

2、动态代理——JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 接口
 */
interface Subject {
    public void dosomething();
}

/**
 * 真实主题类
 */
class RealSubject implements Subject {
    // 真实主题类的核心操作
    @Override
    public void dosomething() {
        System.out.println("真实主题需要做的事情");
    }
}

/**
 * 动态代理类
 */
class MyInvocationHandler implements InvocationHandler {

    // 目标对象
    private Object target;
    
    /**
     * 绑定业务对象,并返回一个代理类对象
     * @param target
     * @return 目标对象的代理类对象
     */
    public Object bind(Object target) {
        this.target = target;
        
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), this);
    }
    
    /**
     * 代理对象执行的完整操作
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        System.out.println("核心业务操作之前的准备操作");
        // 调用核心业务操作
        result = method.invoke(this.target, args);
        System.out.println("核心业务操作之后的结束操作");
        return result;
    }
}
 

public class TestProxy {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        MyInvocationHandler proxy = new MyInvocationHandler();
        Subject subject = (Subject) proxy.bind(realSubject);
        subject.dosomething();
    }
}

JDK动态代理的优缺点

优点:完美实现动态代理操作。

缺点:唯一的遗憾是如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

 

3、动态代理——CGLIB动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 真实主题类
 */
class RealSubject {
    // 真实主题类的核心操作
    public void dosomething() {
        System.out.println("真实主题需要做的事情");
    }
}

class SubjectCglib implements MethodInterceptor {

    // 目标类
    private Object target;
    
    /**
     * 绑定目标对象,动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理对象
     * @param target
     * @return 代理对象
     */
    public Object getInstance(Object target) {
        this.target = target;
        
        // 增强器,动态代码生成器
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);
        // 设置生成类的父类类型
        enhancer.setSuperclass(this.target.getClass());
        // 动态生成字节码并返回代理对象
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("核心业务操作之前的准备操作");
        Object result = methodProxy.invoke(object, args);
        System.out.println("核心业务操作之后的结束操作");
        return result;
    }
}

public class TestProxy {
    public static void main(String[] args) {
        SubjectCglib subjectCglib = new SubjectCglib();
        RealSubject subject = (RealSubject) subjectCglib.getInstance(new RealSubject());
        subject.dosomething();
    }
}

 CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

 

参考:

1、https://www.cnblogs.com/daniels/p/8242592.html

2、https://www.cnblogs.com/best/p/5679656.html

 

以上是关于设计模式之代理模式的主要内容,如果未能解决你的问题,请参考以下文章

Spring之代理模式

#yyds干货盘点# 设计模式之代理模式:动态代理

设计模式之代理模式(Proxy)详解及代码示例

代理模式之静态代理实现代码

设计模式之代理模式

设计模式之代理模式