JDK动态代理

代理模式介绍

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

静态代理

在这里插入图片描述

歌手接口Singer

package designMode.proxy;

//歌手接口,提供唱歌的方法
public interface Singer {
    public void sign();
}

歌手类,实现Singer接口

public class Zhoujl implements Singer{
    @Override
    public void sign() {
        System.out.println("演唱歌曲:青花瓷...");
    }
}

经纪人公司(歌手的代理),实现Singer接口

/**
 * 经纪人公司,代理歌手对外进行
 */
public class StaticProxyCompany implements Singer {
    //真正的歌手
    private Singer realSinger;

    public StaticProxyCompany(Singer singer){
        this.realSinger = singer;
    }

    @Override
    public void sign() {
        CommonUtil.doBefore();
        realSinger.sign();
        CommonUtil.doAfter();
    }
}

执行测试

public class designModeTest {
    @Test
    public void staticProxyTest(){
        Zhoujl z = new Zhoujl();
        StaticProxyCompany proxy = new StaticProxyCompany(z);
        proxy.sign();
    }
}

在这里插入图片描述

动态代理

在这里插入图片描述

JDK动态代理,根据传入的类,生产代理对象

package designMode.proxy.jdkProxy;

import util.CommonUtil;

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

/**
 * 艺人经纪公司,通过动态代理实现
 */
public class JdkProxyCompany{

    /**
     * 为艺人创建代理
     *
     * @param realSinger
     * @return
     */
    public static Object getProxy(Object realSinger) {
        Class<?> targetClass = realSinger.getClass();
        return Proxy.newProxyInstance(
                targetClass.getClassLoader(), 
                targetClass.getInterfaces(), 
                new MyInvocationHandler(realSinger)
                );
    }


    /**
     * 实际对艺人增强的方法
     */
    private static class MyInvocationHandler implements InvocationHandler {

        private Object realSinger;

        public MyInvocationHandler(Object realSinger) {
            this.realSinger = realSinger;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //前置增强
            CommonUtil.doBefore();

            //调用被代理的方法
            Object res = method.invoke(realSinger, args);

            //后置增强
            CommonUtil.doAfter();

            return res;
        }
    }
}

执行测试,结果和静态代理一致

package designMode;

import designMode.proxy.Singer;
import designMode.proxy.Zhoujl;
import designMode.proxy.jdkProxy.JdkProxyCompany;
import designMode.proxy.staticProxy.StaticProxyCompany;
import org.junit.Test;
import sun.misc.ProxyGenerator;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class designModeTest {
    @Test
    public void jdkProxyTest() throws Throwable {
        //周杰伦开演唱会
        Singer z = new Zhoujl();
        Singer zProxy = (Singer)JdkProxyCompany.getProxy(z);
        zProxy.sign();

        //将JDK生成的代理类输出来
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{z.getClass()});
        FileOutputStream fos = new FileOutputStream("Proxy0.class");
        fos.write(bytes);
        fos.flush();
    }
}

看看反编译之后的代理类长什么样

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import designMode.proxy.Zhoujl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Singer {
    //删除了非重要属性
    
    //动态生产的代理类的构造方法(!!!!!)
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    
    //代理类同样实现了Singer接口,与静态代理相比较,只缺realSinger就完成了
    public final void sign() throws  {
        try {
            //super.h 对应是父类的h变量[Proxy.newInstance方法中的InvocationHandler参数]
            //所以这里就是我们自己写的InvocationHandler实现类的invoke方法
            //这个h是什么时候传入的? super.h  说明是从 Proxy 的构造方法中传入的
            super.h.invoke(this, m3, (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"));
            m8 = Class.forName("designMode.proxy.Zhoujl").getMethod("notify");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("designMode.proxy.Zhoujl").getMethod("wait", Long.TYPE);
            m3 = Class.forName("designMode.proxy.Zhoujl").getMethod("sign");
            m5 = Class.forName("designMode.proxy.Zhoujl").getMethod("wait", Long.TYPE, Integer.TYPE);
            m7 = Class.forName("designMode.proxy.Zhoujl").getMethod("getClass");
            m9 = Class.forName("designMode.proxy.Zhoujl").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("designMode.proxy.Zhoujl").getMethod("wait");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    
   //非重要代码已删除

}

源码分析

Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), new MyInvocationHandler(realSinger));

这里执行完成后,已经生产了一个类 class $Proxy0 extends Proxy implements Singer ,Singer很简单,下面看看的Proxy源码做了什么

public class Proxy implements java.io.Serializable {
    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * 禁止通过New来实例化
     */
    private Proxy() {
    }

    //构造方法,传入InvocationHandler
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    
    
    //获取代理对象的实例
    //第一步: 根据ClassLoader , Class<?>[] 动态生成代理类$Proxy0.class
    
    //第二步: 类 $Proxy0 的构造方法需要InvocationHandler h, 我们传入了h
    //可以执行实例化
    
    //第三步: 返回实例化后的$Proxy0,这个类已经拥有了InvocationHandler的实现类
    //可以调用invoke()
    public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
     //第一步: 根据ClassLoader , Class<?>[] 动态生成代理类$Proxy0.class
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        //获得$Proxy0的构造器,Proxy(InvocationHandler h)
        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;
                }
            });
        }
        //第二步: 类 $Proxy0 的构造方法需要InvocationHandler h, 我们传入了h
        //执行实例化
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

使用到的工具类

package util;

public class CommonUtil {

    private static StringBuilder sb;

    public static String lineSplit = "\n|\t\t\t";

    public static void printStart(StringBuilder stringBuilder){
        stringBuilder.append("\n+-------------------");
    }

    public static void printEnd(StringBuilder stringBuilder){
        stringBuilder.append("\n\\____________________");
    }

    public static void doBefore(){
        sb = new StringBuilder();
        CommonUtil.printStart(sb);
        sb.append("执行演唱会准备工作");
        sb.append(CommonUtil.lineSplit);
        sb.append("1.投放广告");
        sb.append(CommonUtil.lineSplit);
        sb.append("2.售票");
        sb.append(CommonUtil.lineSplit);
        sb.append("3.安排粉丝入场");
        CommonUtil.printEnd(sb);
        System.out.println(sb.toString());
    }

    public static void doAfter(){
        sb = new StringBuilder();
        CommonUtil.printStart(sb);
        sb.append("执行演唱会结束工作");
        sb.append(CommonUtil.lineSplit);
        sb.append("1.安排艺人退场");
        sb.append(CommonUtil.lineSplit);
        sb.append("2.安排粉丝退场");
        sb.append(CommonUtil.lineSplit);
        sb.append("3.场地清退");
        CommonUtil.printEnd(sb);
        System.out.println(sb.toString());
    }
}

手动实现简单的动态代理

接口

/**
 * @Author: 
 * @Date: 2018/8/29 23:31
 * @Desc: 接口
 **/
public interface UserService {

    String sayHello() throws Throwable;
}

实现类

/**
 * @Author: PENGXIAOLIANG
 * @Date: 2018/8/29 23:32
 * @Desc: 实现类
 **/
public class UserServiceImpl implements UserService{

    @Override
    public String sayHello() {
        System.out.println("你好,上海");
        return "执行成功";
    }
}

动态生成代理类(代理类同样实现了接口,且是通过传入的Handler来完成的)

import com.bootdo.simpleProxy.classloader.SimpleClassLoader;

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.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Author: PENGXIAOLIANG
 * @Date: 2018/9/3 23:28
 * @Desc: 自定义代理类
 **/
public class SelfDefineProxy {

    static String rt = "\r\n";

    public static Object getProxyObject(ClassLoader loader,Class<?> interfaces,InvocationHandler h){
        String path = "D:\\JavaWorkSpace\\bootdo\\bootdo\\src\\main\\java\\com\\bootdo\\simpleProxy\\temp\\$Proxy0.java";

        //1. 使用字符串拼凑出动态代理对象的java源码
        String proxySourceCode = get$Proxy0(interfaces);
        //2. 将源码写入本地文件
        outputFile(proxySourceCode,path);
        //3. 将源码编译成字节码文件 .class
        compileJavaFile(path);
        //4. 将字节码加载到内存(实例化生成好的代理类,传入代理类构造方法需要的h)
        return loadClassToJvm(h);
    }

    //4. 将字节码加载到内存(实例化生成好的代理类)
    private static Object loadClassToJvm(InvocationHandler h){
        try {
            SimpleClassLoader loader = new SimpleClassLoader("D:\\JavaWorkSpace\\bootdo\\bootdo\\src\\main\\java\\com\\bootdo\\simpleProxy\\temp");
            //得到动态代理类的反射对象
            Class<?> $Proxy0 = loader.findClass("$Proxy0");
            //反射获取构造函数
            Constructor<?> constructor = $Proxy0.getConstructor(MyInvocationHandler.class);
            //创建动态代理实例
            return constructor.newInstance(h);
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    //3. 将源码编译成字节码文件 .class
    private static void compileJavaFile(String fileName){

        try {
            //获得当前系统中的编译器
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            //获取文件管理者
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> fileObjects = manager.getJavaFileObjects(fileName);
            //编译任务
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, fileObjects);
            //开始编译,执行完可在当前目录下看到.class文件
            task.call();
            //关闭文件管理者
            manager.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //2. 将源码写入本地文件
    private static void outputFile(String proxySourceCode,String path){

        File f = new File(path);

        try {
            FileWriter fw = new FileWriter(f);
            fw.write(proxySourceCode);
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //1. 使用字符串拼凑出动态代理对象的java源码
    public static String get$Proxy0(Class<?> interfaces){
        Method[] methods = interfaces.getMethods();
        String proxyClass = "package com.bootdo.simpleProxy.temp;" + rt
                + "import com.bootdo.simpleProxy.MyInvocationHandler;" + rt
                + "import java.lang.reflect.Method;" + rt
                + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt

                    + "MyInvocationHandler h;" + rt

                    + "public $Proxy0(MyInvocationHandler h){this.h = h;}" + rt

                    + getMethodString(methods,interfaces) + rt

                + "}" + rt;
        return proxyClass;
    }


    /**
     * 拼接出所有的方法
     * @param methods
     * @param interfaces
     * @return
     */
    private static String getMethodString(Method[] methods,Class<?> interfaces){
        String proxyMethod = "";
        for(Method method : methods){
            proxyMethod += "public String " + method.getName() +"() throws Throwable {" + rt

                            + "Method md = " + interfaces.getName() + ".class.getMethod(\""+ method.getName() +"\",new Class[]{});" + rt
                            + "return (String)this.h.invoke(this,md,null);" + rt
                    + "}";
        }
        return proxyMethod;
    }
}

代理类引入外部类来填充接口方法的内容(代理类生成实例的时候,构造函数需要这个Handler)

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

/**
 * @Author: PENGXIAOLIANG
 * @Date: 2018/8/29 23:44
 * @Desc: 动态代理处理器
 **/
public class MyInvocationHandler implements InvocationHandler {

    private UserService targetObject;

    public MyInvocationHandler(UserService userService){
        this.targetObject = userService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(targetObject, args);
        after();
        return result;
    }

    private void before(){
        System.out.println("事物开启!");
    }

    private void after(){
        System.out.println("事物关闭!");
    }
}

用到的工具类

package com.bootdo.simpleProxy.classloader;

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

/**
 * 自定义的类加载器
 * @author heylinlook
 * @date 2018/4/23 14:34
 * @param
 * @return
 */
public class SimpleClassLoader extends ClassLoader {

    File dir;

    //把文件路径用构造函数传进来
    public SimpleClassLoader(String path) {
        dir = new File(path);
    }

    /*
     * 本方法就是去加载对应的字节码文件
     * */
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        //如果文件路径可用
        if (dir != null) {
            File clazzFile = new File(dir, name + ".class");
            //如果字节码文件存在
            if (clazzFile.exists()) {
                //把字节码文件加载到VM
                try {
                    //文件流对接class文件
                    FileInputStream inputStream = new FileInputStream(clazzFile);
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int len;
                    //将class文件读取到buffer中
                    while ((len = inputStream.read(buffer)) != -1) {
                        //将buffer中的内容读取到baos中的buffer
                        baos.write(buffer, 0, len);
                    }
                    //FIXME 将buffer中的字节读到内存加载为class  这个前缀要动态获取
                    return defineClass("com.bootdo.simpleProxy.temp." + name, baos.toByteArray(), 0, baos.size());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return super.findClass(name);
    }
}

参考资料、源码地址

JDK动态代理的实现原理以及如何手写一个JDK动态代理

JDK动态代理-超详细源码分析

小豹子带你看源码:JDK 动态代理

JDK动态代理源码分析

深入理解Java动态代理与代码模拟JDK实现动态代理【JAVA核心】

菜鸟教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值