揭秘设计模式之代理模式

Posted 醉酒的小男人

tags:

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

什么是代理模式

在生活中这种模式到处可见,比如租房中介、售票黄牛、婚介所、经纪人、快递、媒婆、事务代理、非侵入式日志监听等都是代理模式的实际体现。使用代理模式主要是有两个目的:一是保护目标对象,而是增强目标对象。

代理模式定义

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

通用写法

代理主题角色

package general;
/**
 * 代理主题角色
 */
public interface ISubject {
    void request();
}

 真实主题角色

package general;

/**
 * 真实主题角色
 */
public class RealSubject implements ISubject {

    @Override
    public void request() {
        System.out.println("调用真实主题角色");
    }
}

 代理主题角色

package general;
/**
 * 代理主题角色
 */
public class Proxy implements ISubject {

    private ISubject subject;

    public Proxy(ISubject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {
        before();
        subject.request();
        after();
    }

    private void before() {
        System.out.println("===============方法前执行===============");
    }

    private void after() {
        System.out.println("===============方法后执行===============");
    }
}

测试类

package general;
public class GeneralTest {

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.request();
    }

}

 结果:

静态代理

举一个现实的例子,有些人到了结婚年龄,父母着急白了头,想各种方法到处相亲。可以用代码来实现以下。

创建IPerson类

package staticproxy;

public interface IPerson {
    void findLove();
}

儿子王五要找对象,实现IPerson类:

package staticproxy;
public class WangWu implements IPerson{

    @Override
    public void findLove() {
        System.out.println("儿子要求女孩子优秀颜值高等");
    }
}

朋友李四要替王五找对象,实现IPerson类

package staticproxy;

public class LiSi implements IPerson {

    private WangWu wangWu;

    public LiSi(WangWu wangWu) {
        this.wangWu = wangWu;
    }

    @Override
    public void findLove() {
        System.out.println("李四开始物色");
        wangWu.findLove();
        System.out.println("开始交往了");
    }
}

测试类

package staticproxy;

public class StaticProxyTest {
    public static void main(String[] args) {
        LiSi liSi = new LiSi(new WangWu());
        liSi.findLove();
    }
}

结果

JDK动态代理

静态代理的服务对象比较单一,李四只能给王五服务,能不能有一种机构有一种通用的能力为别人服务介绍对象的。

创建代理类

package jdkproxy;
import staticproxy.IPerson;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 媒婆类
 */
public class JdkMeiPoProxy implements InvocationHandler {

    private Object target;

    public IPerson instance(Object target){
        this.target = target;
        Class<?> clazz = target.getClass();
       return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
       Object result = method.invoke(target,args);
        after();
        return result;
    }

    private void before() {
        System.out.println("媒婆已经开始收集需求了,开始寻找中");
    }

    private void after() {
        System.out.println("双方同意,开始交往");
    }
}

创建赵六实现IPerson类

package jdkproxy;
import staticproxy.IPerson;

public class ZhaoLiu implements IPerson {

    @Override
    public void findLove() {
        System.out.println("赵六要求有车有房");
    }
}

 测试类

package jdkproxy;
import staticproxy.IPerson;
import staticproxy.WangWu;

public class JdkProxyTest {
    public static void main(String[] args) {
        JdkMeiPoProxy jdkMeiPoProxy = new JdkMeiPoProxy();
        IPerson wangwu = jdkMeiPoProxy.instance(new WangWu());
        wangwu.findLove();
        System.out.println("======================================");
        IPerson zhaoliu = jdkMeiPoProxy.instance(new ZhaoLiu());
        zhaoliu.findLove();
    }
}

结果

注意:jdk是面向接口的

cglib动态代理

创建被代理类

package cglibproxy;
/**
 * 单身客户
 */
public class Customer {

    public void findLove(){
        System.out.println("长的大方漂亮乐观");
    }
}

创建代理类

package cglibproxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibMeiPoProxy implements MethodInterceptor {

    public Object getInstance(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        //把被代理类设置成父类
        enhancer.setSuperclass(clazz);
        //设置拦截器CglibMeiPoProxy
        enhancer.setCallback(this);
        //创建并返回代理对象
        return enhancer.create();
    }

    /**
     *
     * @param sub cglib生成的代理对象
     * @param method 被代理对象方法
     * @param objects 方法入参
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(sub,objects);
        after();
        return obj;
    }

    private void before() {
        System.out.println("我是媒婆,现在开始给你找对象了,根据你的要求");
        System.out.println("开始查找对象");
    }

    private void after() {
        System.out.println("如果合适的话,牵手成功");
    }
}

测试类

package cglibproxy;

public class CglibMeiPoTest {
    public static void main(String[] args) {
        Customer customer = (Customer) new CglibMeiPoProxy().getInstance(Customer.class);
        customer.findLove();
    }
}

结果

JDK和CgLib动态代理区别

1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

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

揭秘设计模式之门面模式

揭秘设计模式之委派模式

揭秘设计模式之单例模式

揭秘设计模式之模板方法

sping揭秘12SpringAOP的实现机制

揭秘设计模式之工厂模式