14 September 2018

代理模式

1.背景:代理对象做事情

2.代码:https://github.com/gre2/wuleiDesignPatterns/tree/master/src/main/java/com/wl/prox

3.分类:jdk(静态,动态),cglib

4.jdk静态代理

逻辑:静态代理在编译前就需要代理类实现和被代理类相同的接口,并且直接在实现的方法中调用被代理类相应的方法
缺点:一个代理只能为一个被代理类服务,如果需要代理的类很多,需要写大量的代理类代码
//代理
public class HuangNiuTicket implements TicketInterface {
    @Override
    public void findTicket() {
        System.out.println("我是黄牛,我可以帮买票");
        new WuleiTicket().findTicket();
        System.out.println("我是黄牛,票已到手");
    }
}
//被代理
public class WuleiTicket implements TicketInterface {
    @Override
    public void findTicket() {
        System.out.println("我是吴磊,我想买票");
    }
}
public class MainStaticClass {
    public static void main(String[] args) throws Exception {
        new HuangNiuTicket().findTicket();
    }

}

5.jdk动态代理

逻辑:也是基于接口实现,通过接口指向实现类实例的多态方式,将具体实现和调用解耦
优点:开始不知道针对哪个接口,哪个被代理类创建代理类,因为它是在运行时被创建的
核心:字节码重组(代理对象的类来源)
思路:第一块代码是代理类代码,第二块代码是测试类,第三块代码是生成代理类的字节码文件
     第四块和第五块被第三块依赖的部分代码块
public class HuangNiuDynamicTicket implements InvocationHandler {

    private Object wuleiTicket;

    public HuangNiuDynamicTicket() {
    }

    public HuangNiuDynamicTicket(Object wuleiTicketRequest) {
        this.wuleiTicket = wuleiTicketRequest;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是代理,我可以帮你弄");
        //method:public abstract void com.wl.proxy.jdk.common.TicketInterface.findTicket()
        //调用invoke方法会走到被代理对象的方法中
        Object object = method.invoke(wuleiTicket, args);
        System.out.println("我是代理,帮你弄完了");
        return object;
    }

    public Object getInstance(Object request) {
        this.wuleiTicket = request;
        Class wuleiClazz = wuleiTicket.getClass();
        System.out.println("被代理对象的实例是:" + wuleiClazz);
        //被代理对象的实例是:class com.wl.prox.jdkproxy.common.WuleiTicket
        //定义谁的什么方法被谁代理,之后返回一个proxy生成的对象
        return Proxy.newProxyInstance(wuleiClazz.getClassLoader(), wuleiClazz.getInterfaces(), this);
    }
}
public class MainDynamicClassBak {
    public static void main(String[] args) throws Exception {

        TicketInterface ticketInterfaceB = (TicketInterface) new HuangNiuDynamicTicket().getInstance(new WuleiTicket());
        System.out.println("代理对象的class是:" + ticketInterfaceB.getClass());
        //代理对象的class是:class com.sun.proxy.$Proxy0
        //会走进代理者的invoke方法
        ticketInterfaceB.findTicket();
        //默认调用代理类的invoke方法

        //获取代理类字节码内容
        byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{TicketInterface.class});
        FileOutputStream os = new FileOutputStream("$Proxy0.class");
        os.write(data);
        os.close();
    }
}
public final class $Proxy1 extends Proxy implements TicketInterface {
    private static Method m3;
   
    public $Proxy1(InvocationHandler var1) throws  {
        super(var1);
    }

    public final void findTicket() throws  {
        try {
            //super.h参数查看下一个代码块的Proxy类属性,invoke方法查看下两个代码块
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m3 = Class.forName("com.wl.prox.jdkproxy.common.TicketInterface").getMethod("findTicket");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
public class Proxy implements java.io.Serializable {

    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    protected InvocationHandler h;


    private Proxy() {
    }

    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }
}
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

6.手写classLoader类,手写Proxy类,完成jdk动态代理——-类加载见别的文章

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h);
主要工作
1.生成源代码(用一个无参的接口举例)
2.保存为java文件
3.编译java文件,生成class(JavaCompiler)
4.加载到内存,ClassLoader.defineClass()方法
5.返回被代理的对象
6.代理类实现自己的WlInvocationHandler,ClassLoader也自己实现
public class WlProxy {

    private static String ln = "\r\n";//回车换行

    public static Object newProxyInstance(WlClassLoader loader, Class<?>[] interfaces, WlInvocationHandler h) throws Exception {
        //1.生成源代码
        String proxySrc = generateSrc(interfaces[0]);
        //2.保存为java文件
        String filePath = WlProxy.class.getResource("").getPath();
        File f = new File(filePath + "$Proxy0.java");
        FileWriter fw = new FileWriter(f);
        fw.write(proxySrc);
        fw.flush();
        fw.close();
        //3.编译java文件,生成class
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
        Iterable iterable = manager.getJavaFileObjectsFromFiles(Arrays.asList(f));

        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, iterable);
        boolean flag = task.call();//通过返回值知道是否编译class成功
        //如果失败,会提示哪里失败了
        //https://docs.oracle.com/javase/7/docs/api/javax/tools/JavaCompiler.html
//        for (Diagnostic diagnostic : diagnostics.getDiagnostics())
//            System.out.format("Error on line %d in %s%n",
//                    diagnostic.getLineNumber(),
//                    diagnostic.getSource().toString());
        manager.close();
        //4.加载到内存(jvm)
        //5.返回被代理后的代理对象
        Class proxyClass = loader.findClass("$Proxy0");
        Constructor constructor = proxyClass.getConstructor(WlInvocationHandler.class);

        return constructor.newInstance(h);
    }

    private static String generateSrc(Class<?> anInterface) {
        StringBuffer src = new StringBuffer();
        src.append("package com.wl.prox.jdkproxy.writeProxy;" + ln);
        src.append("import java.lang.reflect.Method;" + ln);
        src.append("public class $Proxy0 implements " + anInterface.getName() + "{" + ln);
        src.append("public WlInvocationHandler h;" + ln);
        src.append("public $Proxy0(WlInvocationHandler h) {" + ln);
        src.append("this.h=h;" + ln);
        src.append("}" + ln);
        for (Method method : anInterface.getMethods()) {
            src.append("public " + method.getReturnType() + " " + method.getName() + "(){" + ln);
            src.append("try{" + ln);
            src.append("Method m= " + anInterface.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + ln);
            src.append("this.h.invoke(this,m,null);" + ln);
            src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
            src.append("}" + ln);
        }
        src.append("}");
        return src.toString();
    }

}
public class WlClassLoader extends ClassLoader {

    private File baseDir;

    public WlClassLoader() {
        String basePath = WlClassLoader.class.getResource("").getPath();
        this.baseDir = new File(basePath);
    }


    @Override
    public Class findClass(String name) throws ClassNotFoundException {
        String className = WlClassLoader.class.getPackage().getName() + "." + name;
        if (baseDir != null) {
            File classFile = new File(baseDir, 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;
    }
}

7.cglib动态代理

区别:jdk动态代理的代理对象,强制转换成接口;
     cglib的动态代理是通过生成一个被代理对象的子类,重写父类方法,可以强制转换成被代理对象(子类引用赋值给父类)
逻辑:被代理对象基于类实现,不依赖接口
原理:字节码重组
//CGLib底层采用ASM字节码生成框架
//多态
public class HuangNiuCglibTicket implements MethodInterceptor {


    public Object getInstance(Class clazz) throws Exception {
        //Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,Enhancer-增强器
        Enhancer enhancer = new Enhancer();
        //生成的子类,继承哪个父类
        enhancer.setSuperclass(clazz);
        //设置回调
        enhancer.setCallback(this);
        //生成源代码,编译class,加载到内存,返回被代理对象的子类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object instance, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //instance为由CGLib动态生成的代理类实例,MethodProxy为生成的代理类对方法的代理引用
        System.out.println("我是cglib黄牛,我可以帮买票");
        methodProxy.invokeSuper(instance, args);
        System.out.println("我是cglib黄牛,票已到手");
        return null;
    }
}


blog comments powered by Disqus