设计模式-代理模式JAVA实现

醉酒当歌 提交于 2020-04-05 15:49:44

代理模式简单说就是对当前已有类中的方法进行前/后置干预的的一种设计模式,目的是在不对现有业务类进行修改的情况下对指定的现有业务在类级别或方法级别进行修改干预。

实现方式目前有两种,一种是静态代理,纯粹基于设计模式通过代码实现。另一种是动态代理,需要通过JDK默认提供的功能和导入CGLIG功能包来增强实现。

首先进行静态代理的实现。

package proxy.staticproxy;

import java.util.List;

import bean.PickDoc;
import bean.PickList;
import bean.PickTask;
import builder.IPickTask;

静态代理的具体实现
public class StaticPickTaskProxy implements IPickTask {

//被代理对象
private IPickTask pickTask;

public StaticPickTaskProxy(IPickTask pickTask) {
    this.pickTask = pickTask;
}

@Override
public List<PickList> getPickList(List<PickDoc> list) {

    System.out.println("前置处理"+pickTask.getClass().getName()+"对象调用前的操作");
    pickTask.getPickList(list);
    System.out.println("后置处理"+pickTask.getClass().getName()+"对象调用前的操作");
    return null;
}

@Override
public List<PickTask> getPickTask(List<PickList> list) {
    // TODO Auto-generated method stub
    return null;
}

}

//静态代理的实际调用
package proxy.staticproxy;

import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;

public class StaticPickTaskProxyMain {

public static void main (String[] args) {

    //使用代理类代替具体的业务类来进行操作
    IPickTask pickTaskProxy = new StaticPickTaskProxy(new SinglePickTask());

    //使用代理类代替具体的业务类来进行操作
    IPickTask pickTaskProxy1 = new StaticPickTaskProxy(new UnionPickTask());

    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

    //非合并拣货
    pickTaskProxy.getPickList(pickDocList);

    //合并拣货
    pickTaskProxy1.getPickList(pickDocList);
}

}

静态代理比较简单,比较容易明白。这里要另外说的是如果前期对要开发的业务设计的好,那么可以一定程度上降低开码的开发量,同时提高可维护性。

比如通常描述静态代理是只能针对某个具体的类中的一个或多个方法来手工实现代理。但因为示例的两个业务实现类SinglePickTask和UnionPickTask都面向IPickTask接口进行实现(采用建造者模式)。这样使得我一个静态代理类可以对这一组业务实现类进行代理。 这就是现实中的好处

动态代理中的JDK默认实现
package proxy.dynamicproxy;

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

public class DynamicJDKProxy implements InvocationHandler {

//被代理对象
private Object obj ;

public DynamicJDKProxy(Object obj) {
    this.obj = obj;
}

//当通过代理类的对象发起对被重写的方法调用时,都会转化为对如下invoke方法的调用
@Override
/**
 * proxy   代理类对象,要实现被代理方法的那个对象,而不是被代类对象,这点不要搞错
 * proxy - the proxy instance that the method was invoked on
 * 
 * 参数method是一个实例,它就是调用在代理实例上的接口方法。声明的
 * 方法对象类是该方法声明的接口,这个接口是所有继承当前method的代理接口的父接口
 * method - the Method instance corresponding to the interface method 
 * invoked on the proxy instance.The declaringclass of the Method 
 * object will be the interface that the method was declared in, which 
 * may be a superinterface of theproxy interface that the proxy 
 * class inherits the method through.
 * 
 * 参数args是包含了代理方法调用中传输的对象数组参数。或者这个接口没有参数。
 * 原始类型的参数被打包在合适的包装类中,如Integer或者Boolean.
 * args - an array of objects containing the values of thearguments 
 * passed in the method invocation on the proxy instance,or null if 
 * interface method takes no arguments.Arguments of primitive types 
 * are wrapped in instances of the appropriate primitive wrapper class,
 * such as java.lang.Integer or java.lang.Boolean.
 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("当前代理对象名称:"+obj.getClass().getSimpleName());
    System.out.println("当前代理方法名称:"+method.getName());
    if(args!=null) {
        for(Object obj: args) {

            System.out.println("参数对象:"+obj.getClass().getSimpleName());
        }
    }
    Object returnObj =null;
    if(method.getName().equals("getPickList")) {
        System.out.println("前置处理");
        returnObj = method.invoke(obj, args);
        System.out.println("后置处理");
    }

    return returnObj;
}

}

//实现调用测试代码
package proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;

public class DynamicJDKProxyMain {

public static void main(String[] arg) {

    IPickTask  singlePickTask = new SinglePickTask();

    IPickTask  unionPickTask = new UnionPickTask();

    //定义拣货单据列表
    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

     //要代理的对象
    InvocationHandler singlePickTaskHandler = new DynamicJDKProxy(singlePickTask);

    InvocationHandler unionPickTaskHandler = new DynamicJDKProxy(unionPickTask);

    //获取类加载器
    ClassLoader loader = singlePickTask.getClass().getClassLoader();
    //获取类接口对象列表
    Class[] interfaces = singlePickTask.getClass().getInterfaces();
     /*
     * classloader,要代理哪个类就用哪个类的加载器来加载要新创建的代理类
     * interfaces,代理类要实现哪些接口,与被代理类对象的一致
     * handler, 方法分发调用的处理器
     * loader - the class loader to define the proxy class
     * interfaces - the list of interfaces for the proxy classto implement
     * h - the invocation handler to dispatch method invocations to
     * 这里是对要被代理的对象动态生成一个新的代理类,
     * 所以做为一个新类,它需要有相应的类加载器,同时这个类是通过反射来构造的,
     * 所以它构造时的方法列表就来自于被被代理对象
     */
    IPickTask proxyPickTask = (IPickTask) Proxy.newProxyInstance(loader, interfaces, singlePickTaskHandler);

    proxyPickTask.getPickList(pickDocList);
}

}
//运行结果
设计模式-代理模式JAVA实现

这里的注释已经比较多,应该比较容易理解。总之就是JDK自动构建了一个与静态代理实现方式一样的代理类,来代理当前的业务类。与静态代理相比的好处是不用一定要手工实现业务类对应接口的所有方法,尤其是在对基于多个接口实现的不同业务类的代理的时候比较好。

CGLIB动态代理实现
首先,这个不是JDK默认带有的功能,需要单独下载JAR包或者运行在springboot的工程下,引入springboot工程中自带的相应实现。本例引用的是cglib-nodep-2.2.2.jar

代理类代码如下
package proxy.dynamicproxy;

import java.lang.reflect.Method;

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

public class DynamicCGLibProxy implements MethodInterceptor {

private Object target;

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

public Object newInstance() {

    //在这里对被代理对象增强生成一个代理对象
    Enhancer enhancer = new Enhancer();                
    enhancer.setSuperclass(target.getClass());                
    enhancer.setCallback(this);                
    return enhancer.create();

}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

    System.out.println("当前代理对象名称:"+obj.getClass().getSimpleName());
    System.out.println("当前代理方法名称:"+method.getName());
    if(args!=null) {
        for(Object arg: args) {

            System.out.println("参数对象:"+arg.getClass().getSimpleName());
        }
    }
    Object returnObj =null;
    if(method.getName().equals("getPickList")) {
        System.out.println("前置处理");
        returnObj = proxy.invokeSuper(obj, args);
        System.out.println("后置处理");
    }

    return returnObj;
}

}

调用演示代码如下
package proxy.dynamicproxy;

import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;

public class DynamicCGLibProxyMain {

public static void main(String[] args) {

    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

    //SinglePickTask 实际业务类
    //pickTaskProxy 生成的代理类象
    IPickTask pickTaskProxy=(SinglePickTask)new DynamicCGLibProxy(new SinglePickTask()).newInstance();

    pickTaskProxy.getPickList(pickDocList);

}

}
//调用效果
设计模式-代理模式JAVA实现
CGLIB代理实现本质上和jdk是一样的,都是新生成一个代理类。区别是两者生成代理类中的方法来源不同。JDK基于取到的对象接口列表在反射时生成相应的方法。而CGLIB通过对当前要被代理的对象生成一个子类对象来解决这个问题

spring中优先使用JDK自带的代理实现方式,当业务方法不基于接口实现时才使用CGLIB方式

另外,在3种实现中均采用通过构造方法传入需要被代理的对象,个人认为这是个比较好的处理方式

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!