代理模式

-- 什么是代理模式?
-- 代理模式应用场景?
-- 静态代理
-- JDK动态代理
-- CGLIB动态代理
-- 强制代理

定义

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。

应用场景

访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

三种代理模式

新建一个业务类和业务接口
Subject.java

public interface Subject {
    void request();
}

RealSubject.java

public class RealSubject implements Subject {
    
    @Override
    public void request() {
        System.out.println("访问真实主题方法...");
    }
}

静态代理

ProxySubject.java

public class ProxySubject implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if(realSubject == null){
            realSubject = new RealSubject();
        }
        preRequest();
        realSubject.request();
        postRequest();
    }

    @Override
    public Subject getProxy() {
        return this;
    }

    public void preRequest(){
        System.out.println("访问真实主题之前的预处理。");
    }

    public void postRequest(){
        System.out.println("访问真实主题之后的后续处理。");
    }
}

ProxyTest.java

public class ProxyTest {

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

总结: 通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,这个“包含”其实也说明是可以扩展的,向上面代码所示,在调用request的前后都加上了处理,在一定程度上降低了系统的耦合度。但如果接口层发生了变化,代理对象的代码也要进行维护。接下来来了解下动态代理。动态代理是在实现阶段不用关心代理谁,而在运行阶段 才指定代理哪一个对象。

JDK动态代理

DynamicProxy.java

public class DynamicProxy implements InvocationHandler {

    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public DynamicProxy(Object _obj){
        this.obj = _obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("访问真实主题之前的预处理。");
        Object result = method.invoke(this.obj, args);
        System.out.println("访问真实主题之后的后续处理。");

        return result;
    }
}

ProxyTest.java

public class ProxyTest {

    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicProxy(realSubject);
        ClassLoader cl = realSubject.getClass().getClassLoader();
        Subject proxy1 = (Subject) Proxy.newProxyInstance(cl, new Class[]{Subject.class}, handler);
        proxy1.request();
    }
}

总结: JDK动态代理就是用JDK原有的API传入目标对象的类加载器和接口,再重写代理方法,返回一个代理对象。不需要重写接口了,也不用维护代理类了,但还是需要传入接口。

CGLIB动态代理

public class ProxyFactory implements MethodInterceptor {

    //维护目标对象
    private Object target;

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

    //给目标对象创建一个代理对象
    public Object getProxyInstance() {
        //1.工具类(它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,
        // 和JDK动态代理不一样的是不管是接口还是类它都能正常工作。)
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("访问真实主题之前的预处理。");
        //执行目标对象的方法
        Object returnValue = method.invoke(target, objects);
        System.out.println("访问真实主题之后的后续处理。");
        return returnValue;
    }
}

ProxyTest.java

public class ProxyTest {

    public static void main(String[] args) {
        RealSubject target1 = new RealSubject();
        RealSubject realSubject1 = (RealSubject) new ProxyFactory(target1).getProxyInstance();
        realSubject1.request();
    }
}

总结: CGlib底层是动态地在内存中生成了目标对象的子类的字节码,并生成相应的对象,JDK动态代理实现则是使用的反射机制。CGlib实现动态代理能满足没有接口的目标对象的代理对象实现。

应用

在Spring中,实现AOP的AspectJ框架底层,会对无接口的目标对象采用CGlib,有接口的目标对象则采用JDK动态代理。

下面再拓展一种代理模式:

强制代理

强制代理即是必须通过真实角色找到代理角色。就好像你直接拨打了某个公司老总的电话,然后老总说,你先去我的秘书那里预约一下先吧。
RealSubject2.java

public class RealSubject2 implements Subject {
    private Subject proxy = null;
    @Override
    public Subject getProxy(){
        this.proxy = new ProxySubject();
        return proxy;
    }

    @Override
    public void request() {
        if(this.isProxy()){
            System.out.println("访问真实主题方法...");
        }else{
            System.out.println("请使用指定的代理访问");
        }
    }

    private boolean isProxy() {
        if(this.proxy == null) {
            return false;
        }else{
            return true;
        }
     }
}

ProxyTest.java

public class ProxyTest {

    public static void main(String[] args) {
        RealSubject2 realSubject2 = new RealSubject2();
        realSubject2.request();
        System.out.println("-----分割线------");
        ProxySubject proxySubject = (ProxySubject) realSubject2.getProxy();
        proxySubject.request();
    }
}

输出结果:

请使用指定的代理访问
-----分割线-----
访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。

总结:强制代理模式使得高层模块与真实角色的业务场景耦合度极地;真实角色的修改,在高层模块中完全不用修改。俩者只通过真实角色提供的一个getProxy()接口来交互。在整个实现过程,采用回调思想,在真实角色中调用代理对象的方法,代理对象通过回调来调用真实角色的方法。

posted @ 2019-12-05 10:06  cilieyes  阅读(137)  评论(0编辑  收藏  举报