日拱一卒_Java动态代理的底层原理

1. 代理模式

代理模式是常用的设计模式之一,其特征是代理类与被代理类有相同的接口,代理类可以为被代理类方法执行进行前置后置处理,增强被代理类方法

代理模式的类结构通常如上图所示,代理类与被代理类之间会存在关联关系,一个代理类的对象持有一个被代理类的对象。代理类的对象本身并不真正实现服务,而是通过调用被代理类对象的相关方法来提供特定的服务

2. 动态代理使用

代理类并不是在 Java 代码中定义,而是在运行时根据在 Java 代码中的“指示”动态生成(字节码由JVM在运行时动态生成而非预存在任何一个 .class 文件中), 这种在程序运行时创建代理类的代理方式被称为动态代理,它的优势在于可以方便地对代理类的函数进行统一处理。

这是因为所有被代理执行的方法,都是通过InvocationHandler#invoke()方法调用,相当于给被代理类所有方法套了一层壳,所以只要在这个方法中统一处理,就可以对所有被代理的方法进行相同的操作了

以下代码展示了动态代理的简单使用,其基本步骤如下:

定义一个公共接口,本例中为 IHello,接口中有一个抽象方法定义一个实现了公共接口的实体类作为被代理类,本例中被代理类 Hello实现了 IHello接口,重写了接口中的抽象方法定义一个实现了 InvocationHandler 接口的方法拦截类,重写 invoke() 方法实现拦截到被代理类方法执行时候的处理逻辑通过 Proxy.newProxyInstance() 方法生成代理对象,持有代理对象之后执行接口方法即可。另外,搜索后端架构师后台回复“架构整洁”,获取一份惊喜礼包。public class ServiceProxy {    public interface IHello {        String sayHi();    }    public static class Hello implements IHello {        @Override        public String sayHi() {            return "Hello";        }    }    // 动态代理类    public static class ProxyHandler<T> implements InvocationHandler {        private T origin;        public ProxyHandler(T origin) {            this.origin = origin;        }       /**         * @param o 代理对象引用         * @param method 正在执行目标的方法         * @param objects 目标方法执行时的入参        */        @Override        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {            String s = "proxy";            s += method.invoke(origin, objects);            return s;        }    }    public static void main(String[] args) {        IHello IHello = (IHello) getInstance(IHello.class, new ProxyHandler<>(new Hello()));        System.out.println(IHello.toString());        generateProxyClass();    }    // 创建代理对象    public static <T> Object getInstance(Class<T> clazz, ProxyHandler<T> handler) {        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);    }    private static void generateProxyClass() {        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());        String path = "/Users/nathan.yang/workspace/algorithm_Java/out/StuProxy.class";        try (FileOutputStream fos = new FileOutputStream(path)) {            fos.write(classFile);            fos.flush();            System.out.println("代理类文件写入成功");        } catch (Exception e) {            System.out.println("写文件错误");        }    }}

3. 动态代理原理

1.Proxy#newProxyInstance() 方法是动态代理的入口,其生成动态代理对象主要有以下几个步骤:

getProxyClass0() 方法生成代理类获取到代理类后将 InvocationHandler 对象入参,反射调用构造方法生成动态代理对象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.      */     Class<?> cl = getProxyClass0(loader, intfs);     /*      * Invoke its constructor with the designated invocation handler.      */     try {         if (sm != null) {             checkNewProxyPermission(Reflection.getCallerClass(), cl);         }         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;                 }             });         }         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);     } }

2.Proxy#getProxyClass0() 方法其实是从一个 WeakCache 中去获取代理类,其获取逻辑是如果缓存类中没有代理类的话就调用ProxyClassFactory#apply(),通过代理类工厂去即时生成一个代理类,其步骤如下:

首先通过指定的类加载器去验证目标接口是否可被其加载通过接口所在包等条件决定代理类所在包及代理类的全限定名称,代理类名称是包名+$Proxy+id通过 ProxyGenerator.generateProxyClass() 生成字节码数组,然后调用 native 方法 defineClass0() 将其动态生成的代理类字节码加载到内存中

牛逼啊!接私活必备的 N 个开源项目!赶快收藏吧

private static Class<?> getProxyClass0(ClassLoader loader,                                        Class<?>... interfaces) {     if (interfaces.length > 65535) {         throw new IllegalArgumentException("interface limit exceeded");     }     // If the proxy class defined by the given loader implementing     // the given interfaces exists, this will simply return the cached copy;     // otherwise, it will create the proxy class via the ProxyClassFactory     return proxyClassCache.get(loader, interfaces); } public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {         Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);         for (Class<?> intf : interfaces) {             /*              * Verify that the class loader resolves the name of this              * interface to the same Class object.              */             Class<?> interfaceClass = null;             try {                 interfaceClass = Class.forName(intf.getName(), false, loader);             } catch (ClassNotFoundException e) {             }             if (interfaceClass != intf) {                 throw new IllegalArgumentException(                     intf + " is not visible from class loader");             }             /*              * Verify that the Class object actually represents an              * interface.              */             if (!interfaceClass.isInterface()) {                 throw new IllegalArgumentException(                     interfaceClass.getName() + " is not an interface");             }             /*              * Verify that this interface is not a duplicate.              */             if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {                 throw new IllegalArgumentException(                     "repeated interface: " + interfaceClass.getName());             }         }         String proxyPkg = null;     // package to define proxy class in         int accessFlags = Modifier.PUBLIC | Modifier.FINAL;         /*          * Record the package of a non-public proxy interface so that the          * proxy class will be defined in the same package.  Verify that          * all non-public proxy interfaces are in the same package.          */         for (Class<?> intf : interfaces) {             int flags = intf.getModifiers();             if (!Modifier.isPublic(flags)) {                 accessFlags = Modifier.FINAL;                 String name = intf.getName();                 int n = name.lastIndexOf(.);                 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));                 if (proxyPkg == null) {                     proxyPkg = pkg;                 } else if (!pkg.equals(proxyPkg)) {                     throw new IllegalArgumentException(                         "non-public interfaces from different packages");                 }             }         }         if (proxyPkg == null) {             // if no non-public proxy interfaces, use com.sun.proxy package             proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";         }         /*          * Choose a name for the proxy class to generate.          */         long num = nextUniqueNumber.getAndIncrement();         String proxyName = proxyPkg + proxyClassNamePrefix + num;         /*          * Generate the specified proxy class.          */         byte[] proxyClassFile = ProxyGenerator.generateProxyClass(             proxyName, interfaces, accessFlags);         try {             return defineClass0(loader, proxyName,                                 proxyClassFile, 0, proxyClassFile.length);         } catch (ClassFormatError e) {             /*              * A ClassFormatError here means that (barring bugs in the              * proxy class generation code) there was some other              * invalid aspect of the arguments supplied to the proxy              * class creation (such as virtual machine limitations              * exceeded).              */             throw new IllegalArgumentException(e.toString());         }     }

3.反射获取到代理类参数为 InvocationHandler.class 的构造器,其实也就是 Proxy 的带参构造器,调用构造器cons.newInstance(new Object[]{h})生成代理对象。另外,搜索Linux就该这样学后台回复“猴子”,获取一份惊喜礼包。

protected Proxy(InvocationHandler h) {     Objects.requireNonNull(h);     this.h = h; }

4.通过以下代码可以将 JVM 中加载的代理类输出成 class 文件,之后就可以使用反编译工具查看代理类的源码

private static void generateProxyClass() {     byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());     String path = "/Users/nathan/workspace/algorithm_Java/out/StuProxy.class";     try (FileOutputStream fos = new FileOutputStream(path)) {         fos.write(classFile);         fos.flush();         System.out.println("代理类文件写入成功");     } catch (Exception e) {         System.out.println("写文件错误");     } }

5.生成的代理类源码如下,很明显可以看到该类实现动态代理的原理:

通过 static 代码块将被代理类中每一个方法封装为 Method 对象,生成方法表代理类对象执行被代理类同名方法时,通过其父类Proxy保留的指向InvocationHandler对象的引用调用 InvocationHandler#invoke() 方法,完成动态代理public final class $Proxy0 extends Proxy implements IHello { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws  {     super(var1); }  public final String sayHi() throws  {     try {       // 父类 Proxy 保留的指向 InvocationHandler 对象的引用调用 invoke() 方法         return (String)super.h.invoke(this, m3, (Object[])null);     } catch (RuntimeException | Error var2) {         throw var2;     } catch (Throwable var3) {         throw new UndeclaredThrowableException(var3);     } } 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);     } }......// 方法表 static {     try {         m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));         m3 = Class.forName("ServiceProxy$IHello").getMethod("sayHi");         m2 = Class.forName("java.lang.Object").getMethod("toString");         m0 = Class.forName("java.lang.Object").getMethod("hashCode");     } catch (NoSuchMethodException var2) {         throw new NoSuchMethodError(var2.getMessage());     } catch (ClassNotFoundException var3) {         throw new NoClassDefFoundError(var3.getMessage());     } }}

百年党史·天天学

学史明理 学史增信 学史崇德 学史力行

百年党史·天天学

重要论述

1923年4月10日

毛泽东在《新时代》创刊号发表《外力、军阀与革命》一文。文章指出:现在国内存在三派势力:革命的民主派,非革命的民主派,反动的军阀派。今后中国政治的形势将成为以下情况:一方是最急进的共产派和缓进的研究系、知识派、商人派(非革命的民主派),为了推倒共同敌人与国民党合作,成功一个大的民主派;一方就是反动的军阀派。中国政治的结局是民主派战胜军阀派,但目前及最近之将来一个期内,中国必仍然是军阀的天下。其原因,一是国际资本帝国主义对中国的侵略,二是中国的社会经济最便利于军阀的统治。文章还指出:这个期内是外力和军阀勾结为恶,是必然成功一种极反动极混乱的政治的。但政治愈反动愈混乱的结果,是必然要激起全国国民的革命观念,国民的组织能力也会要一天进步一天。也就是说,更反动更混乱的政治,是和平统一的来源,是革命的生母,是民主独立的圣药。大家不可不知道。

这篇文章收入《毛泽东文集》第一卷。

1934年4月10日

毛泽东在长冈乡调查和才溪乡调查所得结果基础上,综合其他乡苏维埃工作经验,写《乡苏怎样工作?》一文,以指导乡苏维埃政权建设。文章指出:改善乡苏工作的方向,应该朝着最能够接近广大群众,最能够发挥群众的积极性与创造性,最能够动员群众执行苏维埃任务,并且最能够争取任务完成的速度,使苏维埃工作与革命战争、群众生活的需要完全配合起来,这是苏维埃工作的原则。文章阐述了许多重要的工作方法,包括要懂得抓紧每一时期的中心工作,不应该只忙一些零碎事务,把中心工作丢掉了;要根据各村的情形和特点去推动工作,解决各村群众的困难问题;要废除一切强迫命令的方法;只有决定,没有检查,就是官僚主义的领导,它同强迫命令主义是一样有害的。

这篇文章收入《毛泽东文集》第一卷。

1948年4月10日

毛泽东为中共中央起草致华东局转许世友、谭震林、谢有法并告各中央局、分局、前委电。电报强调将全国一切可能和必须统一的权力统一于中央,指出:中央不止一次地向各地各军领导同志指出,中央的一切政策必须无保留地执行,不能允许不得中央同意由任何下级机关自由修改。我们这样做是完全合乎中国革命形势的要求的。中国新的革命高潮的到来,我党已经处在夺取全国政权的直接的道路上,这一形势要求我们全党全军首先在一切政治上的政策及策略方面,在军事上的战略及重大战役方面的完全统一,经济上及政府行政上在几个大的区域内的统一,然后按照革命形势的发展进一步地考虑在军队的编制和供应上,在战役行动的互相配合上,以及在经济上在政府行政上(那时须建立中央政府)作更大的统一。总之,革命形势要求我党缩小(不是废除)各地方各兵团的自治权,将全国一切可能和必须统一的权力统一于中央,而在各地区和各部分则统一于受中央委托的领导机关。各地领导同志必须迅速完成在这方面的一切必要的精神准备和组织准备。

1956年4月10日

周恩来主持国务院常务会议,在讨论国家计委《关于一九五六年基本建设计划安排和要求增加部分投资的补充报告》时,讲话指出:搞计划必须注意实事求是。生产是中心,三大改造也要以生产来推动。一切都要靠生产,生产是主要的环节。搞生产就要联系到平衡,一定要为平衡而奋斗。数量上平衡以后,还有品种和时间上的平衡问题。

陈云在会上讲话指出:订计划应该遵循按比例发展的原则,基建和生产的比例是最重要的。如果基建规模超过生产发展,就会出问题。以后订计划应该首先进行物资平衡,然后再进行财力平衡。另外,大规模的建设,必须要有储备,特别是木料、钢材、水泥等的储备。否则,建设中碰到问题就无法解决。

1974年4月10日

邓小平在联大特别会议上代表中国政府发言,详细阐述了毛泽东关于三个世界划分的战略思想。发言指出:现在的世界实际上存在着互相联系又互相矛盾着的三个方面、三个世界。美国、苏联是第一世界;亚非拉发展中国家和其他地区的发展中国家,是第三世界;处于这两者之间的发达国家,是第二世界。中国是一个社会主义国家,也是一个发展中的国家,中国属于第三世界。中国政府和人民,坚决支持一切被压迫人民和被压迫民族的正义斗争,这是我们应尽的国际主义义务。中国现在不是,将来也不做超级大国。

发言强调,原料和发展问题的实质,就是发展中国家维护国家主权,发展民族经济,反对帝国主义、特别是超级大国的掠夺和控制的问题。这是当前第三世界国家和人民反殖、反帝、反霸斗争的一个极其重要的方面。第三世界国家要发展自己的经济,首要的前提是维护政治独立。没有政治独立,就不可能获得经济独立;而没有经济独立,一个国家的独立就是不完全、不巩固的。第三世界国家强烈要求改变目前极不平等的国际经济关系,中国政府和人民热烈赞同并坚决支持他们提出的一切正义主张。

发言还提出了中国政府的六项主张:国家之间的政治和经济关系都应当建立在和平共处五项原则的基础上,反对任何国家违背这些原则,在任何地区建立霸权和势力范围;各国的事务应当由各国人民自己来管;国家不论大小,不论贫富,应该一律平等,国际经济事务应该由世界各国共同来管,而不应当由一、两个超级大国来垄断;国际贸易应当建立在平等互利、互通有无的原则基础上;对发展中国家的经济援助,应当严格尊重受授国的主权,不附带任何政治、军事条件,不要求任何特权或借机牟取暴利;对发展中国家的技术转让必须实用、有效、廉价、方便。

1980年4月10日

邓小平在会见日中青年研修协会友好访华团和日本东京青年会议所访华团时说:我们面临的一项重要任务,是要发现、培养和使用人才。日本企业中百分之九十是中小企业,我们中小企业也是大量的。我们搞四个现代化需要学习日本,在某种意义上来说,日本中小企业的经验会对我们有帮助。中国过去学习苏联的企业管理方法,许多企业追求大而全。现在,有些地方搞试验,把一个厂分成几个厂,搞专业化。凡是这样做的都见效了。中国要搞四个现代化,需要努力争取一个和平的环境,尽可能延缓战争爆发的时间。从全球范围来说,中日关系搞好,是件大事,是重要的稳定因素之一。中日两国人民有两千多年的交往历史。中日两国友好不仅对两国人民有利,而且有利于亚洲、太平洋地区和世界的和平。希望两国青年、儿童懂得中日友好的重要性,把两国友好事业一代一代传下去。

1981年4月10日

邓小平在会见瑞典首相费尔丁时说:中国一贯的目标就是争取和平。中国很穷,最需要和平的国际环境发展自己。因此,中国维护世界和平的政策也是从我们自己的实际需要出发的。各国人民要联合起来,对付霸权主义。实现和平的手段,就是针锋相对地进行斗争,只有这样才能争取更长时间的和平。

2002年4月10日

江泽民访问德国期间在德国外交政策协会发表演讲,阐述了维护世界和平、促进共同发展的正确途径。他指出:进入新世纪以来,国际上发生的一系列突发事件,标志着国际局势正在发生冷战结束以后最为深刻的变化,需要各国人民冷静思考、认真应对。维护世界和平、促进共同发展的正确途径是:顺应时代潮流和各国人民的意愿,因势利导,积极推动建立公正合理的国际政治经济新秩序。各国政府和人民应该在以下方面共同作出努力:积极推动世界走向多极化,尊重各国和各国人民的意愿和利益。推进国际关系民主化,凝聚各国人民的力量解决面临的突出问题。尊重世界多样性,保证各国和睦相处、相互尊重。正确引导经济全球化,促进各国实现共同发展。树立以互信、互利、平等、协作为核心的新安全观,努力营造长期稳定的和平国际环境。这篇讲话的主要部分以《共同创造一个和平繁荣的新世纪》为题,收入《江泽民文选》第三卷。

2018年4月10日

习近平出席博鳌亚洲论坛2018年年会开幕式并发表主旨演讲,强调各国要顺应时代潮流,坚持开放共赢,勇于变革创新,向着构建人类命运共同体的目标不断迈进;中国将坚持改革开放不动摇,继续推出扩大开放新的重大举措,同亚洲和世界各国一道,共创亚洲和世界的美好未来。他指出,一个国家、一个民族要振兴,就必须在历史前进的逻辑中前进、在时代发展的潮流中发展。当今世界,和平合作的潮流滚滚向前,开放融通的潮流滚滚向前,变革创新的潮流滚滚向前。各国人民应该同心协力、携手前行,努力构建人类命运共同体,共创和平、安宁、繁荣、开放、美丽的亚洲和世界。他强调,面向未来,我们要相互尊重、平等相待,走对话而不对抗、结伴而不结盟的国与国交往新路,努力实现持久和平;要对话协商、共担责任,实现普遍安全和共同安全;要同舟共济、合作共赢,构建开放型世界经济,维护多边贸易体制,推动经济全球化朝着更加开放、包容、普惠、平衡、共赢的方向发展;要兼容并蓄、和而不同,使文明交流互鉴成为增进各国人民友谊的桥梁、推动社会进步的动力、维护地区和世界和平的纽带;要敬畏自然、珍爱地球,开拓生产发展、生活富裕、生态良好的文明发展道路,为子孙后代留下蓝天碧海、绿水青山。

百年党史·天天学

党史回眸

1938年

4月10日 鲁迅艺术学院在延安成立,毛泽东出席成立大会并讲话。他说:“要在民族解放的大时代去发展广大的艺术运动,在抗日民族统一战线方针的指导下,实现文学艺术在今天的中国的使命和作用。”鲁迅艺术学院是抗日战争时期中共为培养抗战文艺干部和文艺工作者而创办的一所综合性文学艺术学校,1940年后更名为“鲁迅艺术文学院”,简称“鲁艺”。鲁艺的教育方针是:团结与培养文学艺术的专门人才,致力于新民主主义的文学艺术事业。在延安七年半的时间里,鲁艺开办了文学、戏剧、音乐、美术等系,培养了大批人才。鲁艺还创作了诸如《白毛女》、《南泥湾》、《黄河大合唱》等一大批极富影响力的作品,活跃了敌后抗日根据地军民的文化生活,振奋了中国军民的抗战热情,为抗日战争的胜利作出了贡献,并对中国现代文学艺术产生了影响。

1971年

4月10日 美国乒乓球代表团访华,乒乓外交拉开序幕。新中国成立后,美国对中国采取封锁、孤立政策,两国民间交往也完全隔绝。1969年尼克松就任美国总统后,为了摆脱越南战争的困境,改变当时苏攻美守的战略态势,谋求发展对华关系,先后托巴基斯坦总统和罗马尼亚总统向中国领导人传话,希望派特使秘密访华。中国方面很快作出回应,之后两国关系开始松动。1971年春,正当两国领导人通过巴基斯坦秘密渠道酝酿美国领导人访华的时候,毛泽东抓住3月底4月初在日本名古屋举行的第31届世界乒乓球锦标赛的时机,作出决策,邀请美国乒乓球队访华,以中美人民之间的交往作为打开两国官方关系的序幕。1971年4月10日,美国乒乓球代表团和一小批美国新闻记者抵达北京,这就是当时被誉为“小球转动大球”的“乒乓外交”。中美两国乒乓球队的友好往来,以出人意料的方式促进了中美关系的发展和世界形势的变化。

1977年

4月10日 邓小平致信华国锋、叶剑英和中共中央。针对“两个凡是”的错误观点,指出:我们必须世世代代地用准确的完整的毛泽东思想来指导我们全党、全军和全国人民。5月3日,中共中央转发这封信。

1995年

4月10日 伟大的无产阶级革命家、政治家、杰出的马克思主义者,中国社会主义经济建设的开创者和奠基人之一,党和国家久经考验的卓越领导人陈云同志,因病在北京逝世,享年九十岁。

2014年

4月10日 中央军委印发《关于贯彻落实军委主席负责制建立和完善相关工作机制的意见》。2017年11月2日,中央军委印发《关于全面深入贯彻军委主席负责制的意见》。

2015年

4月10日 中共中央办公厅印发《关于在县处级以上领导干部中开展“三严三实”专题教育方案》。从4月底开始,在县处级以上领导干部中不分批次、不划阶段、不设环节开展“三严三实”专题教育,着力解决“不严不实”问题。

百年党史·天天学

 历史瞬间 

1974年4月10日,邓小平在联合国大会第六届特别会议上发言。

计算机与通信学院

-JSEI1978-

投稿 & 联系我们

[email protected]

编辑/王振榕

责任编辑/秦添钖

审核/姜晓斌