-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 34.5 KB
/
content.json
1
{"meta":{"title":"Guoxiong Li","subtitle":null,"description":"Guoxiong Li","author":"Guoxiong Li","url":"http://lgxbslgx.github.io","root":"/"},"pages":[{"title":"About Me","date":"2021-07-15T11:56:21.045Z","updated":"2021-07-15T11:56:21.037Z","comments":true,"path":"about/index.html","permalink":"http://lgxbslgx.github.io/about/index.html","excerpt":"","text":"Education Sept 2013 - Jul 2017 / Jinan University / Software Engineering Work Experience Nov 2016 - Feb 2017 / Shenzhen Binxun Technology Co., Ltd. / Intern Jul 2017 - Feb 2019 / China Merchants Bank Co., Ltd / Full-time Open Source Experience OpenJDK Committer Interest OpenJDK LLVM kotlin netty Find Me [email protected] GitHub Blog Zhihu"}],"posts":[{"title":"分享汇总","slug":"Sharing-summary","date":"2021-12-18T16:59:15.000Z","updated":"2021-12-18T17:12:17.872Z","comments":true,"path":"2021/12/19/Sharing-summary/","link":"","permalink":"http://lgxbslgx.github.io/2021/12/19/Sharing-summary/","excerpt":"","text":"相关信息和朋友们发起了一个“知识分享”计划,互相分享计算机知识和经验,进行新知识储备。这里汇总我自己已分享的内容。项目地址: sharing所有人的分享内容: catalog 已分享内容下面URL里面的文档和代码就是分享的内容。OpenJDK概述OpenJDK补丁案例分析io和nettylog4j2漏洞复现","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"},{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"}]},{"title":"编译器专栏","slug":"Compiler-article-summary","date":"2021-12-18T15:40:15.000Z","updated":"2021-12-23T14:28:10.476Z","comments":true,"path":"2021/12/18/Compiler-article-summary/","link":"","permalink":"http://lgxbslgx.github.io/2021/12/18/Compiler-article-summary/","excerpt":"","text":"相关信息最近写了一些关于编译器的文章,发表在知乎专栏。这里也留一个链接,方便读者。专栏地址: 编译器专栏 关于HotSpot的C1编译器C1系列:综述C1系列:构造HIRC1系列:规范化C1系列:局部值编号C1系列:函数内联C1系列:条件表达式消除C1系列:块合并","categories":[],"tags":[{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"},{"name":"Compiler","slug":"Compiler","permalink":"http://lgxbslgx.github.io/tags/Compiler/"},{"name":"hotspot","slug":"hotspot","permalink":"http://lgxbslgx.github.io/tags/hotspot/"},{"name":"JVM","slug":"JVM","permalink":"http://lgxbslgx.github.io/tags/JVM/"}]},{"title":"OpenJDK开发指南","slug":"OpenJDK-developer-guide","date":"2021-01-27T06:18:15.000Z","updated":"2021-07-15T05:10:26.812Z","comments":true,"path":"2021/01/27/OpenJDK-developer-guide/","link":"","permalink":"http://lgxbslgx.github.io/2021/01/27/OpenJDK-developer-guide/","excerpt":"","text":"相关信息该系列开发指南发表在知乎专栏。这里也留一个链接,方便读者。专栏地址: OpenJDK开发指南 具体文章OpenJDK开发指南—-前言OpenJDK开发指南—-社区角色OpenJDK开发指南—-邮件列表","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"},{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"}]},{"title":"《深入解析Java编译器》书评","slug":"Java-compiler-book-review","date":"2021-01-21T12:26:15.000Z","updated":"2021-07-15T12:59:35.963Z","comments":true,"path":"2021/01/21/Java-compiler-book-review/","link":"","permalink":"http://lgxbslgx.github.io/2021/01/21/Java-compiler-book-review/","excerpt":"","text":"相关信息 书名: 《深入解析Java编译器 源码剖析与实例详解》 作者: 马智 这一系列书评发表在我的知乎专栏。博客这里给个链接,方便读者。专栏地址: 《深入解析Java编译器》书评 链接 《深入解析Java编译器》综述 《深入解析Java编译器》第一章 《深入解析Java编译器》第二章 《深入解析Java编译器》第三章 《深入解析Java编译器》第四章 《深入解析Java编译器》第五章 《深入解析Java编译器》第六章 《深入解析Java编译器》第七章 《深入解析Java编译器》第八章 《深入解析Java编译器》第九章 《深入解析Java编译器》第十章 《深入解析Java编译器》第十一章 《深入解析Java编译器》第十二章 《深入解析Java编译器》第十三章 《深入解析Java编译器》第十四章 《深入解析Java编译器》第十五章 Java编译器总结","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"},{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"},{"name":"Javac","slug":"Javac","permalink":"http://lgxbslgx.github.io/tags/Javac/"}]},{"title":"OpenJDK Main-line Contribution Summary","slug":"OpenJDK-Contribution-Summary","date":"2020-11-20T06:39:15.000Z","updated":"2022-06-08T09:29:48.291Z","comments":true,"path":"2020/11/20/OpenJDK-Contribution-Summary/","link":"","permalink":"http://lgxbslgx.github.io/2020/11/20/OpenJDK-Contribution-Summary/","excerpt":"","text":"Related description and LinksThe JDK Project is the JDK main-line development of OpenJDK. Introduction Code Base tool: javac JDK-8254023 PATCH A module declaration is not allowed to be a target of an annotation that lacks an @Target meta-annotation JDK-8254557 PATCH Compiler crashes with java.lang.AssertionError: isSubtype UNKNOWN JDK-8255968 PATCH Confusing error message for inaccessible constructor JDK-8257037 PATCH No javac warning when calling deprecated constructor with diamond JDK-8245956 PATCH JavaCompiler still uses File API instead of Path API in a specific case JDK-8231622 PATCH SuppressWarning(“serial”) ignored on field serialVersionUID JDK-8257740 PATCH Compiler crash when compiling type annotation on multicatch inside lambda JDK-8230623 PATCH Extract command-line help for -Xlint sub-options to new –help-lint JDK-8258525 PATCH Some existing tests should use /nodynamiccopyright/ instead of the standard header JDK-8258662 PATCH JDK 17ea: Crash compiling instanceof check involving sealed interface JDK-8255729 PATCH com.sun.tools.javac.processing.JavacFiler.FilerOutputStream is inefficient JDK-8225003 PATCH NPE in Attr.attribIdentAsEnumType JDK-8239596 PATCH PARAMETER annotation on receiver type does not cause error JDK-8226216 PATCH parameter modifiers are not visible to javac plugins across compilation boundaries JDK-8216400 PATCH improve handling of IOExceptions in JavaCompiler.close() JDK-8198317 PATCH Enhance JavacTool.getTask for flexibility JDK-8057543 PATCH Replace javac’s Filter with Predicate (and lambdas) JDK-8255757 PATCH Javac emits duplicate pool entries on array::clone JDK-8259025 PATCH Record compact constructor using Objects.requireNonNull JDK-8150303 PATCH Rewrite test/tools/javac/Paths/Diagnostics.sh JDK-8203925 PATCH tools/javac/importscope/T8193717.java ran out of java heap JDK-8231179 PATCH Investigate why tools/javac/options/BCPOrSystemNotSpecified.java fails on Window JDK-8259359 PATCH javac does not attribute unexpected super constructor invocation qualifier, and may crash JDK-8236490 PATCH Compiler bug relating to @NonNull annotation JDK-8232765 PATCH NullPointerException at Types.eraseNotNeeded() when compiling a class JDK-8213766 PATCH Assertion error in TypeAnnotations$TypeAnnotationPositions.resolveFrame JDK-8260053 PATCH Optimize Tokens’ use of Names JDK-8260566 PATCH Pattern type X is a subtype of expression type Y message is incorrect JDK-8200145 PATCH Conditional expression mistakenly treated as standalone JDK-8264696 PATCH Multi-catch clause causes compiler exception because it uses the package-private supertype JDK-8263642 PATCH javac emits duplicate checkcast for first bound of intersection type in cast JDK-8265899 PATCH Use pattern matching for instanceof at module jdk.compiler(part 1) JDK-8265900 PATCH Use pattern matching for instanceof at module jdk.compiler(part 2) JDK-8265901 PATCH Use pattern matching for instanceof at module jdk.compiler(part 3) JDK-8266625 PATCH The method DiagnosticSource#findLine returns wrong results when using the boundary values JDK-8266675 PATCH Optimize IntHashTable for encapsulation and ease of use JDK-8266796 PATCH Clean up the unnecessary code in the method UnsharedNameTabl#fromUtf JDK-8266819 PATCH Separate the stop policies from the compile policies completely JDK-8267355 PATCH Adjust the parameters of the method UnicodeReader#digit JDK-8267361 PATCH JavaTokenizer reads octal numbers mistakenly JDK-8267570 PATCH The comment of the class JavacParser is not appropriate JDK-8267578 PATCH Remove unnecessary preview checks JDK-8267580 PATCH The method JavacParser#peekToken is wrong when the first parameter is not zero JDK-8266239 PATCH Some duplicated javac command-line options have repeated effect JDK-8263926 PATCH JavacFileManager.hasExplicitLocation fails with NPE while compiling JDK-8268670 PATCH yield statements doesn’t allow ~ or ! unary operators in expression JDK-8267610 PATCH NPE at at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop JDK-8269738 PATCH AssertionError when combining pattern maching and function closure JDK-8269113 PATCH Javac throws when compiling switch (null) JDK-8268894 PATCH forged ASTs can provoke an AIOOBE at com.sun.tools.javac.jvm.ClassWriter::writePosition JDK-8271254 PATCH javac generates unreachable code when using empty semicolon statement JDK-8273408 PATCH java.lang.AssertionError: typeSig ERROR on generated class property of record JDK-8274942 PATCH AssertionError at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:155) JDK-8276836 PATCH Error in javac caused by switch expression without result expressions: Internal error: stack sim error JDK-8286573 PATCH Remove the unnecessary method Attr#attribTopLevel and its usage hotspot JDK-8227106 PATCH InitiatingHeapOccupancyPercent is G1-specific but defined in shared JDK-8250888 PATCH nsk/jvmti/scenarios/general_functions/GF08/gf08t001/TestDriver.java fails JDK-8268368 PATCH Adopt cast notation for JavaThread conversions JDK-8278104 PATCH C1 should support the compiler directive ‘BreakAtExecute’","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"},{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"},{"name":"hotspot","slug":"hotspot","permalink":"http://lgxbslgx.github.io/tags/hotspot/"},{"name":"JVM","slug":"JVM","permalink":"http://lgxbslgx.github.io/tags/JVM/"},{"name":"Javac","slug":"Javac","permalink":"http://lgxbslgx.github.io/tags/Javac/"}]},{"title":"OpenJDK的JDK-8254557解决过程","slug":"OpenJDK-8254557","date":"2020-10-26T05:01:22.000Z","updated":"2021-07-15T05:10:26.812Z","comments":true,"path":"2020/10/26/OpenJDK-8254557/","link":"","permalink":"http://lgxbslgx.github.io/2020/10/26/OpenJDK-8254557/","excerpt":"","text":"相关链接 OpenJDK javac compiler 资料 javac邮件列表 关于javac的一个小总结 JDK-8254557 我的Pull Request OpenJDK的测试工具 jtreg 基础知识很多具体的内容都可以在 相关链接 里面找到。这里写一些基本的信息,让没时间深入理解的人也可以大概了解情况。 编译器前端的基本组成javac的作用是把java源程序翻译成中间代码,以编译原理的角度,就是一个编译器前端。下面简述一个编译器前端的组成。 词法分析(lexical analysis, lexer, scanner): 把源程序(一串字符流)进行分词,产生词汇流(token list)。这些词汇也叫token。 语法分析(parser): 读取token流,分析语法是否正确,产生抽象语法树(abstract syntax tree, AST)。 语义分析(semantic analysis): 分析AST的语义是否正确,生成一些分析的结果(可以简单称这些结果为属性attribute),形成一个新的树(也可以叫AST)。 中间代码生成(code generate): 根据AST树,产生中间代码。 javac的基本步骤具体语言的编译器前端都有一些特殊的内容,javac的具体步骤如下: 1.Parser(使用scanner和tokenizer): 词法分析和语法分析。把scanner和parser两步一起完成,减少一次完整遍历,是编译器的常规操作。 2.Enter: 生成符号,初始化符号表。同时也生成一些类型信息,验证一些注解(使用了Attr)是否正确。 3.Annotation processing: 注解处理。 4.Attr: 分析名称和表达式。 5.Flow: 分析程序流程。 6.Desugar: 简化语法糖。 7.Code generation: 中间代码生成。 简单的理解和归纳:上文的第1点就是编译器前端的词法分析和语法分析,上文的2-6就是语义分析,7就是中间代码生成。 问题描述详见链接 JDK-8254557 。下面简单描述一下。有人使用类似下面这段代码的时候,javac编译出错。这个错误不是指javac认为源程序有问题而报错,而是javac本身出现问题,没办法继续运行了。 12345678910111213141516171819202122232425262728293031323334353637import java.util.Iterator;import java.util.function.Function;public class T8254557 { // test anonymous class in if statement public <T> void testIf(boolean b) { test(rs -> { if (b) { return new Iterator<>() { @Override public boolean hasNext() { return true; } @Override public T next() { return null; } }; } else { return new Iterator<>() { @Override public boolean hasNext() { return true; } @Override public T next() { return null; } }; } }); } private void test(Function function) { }} JDK-8254557 里面也有一个例子,和我这个类似。上面的例子是我最后提交补丁的时候带的单元测试例子。而报告者也指出如果把new Iterator<>写成new Iterator<T>,就可以编译成功,不会报错。 解决过程寻找原因 从 JDK-8254557 里的报错信息可以大概知道: javac在判断一个类型是不是另一个类型的子类型时,javac出错了。 我根据错误信息里的调用栈,在代码里打断点一步一步的调试。因为对代码不熟悉,我选择从调用栈第一步jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)开始。 调试完后,我知道了问题出现的具体场景: 验证注解@Override能否作用在方法(method) public boolean hasNext()上。@Override大家都很常用,作用在子类的重写方法上,很明显例子代码的这种写法是对的。 这个验证流程可以抽象为: 先获取@Override可以作用的类型,@Override只能作用在方法(method)上。 获取public boolean hasNext()的类型,这是一个方法,理论上类型应该是方法。 然后比较两个类型是否兼容(是否是子类型),这就和刚刚说的报错信息相对应了。 按照上面的验证流程,为什么会报错呢?原来获取public boolean hasNext()的类型为Unknown,而不是method。这个流程根本不懂得怎么处理一个Unknown类型,只能向上一层抛出错误。 原因总结: javac前面的步骤把本应该是method类型的信息解析成了一个Unknown类型,使得验证注解@Override的可作用类型时出错。 尝试解决 思路: 既然这次验证不懂得怎么处理Unknown类型,那我在遇到Unknown类型的时候,先调用适当的方法重新分析类型,再进行验证。 我发现错误信息的调用栈上有一个方法Annotation.attributeAnnotationValues,它在验证之前先判断类型是否为空,为空的话,就分析其类型。代码如下: 123456789private List<Pair<MethodSymbol, Attribute>> attributeAnnotationValues(JCAnnotation a, Type expected, Env<AttrContext> env){ Type at = (a.annotationType.type != null ? a.annotationType.type : attr.attribType(a.annotationType, env)); a.type = chk.checkType(a.annotationType.pos(), at, expected); // 省略下面的代码} 那能不能再判断一下类型是否为Unknown,是Unknown的话,就分析其类型呢?我基于这个想法,把代码修改如下。这是现在总结的时候临时写的,当时具体的代码已经没有了。使用这段代码可能报错。 123Type at = ((a.annotationType.type != null && a.annotationType.type != UnknownType) ? a.annotationType.type : attr.attribType(a.annotationType, env)); // 加了对`Unknown`类型的判断a.type = chk.checkType(a.annotationType.pos(), at, expected); // 这句没变 我写了一个单元测试,测试我的解决方案是否正确。单元测试代码 类似 问题描述那节的例子代码和这个 bug JDK-8254557 里的代码。 OpenJDK的单元测试使用了一个叫jtreg的内部工具,因为OpenJDK开始开发的时候,junit这些单元测试框架还没出现,OpenJDK内部只能自己写一个单元测试工具。jtreg的详细内容读者可以自己看上面的链接,而我最终补丁里的单元测试内容可以看我的PR。 结果是单元测试没通过,我的解决方案中,返回的变量at依然是Unknown。 总结: 这个解决方案不管Unknown类型是怎么得来的,只对Unknown类型的内容再重新分析类型,最终还是没有解决问题。 寻找根本原因 思路: 找到最开始分析出Unknown类型的地方,那里的代码极有可能出错了。 这个bug出现的地方属于上面javac基本步骤里的Attr,那分析出Unknown类型的代码应该在前面的Parser和Enter阶段。 我继续调试代码,主要是Parse和Enter阶段的代码,也有Attr阶段的代码。(我发现Enter的有些过程用到了Attr的一些方法,Attr也会使用Enter做一些操作,这个和我之前理解的严格分阶段有些不同,文档也没有强调说明,这种坑只能自己踩了) 最终发现问题所在: 首先,Attr会递归分析所有名称和表达式的类型。但是当分析匿名内部类的时候,有一种情况,Attr会跳过匿名内部类,在分析完所有地方之后,再回来分析刚刚忽略的匿名内部类。这种情况极其特殊,很难出现,具体判断代码为: if (isDiamond && ((tree.constructorType != null && inferenceContext.free(tree.constructorType)) || (tree.clazz.type != null && inferenceContext.free(tree.clazz.type))))。isDiamond表示是否为棱型泛型,也就是<>,注意尖括号里面什么都没有。看到这个是不是觉得有点熟悉,前面问题描述里面提到把<>改成<T>就没有bug了。 这种情况还要求默认构造函数需要含有泛型,或者匿名类类型含有泛型。换句话说,就是你写代码的时候匿名类上写的是<>,而javac分析之后,把你的类型或者构造函数类型变成了<T>。这种条件极其苛刻,以至于你把问题描述例子代码里的 方法test()、lambda表达式、if语句、使用<>匿名类 的其中一层去掉,都无法重现问题。 另一方面,Attr会在分析完 lambda表达式、if语句、while语句、do while语句、for语句 之后,调用一个preFlow方法,preFlow把这些语句里面 未分析出类型的所有内容,都置成了Unknown类型。这样做是合理的,因为分析完一棵子树之后,没分析出一些内容的类型,说明这些内容可能出现了语义错误。为了避免以后的处理出现空指针,只能把这些类型置为Unknown。 刚刚说的两个方面都是合理的。因为<>匿名类可能需要其他地方的信息才能推导出具体的泛型信息,所以要推迟分析; 而类型为空,会让之后的flow阶段报空指针错误,所以要置为Unknown类型。但是这两种情况一结合,就出现bug了。在 lambda表达式、if语句、while语句、do while语句、for语句 里面的 满足第一种情况的<>匿名类,会被preFlow把匿名类里面所有内容的类型置为Unknown。从而导致了后来Attr使用类型时,遇到Unknown报错。 分析到这里,我们大概知道解决方案:修改preFlow方法,让它不修改刚刚跳过的<>匿名类内容的类型(即不把刚刚跳过的内容置为Unknown,让它为空)。 在看preFlow方法的时候,根据一些注释内容,我发现一些额外信息: 之前有一个类似的bug JDK-8203277。对应的解决方法在 这里。它解决了lambda表达式里面使用preFlow出现的问题。 而 JDK-8231826 解决过程中,提交了一些 代码。这些代码在 Attr分析 if语句、while语句、do while语句、for语句 的时候使用preFlow, 导致了现在这个问题 JDK-8254557。这可以说是bug JDK-8203277 的回退(regression)。 总结: preFlow导致了不必要的Unknown类型产生,有些需要使用类型的程序无法理解和解析Unknown,从而抛出错误。 最终解决方案 JDK-8203277 的 解决方案 重写了PostAttrAnalyzer的visitClassDef和visitLamda方法,如下所示。可以看到visitClassDef和visitLamda方法里面没有内容,表示所有匿名类和lambda表达式preFlow都不检查(即对里面真正错误的内容都没管,没设置为Unknown)。这其实不是最优的方案。 123456789101112131415161718192021222324void preFlow(JCLambda tree) { attrRecover.doRecovery(); new PostAttrAnalyzer() { @Override public void scan(JCTree tree) { if (tree == null || (tree.type != null && tree.type == Type.stuckType)) { //don't touch stuck expressions! return; } super.scan(tree); } @Override public void visitClassDef(JCClassDecl that) { // or class declaration trees! } public void visitLambda(JCLambda that) { // or lambda expressions! } }.scan(tree.body);} 我的解决方案在它基础上改善了一些内容。我在visitClassDef和visitLamda方法里面做了条件判断,让preFlow不检查这种没解析过的匿名类和lambda表达式。而对于已经解析过的匿名类和lambda表达式,preFlow一样检查和设置Unknown类型。代码如下所示: 1234567891011121314151617181920212223242526272829303132333435void preFlow(JCTree tree) { attrRecover.doRecovery(); new PostAttrAnalyzer() { @Override public void scan(JCTree tree) { if (tree == null || (tree.type != null && tree.type == Type.stuckType)) { //don't touch stuck expressions! return; } super.scan(tree); } @Override public void visitClassDef(JCClassDecl that) { if (that.sym != null) { // Method preFlow shouldn't visit class definitions // that have not been entered and attributed. // See JDK-8254557 and JDK-8203277 for more details. super.visitClassDef(that); } } @Override public void visitLambda(JCLambda that) { if (that.type != null) { // Method preFlow shouldn't visit lambda expressions // that have not been entered and attributed. // See JDK-8254557 and JDK-8203277 for more details. super.visitLambda(that); } } }.scan(tree);} JDK-8254557 描述里面只有 if语句 对应的测试案例。我在阅读源码时候,知道 while语句、do while语句、for语句也有对应的问题。所以我加上了对应的测试案例,完善了我之前写的单元测试。 具体代码 提交补丁以及社区交流 OpenJDK从JDK16开始从Mercurial迁移到Git,并且使用Github的Pull Request进行代码review。详情请见: JDK 16, JEP 357: Migrate from Mercurial to Git 和 JEP 369: Migrate to GitHub 和常规的Github Pull Request操作一样。新建分支JDK-8254557,提交对应的代码,push到自己的 Github仓库, 然后提 Pull Request. 如果没有签OCA的话,需要签OCA,我已经签过了。签OCA的流程如下: 下载 OCA,写相关信息并签名,拍一个照片发给邮箱[email protected],Oracle审核完之后,就会回复你。更多详情如下: OCA contributor 签完OCA后,第一次在Github贡献代码,需要在PR的评论里面写/signed,表示你已经签过OCA,然后Oracle的人就会发邮件问你这个Github帐号是不是你的。回复邮件确定就可以了。可以看 这里 了解具体操作。 之后就会有人来review你的代码,代码需要1到多个人review,具体人数 根据代码修改的内容来定,如果是虚拟机的代码就需要至少2个人review。 review完成后,需要在评论里面写/integrate进行集成,如果你的帐号有commit权限(OpenJDK的Committer以上才有),代码就会自动被合成。如果没有,就会有一个Committer以上的人在评论里写/sponsor,以他的帐号提交你的代码(这个patch的贡献者还是你)。一般review你代码的人,会顺便sponsor的,如果隔几天没人sponsor,你可以在评论里面问一下。 遇到问题可以多看一下别人的PR,和根据机器人指示看对应的文档。 题外话 注意: JDK-8203277 是一个影响OpenJDK9、10、11的bug,在当时的开发主线(main line, 也就是当前正在开发的最新版本)JDK12得到了解决。2018年9月OpenJDK11发布,这个问题在2018年11月解决,刚好错过了。我现在使用OpenJDK11的最新代码测试,bug还存在。不知道什么原因,这个补丁没有backport(向后移植)到之前的版本。而我解决的 JDK-8254557 则是在OpenJDK11、14、15、16(9和10已经不再维护,14也将不再维护)都出现的问题,然而我的补丁也只是提交到开发主线(当前的开发主线OpenJDK16),也就是说JDK11、14、15还是没有解决这个问题的。 友情提示: 公司技术选型,一定要选8和11这种长期支持的版本,而且要及时更新!要不然掉坑里都不知道。 如果大家对OpenJDK感兴趣,欢迎发邮件和我交流。","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"},{"name":"OpenJDK","slug":"OpenJDK","permalink":"http://lgxbslgx.github.io/tags/OpenJDK/"},{"name":"Compiler","slug":"Compiler","permalink":"http://lgxbslgx.github.io/tags/Compiler/"},{"name":"Javac","slug":"Javac","permalink":"http://lgxbslgx.github.io/tags/Javac/"}]},{"title":"spring-framework的issue-22675解决过程","slug":"Commit-the-code-to-spring-framework-for-the-first-time","date":"2019-05-14T15:39:52.000Z","updated":"2021-07-15T05:10:26.812Z","comments":true,"path":"2019/05/14/Commit-the-code-to-spring-framework-for-the-first-time/","link":"","permalink":"http://lgxbslgx.github.io/2019/05/14/Commit-the-code-to-spring-framework-for-the-first-time/","excerpt":"","text":"相关链接 spring-framework issue-22675 issue-22675 相关的提问 我的PR 修复版本5.2.0.M1 问题描述当同一个类有多个bean定义,Spring在注入的时候,不知道使用哪个bean定义,从而提示bean定义重复,应用也会启动失败。如果在其中一个bean定义加上@Primary注解,Spring就可以优先使用该bean定义,应用就可以正常启动运行。这是Spring已有的功能。 issue-22675 报告了一种特殊情况,在这个情况下这个功能无法生效,即@Primary不起作用。使用了@Primary之后,Spring还是提示了bean定义重复。这种特殊情况就是:@Primary作用在FactoryBean的bean定义。代码例子可以看上面的相关链接。 解决过程1. 准备阶段因为这是我在Spring项目第一个bug的解决过程,所以我把准备阶段写的详细一点。一方面是为了总结,另一方面是给想参与开源项目的人一些指导。首先我的目标很明确:理解Spring源码、为Spring解决bug、进而希望为Spring实现新功能。有了这个目标之后,做了一些基础工作和学习积累。准备的内容有: 复习设计模式,看了《head first 设计模式》。因为最开始翻代码一脸迷茫,发现设计模式不熟悉,赶紧补上。 看Spring官方文档:要理解Spring源码,一定要先知道Spring能做什么,之前只是在项目上用Spring,对Spring能做的事情不够全面。这次看了文档前三部分,即看完 核心技术(Core Technologies)。 看《Spring源码深度解析》和《Spring技术内幕》,也只看了对应的核心部分:core、beans、context、aop、aspects这些模块。 (这个方法不可取,别学)看模块的详细源码,当时是每个包都导出类的关系图(感谢Idea强大的功能),然后一个一个类地去看源码。看完了core模块的代码感觉还好,看beans模块两天直接放弃。 看《Expert One-on-One J2EE Development without EJB》,有了之前对Spring的初步理解,然后就看了这本经典著作。 再次学习Gradle,因为spring-frame项目是Gradle构建的。大学开发Android应用有Gradle使用经验,这次只是回头看一下基本用法。这里只是强调构建工具也是需要学习的一个方面。 有了这些准备,我就开始在GitHub上看Spring的issue了。没有捷径,一个一个地看,理解问题,看自己能不能解决。下面开始这个bug的解决过程。 2. 知识回顾虽然看了一遍Spring官方文档,但是遇到具体功能,还是不能立刻想起功能的作用和用法。根据issue的描述,我重新看了下面的内容: @Configuration和@Bean配置bean的方法 @Primary的使用 FactoryBean的使用方法,对于解决这个bug很关键。 3. 构建测试demo使用spring-boot搭建一个测试demo,准备开始代码调试之旅。(用spring-boot为了方便搭建,直接用原始的Spring也行) 4. 调试根据demo报错的调用栈,把栈上层的几个类和方法打上断点。然后在两种不同的情况(使用FactoryBean和不使用FactoryBean)下,不断调试代码。调试期间,看注释和上网搜索这些类和方法的作用,推测可能出现问题的类和方法。比较这两种情况,看哪里不一样。这个调试过程及其漫长和需要耐心。当然之前的准备和对源码的理解也派上了用场。(顺便说一下,Idea的调试功能太好用了,疯狂安利Idea)面对一个大型的不熟悉的项目,这种调bug的方式,还是比较好用的。 5. 调试结果原来是bean别名和FactoryBean名称使用的问题。在使用FactoryBean的时候,要获取FactoryBean创建的bean,只需要原始bean名称:”beanName”,如果要获取FactoryBean本身,需要在前面加上”&”,即bean名称为:”&beanName”。在使用别名搜索或获取bean的时候,系统要把别名转换成权威的名字(canonical name),再用这个canonical name去做操作。当用到bean的名称的时候,系统都需要把前面的&去掉,并且转换成canonical name,再进行其他操作。这个步骤由AbstractBeanFactory的transformedBeanName方法来完成的。下面举个例子 1234567891011AbstractBeanFactory.java@Overridepublic boolean containsBean(String name) { String beanName = transformedBeanName(name); if (containsSingleton(beanName) || containsBeanDefinition(beanName)) { return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name)); } // Not found -> check parent. BeanFactory parentBeanFactory = getParentBeanFactory(); return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));} 上面这个方法根据bean的名称,查看bean是否在容器中。它不信任输入,认为输入的名称不一定是canonical name。所以它使用transformedBeanName进行了处理,拿到canonical name,再根据这个canonical name去看有没有包含相应的bean,所以没有出现问题。 123456789DefaultListableBeanFactory.javaprotected boolean isPrimary(String beanName, Object beanInstance) { if (containsBeanDefinition(beanName)) { return getMergedLocalBeanDefinition(beanName).isPrimary(); } BeanFactory parent = getParentBeanFactory(); return (parent instanceof DefaultListableBeanFactory && ((DefaultListableBeanFactory) parent).isPrimary(beanName, beanInstance));} 而上面这个方法,在使用beanName之前没有做处理。如果输入是带”&”的名称或者是bean别名,根据bean名称找不到对应的bean定义,isPrimary将会为false。同一个类的全部bean定义的isPrimary都为false,Spring不知道选择哪个,就会出现issue描述中的问题(提示bean定义重复,应用启动失败)。 6. 解决方法分析到了这里,解决就变得简单了,使用beanName之前,先使用transformedBeanName处理一下。代码如下: 123456789protected boolean isPrimary(String beanName, Object beanInstance) { String transformedBeanName = transformedBeanName(beanName); if (containsBeanDefinition(transformedBeanName)) { return getMergedLocalBeanDefinition(transformedBeanName).isPrimary(); } BeanFactory parent = getParentBeanFactory(); return (parent instanceof DefaultListableBeanFactory && ((DefaultListableBeanFactory) parent).isPrimary(transformedBeanName, beanInstance));} 但是这就行了吗?当然不行!时刻谨记,开源项目的代码将会被很多公司、组织和个人使用。添加代码之后,必须添加对应的测试代码。修改代码之后,必须运行之前的测试代码(回归测试)。我添加了两个测试用例,并且运行了那个包的所有测试用例,才提交代码,提PR。测试代码可以看上面的相关链接。 题外话之前一直认为GitHub可以搜索一个人的全部code、comment、issue、pull request,没必要再写博客汇总自己做的一些事情。但是最近在tomcat邮件列表进行一些讨论后,越来越觉得GitHub的搜索功能并不能满足自己的全部需求。 一方面,自己参与和感兴趣的项目可能不在GitHub进行讨论。这些讨论分布在了GitHub、邮件、以及项目单独的issue tracker里面,很难汇总。另一方面,对问题的讨论过程和解决方法进行回顾和总结,可以加深对项目的理解。这种分享也可以启发和帮助到对相关问题有困惑的人。spring-framework的issue-22675是5月份解决的一个bug了,现在10月份再写解决过程,一些细节已经记不清楚了,只能粗略记录。这些细节的遗忘也说明了及时写博客的重要性。今后要坚持写博客,利人利己。 如非必要,我尽量不会贴一堆源码和对源码进行大量的分析。因为对于知名的开源项目,其主要的架构和源码已经有很多人分析并且写了博客或者出版了相关书籍。我主要想提供问题解决的思路和过程,为想参与开源项目的人提供一些指导。如果一些具体细节在网上搜索不到很好的分析过程,我也会进行一些分析。最主要是避免重复。现在用中文搜索技术问题,有些时候前半页都是一样的内容。虽然发布者会在里面加上一条引用或出处的链接,但是我感觉这种重复是很没必要和略不负责任的行为。","categories":[],"tags":[{"name":"spring-framework","slug":"spring-framework","permalink":"http://lgxbslgx.github.io/tags/spring-framework/"},{"name":"Java","slug":"Java","permalink":"http://lgxbslgx.github.io/tags/Java/"}]}]}