代理模式

1代理模式

代理模式的运用很多,王宝强的经纪人就是王宝强的代理。

一个不懂技术的老板想管控技术部门,那么他会找一个听他话的技术总监作为代理。

被代理对象又叫做委托对象。

1.1静态代理

---------------------------------------------------------------------------------------------------

1.1.1定义接口

    
public interface ICoder {

    /**
     * 实现业务需求
     * @param demandName 需求名
     */
    public void implDemands(String demandName);

}

这个接口是一个码农接口,主要任务是实现业务需求

1.1.2实现类

public class JavaCoder implements ICoder {

    private String name;

    @Override
    public void implDemands(String demandName) {
        System.out.println(name + " implemented demand:《" + demandName + "》 in JAVA!");
    }

    public JavaCoder(String name) {
        this.name = name;
    }
}

public class PythonCoder implements ICoder {

    private String name;

    @Override
    public void implDemands(String demandName) {
        System.out.println(name + " implemented demand:《" + demandName + "》 in Python!");
    }

    public PythonCoder(String name) {
        this.name = name;
    }
}

1.1.3代理类

public class PMProxy implements ICoder {
    private ICoder iCoder;

    public PMProxy(ICoder iCoder) {
        this.iCoder = iCoder;
    }

    @Override
    public void implDemands(String demandName) {
        System.out.println("需求调研");
        System.out.println("需求分析");
        System.out.println("写需求文档");
        iCoder.implDemands(demandName);
    }
}

1.1.4客户端

public class Client{
    public static void main(String[] args) {
        
        ICoder coder1 = new JavaCoder("黄文俊");
        ICoder coder2 = new JavaCoder("韩琦");

        coder2.implDemands("SOS定位");
        
        ICoder proxy = new PMProxy(coder1);
        proxy.implDemands("工单导入");
    }
}
 

其实了解过装饰模式的人很可能就会发现静态代理其实和装饰着模式是一样一样的。学习设计模式不能死脑经,我们的思路是代理,即使它的实现和装饰相同但是他的用途和目的不仅仅是为了装饰。而是代理。

这就是静态代理模式。

如果一个系统代码中有上百个对象需要被代理,那么你就要写上百个代理类,如此静态代理不是解决方案。

1.2动态代理

有一个需求就是,需要给每个方法计算执行时间,因为有的方法我们需要知道他的性能如何,因此这种需求是合理的。或者某些操作数据库的方法,需要执行事务。因此在方法的开头和结尾有很多重复的相同代码会出现。如果有一百个方法都要做相同的事情,那么我们只能在一百个方法上写相同代码。代码和业务逻辑代码混合起来烟花缭乱,日后不好维护,而且写这么多重复代码,凡是一个有追求得人都会想办法解决。解决之道就是AOP编程,而AOP编程的基础正是动态代理。

动态代理分为以下两种

1.2.1 jdk动态代理

JDK的动态代理需要用到一个代理类和委托类之间的中介类,这个类叫做动态代理类

复用以上的ICoder,JavaCoder,PythonCoder,然后编写代理类

public class CoderDynamicProxy implements InvocationHandler {

    private ICoder coder;

    public CoderDynamicProxy(ICoder coder){
        this.coder = coder;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("需求调研");
        System.out.println("需求分析");
        System.out.println("写需求文档");
        Object result=method.invoke(coder,args);
        return result;
    }
}
public static void main(String[] args) {
    ICoder coder = new JavaCoder("肖世伟");
    InvocationHandler handler = new CoderDynamicProxy(coder);
    //获取类加载器
    ClassLoader cl = coder.getClass().getClassLoader();
    //动态产生一个代理类
    ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
    //通过代理类,执行doSomething方法;
    proxy.implDemands("导出excel表");
}
执行结果如下



如果只是使用JDK动态代理那么到这里就足够了,但是欲求更多还需知道更多。如:

1)代理调用过程

将main方法和代理类的invoke方法都打上断点执行看看过程:

其实main方法执行到代理对象执行方法的时候就进入到了代理对象的invoke方法中:

proxy.implDemands("导出excel表");
这个proxy对象是由
(ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
生成的,那么他是如何生成的呢?

2)源码


在生成代理对象的时候我们传入了三个参数进去,第一个是委托的加载器,第二是委托的接口集,第三是中介代理对象,委托作为代理对象的一个成员变量。然后进入到JDK源码中:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone(); 

final SecurityManager sm = System.getSecurityManager(); 

  if (sm != null) { 

      checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 

 } 

Class<?> cl = getProxyClass0(loader, intfs); 

try

  if (sm != null) { 

      checkNewProxyPermission(Reflection.getCallerClass(), cl); 

 } 

  final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { .............................................. }}

通过委托的加载器和接口集得到委托的反射(类对象),然后得到构造器,最后通过构造器结合中介代理类得到代理对象。源码只要知道大概意思就行了,不需要钻牛角尖非要把每一个细节看完。到这里就行了

当代理执行委托的方法后其实方法和参数被传到了中介代理中的invoke中执行


查看代理对象的类文件:

public final class TestProxyGen extends Proxy implements ICoder {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public TestProxyGen(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void implDemands(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("b_代理模式.动态代理.ICoder").getMethod("implDemands", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们看到 代理类继承了Proxy类且实现了ICoder接口。并实现了ICoder接口的方法

方法体内执行的确是:

super.h.invoke(this, m3, new Object[]{var1});

即将自己(代理),方法,参数列表,传进了中介代理的invoke中。

如此就明白了为什么代理执行接口的方法会进中介类的invoke中去了


1.2.2 CGLib动态代理

上面我们看到JDK生成的代理对象继承了Proxy,而Java是单继承的,并且代理类也实现了ICoder接口。也就是说代理类和委托都是实现同一个接口的。JDk动态代理要求委托对象必须实现接口,而实际场景中并不是所有类都要实现接口,诸多的不便。因此spring这样的大型框架是不用JDK的代理的。SPRING著名的AOP编程基础是代理模式,那么他是怎么实现代理的呢?他是使用的CDLib动态代理

CGLib动态代理使用十分简单

注意点:

1、CGLib使用中委托类不能是final修饰的,因为CGLib生成的代理类是委托(被代理类)的子类

2、final修饰的方法不会被切入;因为委托的方法被final后就不能被重写了

3、如果委托的构造函数不是默认空参数的,那么在使用Enhancer类create的时候,选择create(java.lang.Class[] argumentTypes, java.lang.Object[] arguments) 方法。

public class JavaCoder implements ICoder {

    private String name;

    @Override
    public void implDemands(String demandName) {
        System.out.println(name + " implemented demand:" + demandName + " in JAVA!");
    }

    public JavaCoder(String name) {
        this.name = name;
    }

    public JavaCoder() {
    }
}

里面的接口可要可不要,反正代理类生成的将是JavaConder的子类


创建CGlib的代理工厂类

public class CGLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before..............");
        Object result=methodProxy.invokeSuper(o, objects);
        System.out.println("after..............");
        return result;
    }
    /**
     * 创建代理对象方法
     *
     * @param targetClass   代理对象的类对象
     * @param args          对应的构造器参数类型
     *
     *                          例:有构造器如下
     *                          public Person(name,age){...} nameString.class ageint.class
     *                          写入name的类型与age的类型
     *
     *                          则:new Class[]{String.class,int.class}
     *
     * @param argsValue     对应的构造器参数值
     *
     *                          :如此创建对象 new Person("name",23) 用以下方式传入:new Object[]{"name",23}
     *
     * @param <T>           <泛型方法>
     * @return              返回跟代理对象类型
     */
    public <T> T getInstance(Class<T> targetClass,Class[] args,Object[] argsValue){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);//设置父类的反射
        enhancer.setCallback(this);
        return (T) enhancer.create(args,argsValue);
    }

    /**
     * 创建代理对象方法
     *
     * @param targetClass   代理对象的类对象
     * @param <T>           <泛型方法>
     * @return              返回跟代理对象类型
     */
    public <T> T getInstance(Class<T> targetClass){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }
}

main方法测试

public static void main(String[] args) {
    CGLibProxy cgLibProxy=new CGLibProxy();//代理中介类,拦截被代理对象的行为
    Class[] classes={String.class};
    String[] prams={"田辉"};
    JavaCoder javaCoderProxy= cgLibProxy.getInstance(JavaCoder.class,classes,prams);
    javaCoderProxy.implDemands("方案报警");
}

以上就是CGLib的用法了,但是在实际情况之中我们还可能会用到很多CGlib的其他用法。下面就具体针对各种方法进行详解

(一)单回调

复用上面的JavaCoder

public class CGLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before..............");
        Object result=methodProxy.invokeSuper(o, objects);
        System.out.println("after..............");
        return result;
    }
}
public static void main(String[] args) {
    demo02.CGLibProxy cgLibProxy=new demo02.CGLibProxy();//代理中介类,拦截被代理对象的行为
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(JavaCoder.class);//设置父类
    enhancer.setCallback(cgLibProxy);//设置回调,即cgLibProxy类,因为回调方法在此类中定义
    JavaCoder javaCoderProxy= (JavaCoder) enhancer.create();
    javaCoderProxy.implDemands("上传");
}

这个和上面的其实一样,只是上面的封装了一下工厂方法getInstance()

(二)多回调

添加一个中介代理类

public class CGLibProxy2 implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before2..............");
        Object result=methodProxy.invokeSuper(o, objects);
        System.out.println("after2..............");
        return result;
    }
}

public static void main(String[] args) {

    CGLibProxy cgLibProxy=new CGLibProxy();//代理中介类,拦截被代理对象的行为
    CGLibProxy2 cgLibProxy2=new CGLibProxy2();//代理中介类,拦截被代理对象的行为

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(JavaCoder.class);//设置父类
    //enhancer.setCallback(cgLibProxy);//设置回调,即cgLibProxy类,因为回调方法在此类中定义
    enhancer.setCallbacks(new Callback[]{cgLibProxy,cgLibProxy2});

    enhancer.setCallbackFilter(new CallbackFilter() {//用过滤器来判断是使用哪个中介代理类
        @Override
        public int accept(Method method) {
            if (method.getName().equals("toString")) {
                return 0;
            } else {
                return 1;
            }
        }
    });

    JavaCoder javaCoderProxy= (JavaCoder) enhancer.create();
    javaCoderProxy.implDemands("上传");

}



(三)不处理

除了两个代理中介类以外还可以加入不处理,利用:

Callback noopCb = NoOp.INSTANCE;


(四)固定值

public class ProxyFixedValue implements FixedValue {
    @Override
    public Object loadObject() throws Exception {
        return "Fixed Value";
    }
}

(五)懒加载

public interface LazyLoader extends Callback {
    Object loadObject() throws Exception;
}
public interface Dispatcher extends Callback {
    Object loadObject() throws Exception;
}

定义上面两个接口

其中,LazyLoader只有在第一次用到的时候才会被加载。而Dispathcer会在每次用到的时候都会触发。假定有个学生类,学术类,学术类包含2门课的课程表对象,分别是英语课程表和数学课程表,看代码:



1.3自己模拟实现动态代理

-----------------------------------------------------------------------------------

































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值