设计模式之代理模式二

2018-08-06  本文已影响13人  小白小白啦

设计模式之代理模式一

现在只是对小汽车进行代理,如果要实现对火车,自行车的代理是不是需要创建火车代理类,自行车代理类等,太麻烦了。可以通过动态代理进行实现。

JDK动态代理

jdk动态代理
jdk动态代理

创建一个TimeHandler的时间处理类

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

/**
 * Create by fengguofei
 * Date: 2018/8/5
 * Time: 15:16
 */
public class TimeHandler implements InvocationHandler {

    private Object target;

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    /**
     * @param proxy 被代理对象
     * @param method 被代理对象的方法
     * @param args 方法的参数
     * @return 方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶。。。");
        method.invoke(target);
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶。。。 汽车行驶时间:" + (endtime - starttime) + "毫秒!");;
        return null;
    }
}

创建Test测试类

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

/**
 * Create by fengguofei
 * Date: 2018/8/5
 * Time: 15:21
 */
public class Test {

    /**
     * jdk动态代理测试
     * @param args
     */
    public static void main(String[] args) {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Class<?> cls = car.getClass();

        /**
         * loader 类加载器
         * interfaces 实现接口
         * h InvocationHandler
         */
        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
        m.move();
    }
}

输出

汽车开始行驶。。。
汽车行驶中。。。
汽车结束行驶。。。 汽车行驶时间:607毫秒!

所谓Dynamic Proxy是这样一种class:
它是在运行时生成的clas
该class需要实现一组interface
使用动态代理类时,必须实现InvocationHandler接口

动态代理类实现步骤:
1 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2 创建被代理的类以及接口
3 调用Proxy的静态方法,创建一个代理类 newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h)
4 通过代理调用方法

cglib动态代理

JDK动态代理 CGLIB动态代理
只能代理实现了接口的类 针对类来实现代理的
没有实现接口的类不能实现JDK的动态代理 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用

创建Train类

public class Train {
    public void move(){
        System.out.println("火车行驶中。。。");
    }
}

创建CglibProxy类

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

import java.lang.reflect.Method;

/**
 * Create by fengguofei
 * Date: 2018/8/5
 * Time: 18:06
 */
public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz){
        //设置创建子类的类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);

        return enhancer.create();
    }

    /**
     * 拦截所有目标类方法的调用
     * @param o 目标类的实例
     * @param method 目标方法的反射对象
     * @param objects 方法的参数
     * @param methodProxy 代理类的实例
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("日志开始。。。");
        //代理类调用父类的方法
        methodProxy.invokeSuper(o, objects);
        System.out.println("日志结束。。。");
        return null;
    }
}

创建测试类

public class Client {

    public static void main(String[] args) {

        CglibProxy proxy = new CglibProxy();
        Train t = (Train)proxy.getProxy(Train.class);

        t.move();
    }
}

输出

日志开始。。。
火车行驶中。。。
日志结束。。。

JDK动态代理模拟

动态代理实现思路:
实现功能:通过Proxy的newProxyInstance返回代理对象
1 声明一段源码(动态产生代理)
2 编译源码(JDK Compiler API),产生新的类(代理类)
3 将这个类load到内存当中,产生一个新的对象(代理对象)
4 return 代理对象

创建Proxy类

package pattern.proxy.simulateProxy;

import org.apache.commons.io.FileUtils;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.tools.JavaCompiler.CompilationTask;

/**
 * Create by fengguofei
 * Date: 2018/8/5
 * Time: 19:55
 */
public class Proxy {

    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
        String rt = "\r\n";
        String methodStr = "";
        for(Method m:infce.getMethods()){
            methodStr += "    @Override" + rt +
            "   public void " + m.getName() +"() {" + rt +
            " try{" + rt +
            " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
            " h.invoke(this, md);" + rt +
            " }catch(Exception e){e.printStackTrace();}" + rt +
            "   }";
        }
        String str =
        "package pattern.proxy.simulateProxy;" + rt +
        "import  pattern.proxy.simulateProxy.InvocationHandler;" + rt +
        "import  java.lang.reflect.Method;" + rt +
        "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                "    public $Proxy0(InvocationHandler h) {" + rt +
                "        super();" + rt +
                "        this.h = h;" + rt +
                "    }" + rt +
                " private InvocationHandler h;" + rt +
               methodStr + rt +
                "}";
        //产生代理类的java文件
        String filename = System.getProperty("user.dir") + "\\bin\\pattern\\proxy\\simulateProxy\\$Proxy0.java";
        System.out.println(filename);
        File file = new File(filename);
        FileUtils.writeStringToFile(file, str);

        //编译
        //拿到编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //文件管理者
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        //获取文件
        Iterable units = fileMgr.getJavaFileObjects(filename);

        List<String> options = new ArrayList<String>();
        options.add("-encoding");
        options.add("UTF-8");
        //编译任务
        CompilationTask t = compiler.getTask(null, fileMgr, null, options, null, units);
        //进行编译
        t.call();
        fileMgr.close();
        //load到内存中
//        ClassLoader cl = ClassLoader.getSystemClassLoader();
//        System.out.println(cl.toString());//sun.misc.Launcher$AppClassLoader@18b4aac2
        DiskClassLoader diskLoader = new DiskClassLoader(System.getProperty("user.dir") + "\\bin\\pattern\\proxy\\simulateProxy");
        Class c = diskLoader.loadClass("pattern.proxy.simulateProxy.$Proxy0");
        System.out.println(c.getName());
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        return ctr.newInstance(h);
    }
}

主要代码是Proxy的创建,建议看一下慕课上面的课程,很清晰,主要是字符串的拼接,编译,加载到内存,然后是创建代理类实例。但是在编写代码的时候一模一样就是不能运行,一直报错,无法把编译后的$Proxy0.class加载到内存中,可能是jdk版本或者其他问题。后来参考一看你就懂,超详细java中的ClassLoader详解创建了DiskClassLoader类加载器,就可以加载到内存了。其他的代码就参考github,不在粘贴了。
全部演示代码在github

上一篇下一篇

猜你喜欢

热点阅读