代理模式

168 阅读2分钟

静态代理

package proxy.statical;

import proxy.Coder;
import proxy.Person;

public class RobotProxy implements Person {

    private Coder coder;

    public RobotProxy(Coder coder) {
        this.coder = coder;
    }

    @Override
    public void cleaning() {
        System.out.println("robot cleaning 准备");
        coder.cleaning();
        System.out.println("robot cleaning 完毕");
    }

    @Override
    public void cooking() {
        System.out.println("robot cooking 准备");
        coder.cooking();
        System.out.println("robot cooking 完毕");
    }
}

测试

package proxy.statical;

import proxy.Coder;

public class StaticalTest {

    public static void main(String[] args) {
        RobotProxy robotProxy = new RobotProxy(new Coder());
        robotProxy.cleaning();
        robotProxy.cooking();
    }

}

动态代理

jdk

package proxy.dynamic.jdk;

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

public class JDKProxy implements InvocationHandler {

    private Object target;

    public Object getJDKProxy(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("jdk动态代理,开始");
        Object invoke = method.invoke(target, args);
        System.out.println("jdk动态代理,结束");
        return invoke;
    }
}
package proxy.dynamic.jdk;

import proxy.Coder;
import proxy.Person;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

public class JdkProxyTest {

    public static void main(String[] args) {
        JDKProxy jdkProxy = new JDKProxy();
        Person person = (Person) jdkProxy.getJDKProxy(new Coder());
        person.cleaning();
        person.cooking();

        viewSourceCode();
    }

    /**
     * 查看动态代理动态生成的代码
     */
    public static void viewSourceCode() {
        byte[] proxyClass = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        try {
            FileOutputStream fos = new FileOutputStream("E://$Proxy.class");
            fos.write(proxyClass);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

cglib

需要导入俩包

  • asm-7.2.jar
  • cglib-3.3.0.jar
package proxy.dynamic.cglib;

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 CglibProxy implements MethodInterceptor {

    private Object target;

    public Object getCglibProxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调
        enhancer.setCallback(this);
        //创建并返回代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib动态代理,开始");
        Object invoke = method.invoke(target, objects);
        System.out.println("cglib动态代理,结束");
        return invoke;
    }
}

测试

package proxy.dynamic.cglib;

import proxy.Coder;
import proxy.Person;

public class CglibProxyTest {

    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Person person = (Person) cglibProxy.getCglibProxy(new Coder());
        person.cleaning();
        person.cleaning();
    }

}

手写实现jdk动态代理

package proxy.custom;

import proxy.custom.source.MyClassLoader;
import proxy.custom.source.MyInvocationHandler;
import proxy.custom.source.MyProxy;

import java.lang.reflect.Method;

public class CustomProxy implements MyInvocationHandler {

    private Object target;

    public Object getCustomProxy(Object target) {
        this.target = target;
        return MyProxy.newProxyInstance(new MyClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
        System.out.println("jdk动态代理,开始");
        Object invoke = method.invoke(target, args);
        System.out.println("jdk动态代理,结束");
        return invoke;
    }
}
package proxy.custom.source;

import java.lang.reflect.Method;

public interface MyInvocationHandler {

    Object invoke(Object invoke, Method method, Object[] args) throws Exception;

}
package proxy.custom.source;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {

    private File classPathFile;

    public MyClassLoader() {
        String classPath = MyClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
//        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        // proxy/custom/source/$Proxy0 (wrong name: proxy/custom/$Proxy0)
        // 这里有个问题,class文件生成在proxy/custom/source/下,写该路径却有上面的异常。
        String className = "proxy/custom/$Proxy0";
        if (classPathFile != null) {
            File classfile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
            if (classfile.exists()) {
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try {
                    in = new FileInputStream(classfile);
                    out = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }
                    return defineClass(className, out.toByteArray(), 0, out.size());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != in) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != out) {
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}
package proxy.custom.source;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class MyProxy {

    public static final String LN = "\r\n";

    /**
     * 原理,字节码重组
     * 1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
     * 2、JDK Proxy类重新生成一个新的类,同时新的类要实现被代理类所有实现的所有的接口
     * 3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
     * 4、编译新生成的Java代码.class
     * 5、再重新加载到JVM中运行
     * 以上这个过程就叫字节码重组
     * JDK中有个规范,只要欧式$开头的一般都是自动生成的
     */
    public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h) {
        try {
            // 1、动态生成源代码.java文件
            String src = generateSrc(interfaces);
            // 2、Java文件输出存盘
            String path = MyProxy.class.getResource("").getPath();
            File f = new File(path + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            // 3、把生成的.java文件编译成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();
            // 4、编译生成的.class文件加载到JVM中
            Class<?> proxyClass = classLoader.findClass("$Proxy0");
            Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            f.delete();
            // 5、返回字节码重组以后的新的代理对象
            return constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package proxy.custom;" + LN);
        sb.append("import proxy.Person;" + LN);
        sb.append("import proxy.custom.source.MyInvocationHandler;" + LN);
        sb.append("import java.lang.reflect.Method;" + LN);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + LN);
        sb.append("MyInvocationHandler h;" + LN);
        sb.append("public $Proxy0(MyInvocationHandler h) { " + LN);
        sb.append("this.h = h;");
        sb.append("}" + LN);
        for (Method m : interfaces[0].getMethods()) {
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + LN);
            sb.append("try {" + LN);
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\", new Class[]{});" + LN);
            sb.append("this.h.invoke(this, m, null);" + LN);
            sb.append("} catch (Throwable e) {" + LN);
            sb.append("e.printStackTrace();" + LN);
            sb.append("}" + LN);
            sb.append("}" + LN);
        }
        sb.append("}" + LN);
        return sb.toString();
    }

}

测试

package proxy.custom;

import proxy.Coder;
import proxy.Person;
import proxy.custom.source.MyClassLoader;

public class CustomProxyTest {

    public static void main(String[] args) throws ClassNotFoundException {
        CustomProxy customProxy = new CustomProxy();
        Person person = (Person) customProxy.getCustomProxy(new Coder());
        person.cleaning();
        person.cooking();
//        test();
    }

    public static void test() throws ClassNotFoundException {
        MyClassLoader myClassLoader = new MyClassLoader();
//        System.out.println(myClassLoader.findClass("MyProxy"));
        System.out.println(myClassLoader.findClass("$Proxy0"));
    }

}

公用接口和类

package proxy;

public interface Person {

    void cleaning();

    void cooking();

}
package proxy;

public class Coder implements Person {

    @Override
    public void cleaning() {
        System.out.println("cleaning");
    }

    @Override
    public void cooking() {
        System.out.println("cooking");
    }
}