动态代理模式以及jdk动态代理原理

概述

静态代理有一定的局限性,就是需要指定代理对象的类型,比如一开始只代理了Person接口的对象,如果要代理Animal接口的,但是代理逻辑都是一样的,此时又要新建一个代理类。如果使用动态代理的话,代理的是Object对象,在运行时动态调用方法,所以只要代理逻辑都一样的话,动态代理可以代理所有java对象。

动态代理常用的有jdk的动态代理和cglib的动态代理,两者的区别是jdk动态代理是代理接口的,而cglib动态代理代理的是类。

jdk动态代理

jdk动态代理有几个重要角色:
被代理对象接口:因为jdk动态代理代理的是接口,所以被代理对象一定要实现某些接口,可以多个。
被代理对象。
代理对象:实现InvocationHandler接口并重写invoke方法,这里就是代理对象的代理逻辑。
最后使用Proxy类获取代理对象。

例子:
被代理对象接口:

public interface PersonService {

    void findPerson();

    void getPerson();

    int getPersonById(Integer id);
}

被代理对象:

public class PersonServiceImpl implements PersonService {


    @Override
    public void findPerson() {
        System.out.println("执行方法findPerson");
    }

    @Override
    public void getPerson() {
        System.out.println("执行方法getPerson");
    }

    @Override
    public int getPersonById(Integer id) {
        return id;
    }
}

代理对象:

public class LogAdvise implements InvocationHandler {

    //这个用于保存被代理对象
    private Object target;

	//保存被代理对象的class对象
    private Class clazz;

    public LogAdvise(Object target){
        this.target = target;
        clazz = target.getClass();
    }

//日志记录被代理对象的方法执行前
    private void before(){
        System.out.println("记录方法执行前日志");
    }
//日志记录被代理对象的方法执行后
    private void after(){
        System.out.println("记录方法执行后日志");
    }

	//获取代理对象
    public  Object getProxy(){
    	//参数1:类加载器,使用被代理对象的类加载器即可
    	//参数2:要代理该对象的某些接口,这里是代理该对象实现的全部接口。
    	//参数3:代理对象本身,就是实现InvocationHandler 
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
    }

	//代理类代理逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //参数1proxy:就是生成的代理对象。
        //参数2:被代理接口的方法。
        //参数3:被代理方法的参数集合

		//实现前置通知
        before();
        //通过反射执行该方法。
        Object result = method.invoke(target,args);
        //执行后置通知
        after();
        //返回方法执行的接口。
        return result;
    }
}

测试类:

public class TestDynamicProxy {
    public static void main(String[] args) {
        LogAdvise logAdvise = new LogAdvise(new PersonServiceImpl());
        PersonService proxy = (PersonService) logAdvise.getProxy();
        proxy.findPerson();
        System.out.println();
        proxy.getPerson();

    }
}

结果:
在这里插入图片描述
成功,被代理的接口新增任意方法都会自动被代理,而不用在代理类中新加代理方法(如果是静态代理就要),并且代理的对象时Object,也就是可以代理任意对象。

原理:

jdk的动态代理是在运行时自动生成一个代理类,我们首先要获取到该代理类的class文件再反编译看看代码长啥样先。

我通过代理得知,生成的代理类是$Proxy0@584在这里插入图片描述

使用以下代码把class字节码保存到本地文件中。

public static void main(String[] args)  {
        LogAdvise logAdvise = new LogAdvise(new PersonServiceImpl());
        PersonService proxy = (PersonService) logAdvise.getProxy();
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0@584",new Class[]{PersonService.class});
        try (FileOutputStream fileOutputStream = new FileOutputStream("D://$Proxy0@584.class")){

            fileOutputStream.write(bytes);
        }catch (Exception e){
            e.printStackTrace();
        }


    }

反编译后的代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import winxun.peixun.designpattern.pattern.dynamicproxy.PersonService;

//继承Proxy 类是固定的,实现的接口就是按照Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this); 第二个参数指定的接口数组来实现,最多65535个接口。
public final class $Proxy0@584 extends Proxy implements PersonService {
	//下面是被代理的接口的被代理的方法,数量不定
	//固定有equals、hashcode、toString方法。
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m5;
    private static Method m0;

	//静态代码块,初始化方法。
	static {
        try {
        	//初始化m1为Object的equals方法。
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            //初始化m2为Object的toString方法。
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            //初始化m4位PersonService接口的getPerson方法
            m4 = Class.forName("winxun.peixun.designpattern.pattern.dynamicproxy.PersonService").getMethod("getPerson");
            //初始化m3位PersonService接口的findPerson方法
            m3 = Class.forName("winxun.peixun.designpattern.pattern.dynamicproxy.PersonService").getMethod("findPerson");
            //初始化m5位PersonService接口的getPersonById方法
            m5 = Class.forName("winxun.peixun.designpattern.pattern.dynamicproxy.PersonService").getMethod("getPersonById", Class.forName("java.lang.Integer"));
            //初始化m0为Object的hashCode方法。
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

	//构造方法传入InvocationHandler 实现类
    public $Proxy0_584/* $FF was: $Proxy0@584*/(InvocationHandler var1) throws  {
        super(var1);
    }

	
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

	//这些被代理的方法,都是通过h.invoke传入相应的方法和参数执行。
	//比如该方法:h.invoke(this//就是代理对象本身,m4 // getPerson方法的代理类,null //参数)
	//就是执行上面的LogAdvise对象的invoke方法。
	
    public final void getPerson() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findPerson() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Object getPersonById(Integer var1) throws  {
        try {
            return (Object)super.h.invoke(this, m5, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    

总结动态代理原理就是根据要被代理的接口的方法来动态生成一个代理类,使用反射执行invoke方法实现。手动字符串拼接成一个类。

自定义jdk动态代理

自己写一个jdk动态代理,与实际原理差了一些检查,大致原理是一样的。

先编写一个MyInvocationHandler代替InvocationHandler。

public interface MyInvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

编写MyProxy代替Proxy:

public class MyProxy {

	//保存代理类的MyInvocationHandler 
    protected MyInvocationHandler myInvocationHandler;

    protected MyProxy(MyInvocationHandler myInvocationHandler){
        this.myInvocationHandler = myInvocationHandler;
    }


	//生成代理对象的方法
    public static Object newProxyInstance(ClassLoader classLoader,Class[] interfaces,MyInvocationHandler myInvocationHandler){
    	//这里使用hashcode生成一个后缀,但是效率有点慢,可以选择更好的
        int suffix = Math.abs(UUID.randomUUID().toString().hashCode());
        //获取拼接后的源代码
        String src = generateProxySrc(interfaces,suffix);
        //使用拼接的源代码字符串生成本地文件
        String filePath = MyProxy.class.getResource("/").getPath();
        String fileName = filePath + File.separator + "$MyProxy" + suffix + ".java";
        File file = new File(fileName);
        try (FileWriter fileWriter = new FileWriter(file)){
            fileWriter.write(src);
            fileWriter.flush();
            //3、 把生成的.java 文件编译成.class 文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
            Iterable iterable = manage.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task =
                    compiler.getTask(null,manage,null,null,null,iterable);
            task.call();
            manage.close();
            //4、 编译生成的.class 文件加载到 JVM 中来
            Class proxyClass = classLoader.loadClass("$MyProxy" + suffix);
            Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
            //5、 返回字节码重组以后的新的代理对象
            return c.newInstance(myInvocationHandler);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                if (file.exists())
                    file.delete();
            }catch (Exception e){

            }
        }
        return null;
    }

    private static String generateProxySrc(Class[] interfaces,int suffix) {
        if (interfaces == null || interfaces.length == 0)
            throw new RuntimeException("接口不能为空");
        if (interfaces.length>65535)
            throw new RuntimeException("接口不能超过65535个");
        StringBuilder src = new StringBuilder(1000);
        src.append("import java.lang.reflect.*;").append(System.lineSeparator());
        src.append("import winxun.peixun.designpattern.pattern.dynamicproxy.diy.*;").append(System.lineSeparator());

        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder();
        StringBuilder sb3 = new StringBuilder();
        StringBuilder sb4 = new StringBuilder();
        sb1.append(interfaces[0].getSimpleName());

        for (int i = 0;i<interfaces.length;i++){
            Class clazz = interfaces[i];
            src.append("import ").append(clazz.getName()).append(";").append(System.lineSeparator());
            if (i>0)
                sb1.append(",").append(clazz.getSimpleName());

            Method[] methods = clazz.getMethods();
            for (int k = 0,j=3;k<methods.length;k++,j++){
                Method method = methods[k];
                Class<?>[] parameterTypes = method.getParameterTypes();
                sb2.append("private static Method m").append(j).append(";").append(System.lineSeparator());


                Class<?> returnType = method.getReturnType();
                sb4.append("public final ").append(returnType.getName()).append(" ").append(method.getName()).append("(");

                boolean isNoArgs = false;
                StringBuilder argsStringBuilder = new StringBuilder();
                if (parameterTypes.length>0){
                    sb4.append(parameterTypes[0].getName()).append(" var1");
                    argsStringBuilder.append("new Object[]{var1");
                    sb3.append("m").append(j).append(" = Class.forName(\"").append(clazz.getName()).append("\").getMethod(\"").append(method.getName()).append("\",").append("Class.forName(\"").append(parameterTypes[0].getName()).append("\")");
                }else {
                    argsStringBuilder.append("(Object[])null");
                    sb3.append("m").append(j).append(" = Class.forName(\"").append(clazz.getName()).append("\").getMethod(\"").append(method.getName()).append("\");").append(System.lineSeparator());
                    isNoArgs = true;
                }

                for (int p = 1;p<parameterTypes.length;p++){
                    String simpleName = parameterTypes[p].getName();
                    sb4.append(" ,").append(simpleName).append(" var").append(p+1);
                    argsStringBuilder.append(",var").append(p);
                    sb3.append(",Class.forName(\"").append(parameterTypes[0].getName()).append("\")");
                }

                if (!isNoArgs){
                    sb3.append(");").append(System.lineSeparator());
                }

                if (parameterTypes.length>0)
                    argsStringBuilder.append("}");
                sb4.append(")  {").append(System.lineSeparator());
                sb4.append("try {").append(System.lineSeparator());
                if (returnType.equals(void.class)||returnType.equals(Void.class)){
                    sb4.append("super.myInvocationHandler.invoke(this, m").append(j).append(",").append(argsStringBuilder).append(");").append(System.lineSeparator());
                }else {
                    sb4.append("return (").append(returnType.getName()).append(")super.myInvocationHandler.invoke(this, m").append(j).append(",").append(argsStringBuilder).append(");").append(System.lineSeparator());
                }
                sb4.append("} catch (RuntimeException | Error var1000000000000) {\n" +
                        "            throw var1000000000000;\n" +
                        "        } catch (Throwable var1000000000001) {\n" +
                        "            throw new UndeclaredThrowableException(var1000000000001);\n" +
                        "        }").append(System.lineSeparator());
                sb4.append("}").append(System.lineSeparator());
            }
        }

        src.append("public final class $MyProxy").append(suffix).append(" extends ").append("MyProxy").append(" implements ");
        src.append(sb1);

        src.append("{").append(System.lineSeparator());

        src.append("public $MyProxy").append(suffix).append("(MyInvocationHandler var1)  {").append(System.lineSeparator());
        src.append("super(var1);").append(System.lineSeparator()).append("}").append(System.lineSeparator());

        src.append("private static Method m0;").append(System.lineSeparator());
        src.append("private static Method m1;").append(System.lineSeparator());
        src.append("private static Method m2;").append(System.lineSeparator());

        //开始拼接方法
        src.append(sb2);


        src.append("static{").append(System.lineSeparator());
        src.append("try {").append(System.lineSeparator());
        src.append("m0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\");").append(System.lineSeparator());
        src.append("m1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", Class.forName(\"java.lang.Object\"));").append(System.lineSeparator());
        src.append("m2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\");").append(System.lineSeparator());
        src.append(sb3);

        src.append("}");
        src.append("catch (NoSuchMethodException var2) {\n" +
                "            throw new NoSuchMethodError(var2.getMessage());\n" +
                "        } catch (ClassNotFoundException var3) {\n" +
                "            throw new NoClassDefFoundError(var3.getMessage());\n" +
                "        }");

        src.append("}").append(System.lineSeparator());

        src.append("public final boolean equals(Object var1)   {\n" +
                "        try {\n" +
                "            return (Boolean)super.myInvocationHandler.invoke(this, m1, new Object[]{var1});\n" +
                "        } catch (RuntimeException | Error var3) {\n" +
                "            throw var3;\n" +
                "        } catch (Throwable var4) {\n" +
                "            throw new UndeclaredThrowableException(var4);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public final String toString()   {\n" +
                "        try {\n" +
                "            return (String)super.myInvocationHandler.invoke(this, m2, (Object[])null);\n" +
                "        } catch (RuntimeException | Error var2) {\n" +
                "            throw var2;\n" +
                "        } catch (Throwable var3) {\n" +
                "            throw new UndeclaredThrowableException(var3);\n" +
                "        }\n" +
                "    }\n"+
                "public final int hashCode()   {\n" +
                "        try {\n" +
                "            return (Integer)super.myInvocationHandler.invoke(this, m0, (Object[])null);\n" +
                "        } catch (RuntimeException | Error var2) {\n" +
                "            throw var2;\n" +
                "        } catch (Throwable var3) {\n" +
                "            throw new UndeclaredThrowableException(var3);\n" +
                "        }\n" +
                "    }").append(System.lineSeparator());
        src.append(sb4).append(System.lineSeparator());
        src.append("}");
        return src.toString();
    }
}

自创一个MyLogAdvise:

//区别:这个类是继承自定义的MyInvocationHandler ,而非InvocationHandler 
public class MyLogAdvise implements MyInvocationHandler {

    //这个是被代理对象
    private Object target;

    private Class clazz;

    public MyLogAdvise(Object target){
        this.target = target;
        clazz = target.getClass();
    }

    private void before(){
        System.out.println("记录方法执行前日志");
    }

    private void after(){
        System.out.println("记录方法执行后日志");
    }

    public  Object getProxy(){
    	//基本代码与LogAdvise一致,只是这个方法的使用的是MyProxy的newProxyInstance,而不是Proxy的。
        return MyProxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
    }

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

测试:

public class TestDiyDynamicProxy {
    public static void main(String[] args) {
        MyLogAdvise myLogAdvise = new MyLogAdvise(new PersonServiceImpl());
        PersonService proxy = (PersonService) myLogAdvise.getProxy();


        proxy.findPerson();
        System.out.println();
        proxy.getPerson();
        System.out.println();
        System.out.println(proxy.getPersonById(5));
    }
}

结果成功:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

我会努力变强的

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值