玩转 Spring Boot 原理篇(源码环境搭建)

Posted 一猿小讲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了玩转 Spring Boot 原理篇(源码环境搭建)相关的知识,希望对你有一定的参考价值。

There were failing tests. See the report at: file:///Users/tangbao/growup/spring-boot-2.6.3/buildSrc/build/reports/tests/test/index.html

org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:75 org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:164 org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:196 org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:135

* src/test/java/org/springframework/boot/build/bom/BomPluginIntegrationTests.java org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:81 java.lang.RuntimeException at BomPluginIntegrationTests.java:168 Caused by: javax.xml.xpath.XPathExpressionException at BomPluginIntegrationTests.java:168 Caused by: javax.xml.transform.TransformerException at BomPluginIntegrationTests.java:168 Caused by: java.lang.RuntimeException at BomPluginIntegrationTests.java:168 Caused by: javax.xml.xpath.XPathExpressionException at BomPluginIntegrationTests.java:168 Caused by: javax.xml.transform.TransformerException at BomPluginIntegrationTests.java:168 Caused by: java.lang.RuntimeException at BomPluginIntegrationTests.java:168 org.opentest4j.AssertionFailedError at BomPluginIntegrationTests.java:198* Copyright 2012-2021 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/* Tests for @link BomPlugin.** @author Andy Wilkinson*/ private File projectDir; private File buildFile; @BeforeEach void setup(@TempDir File projectDir) throws IOException this.projectDir = projectDir; this.buildFile = new File(this.projectDir, "build.gradle"); @Test void libraryModulesAreIncludedInDependencyManagementOfGeneratedPom() throws IOException try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'ActiveMQ\', \'5.15.10\') "); out.println(" group(\'org.apache.activemq\') "); out.println(" modules = ["); out.println(" \'activemq-amqp\',"); out.println(" \'activemq-blueprint\'"); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/activemq.version").isEqualTo("5.15.10"); NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[1]"); assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq"); assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-amqp"); // assertThat(dependency).textAtPath("version").isEqualTo("$activemq.version"); assertThat(dependency).textAtPath("scope").isNullOrEmpty(); assertThat(dependency).textAtPath("type").isNullOrEmpty(); dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[2]"); assertThat(dependency).textAtPath("groupId").isEqualTo("org.apache.activemq"); assertThat(dependency).textAtPath("artifactId").isEqualTo("activemq-blueprint"); // assertThat(dependency).textAtPath("version").isEqualTo("$activemq.version"); assertThat(dependency).textAtPath("scope").isNullOrEmpty(); assertThat(dependency).textAtPath("type").isNullOrEmpty(); ); @Test void libraryPluginsAreIncludedInPluginManagementOfGeneratedPom() throws IOException try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'Flyway\', \'6.0.8\') "); out.println(" group(\'org.flywaydb\') "); out.println(" plugins = ["); out.println(" \'flyway-maven-plugin\'"); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/flyway.version").isEqualTo("6.0.8"); NodeAssert plugin = pom.nodeAtPath("//pluginManagement/plugins/plugin"); assertThat(plugin).textAtPath("groupId").isEqualTo("org.flywaydb"); assertThat(plugin).textAtPath("artifactId").isEqualTo("flyway-maven-plugin"); assertThat(plugin).textAtPath("version").isEqualTo("$flyway.version"); assertThat(plugin).textAtPath("scope").isNullOrEmpty(); assertThat(plugin).textAtPath("type").isNullOrEmpty(); ); @Test void libraryImportsAreIncludedInDependencyManagementOfGeneratedPom() throws Exception try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'Jackson Bom\', \'2.10.0\') "); out.println(" group(\'com.fasterxml.jackson\') "); out.println(" imports = ["); out.println(" \'jackson-bom\'"); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/jackson-bom.version").isEqualTo("2.10.0"); NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency"); assertThat(dependency).textAtPath("groupId").isEqualTo("com.fasterxml.jackson"); assertThat(dependency).textAtPath("artifactId").isEqualTo("jackson-bom"); // assertThat(dependency).textAtPath("version").isEqualTo("$jackson-bom.version"); assertThat(dependency).textAtPath("scope").isEqualTo("import"); assertThat(dependency).textAtPath("type").isEqualTo("pom"); ); @Test void moduleExclusionsAreIncludedInDependencyManagementOfGeneratedPom() throws IOException try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'mysql\', \'8.0.18\') "); out.println(" group(\'mysql\') "); out.println(" modules = ["); out.println(" \'mysql-connector-java\' "); out.println(" exclude group: \'com.google.protobuf\', module: \'protobuf-java\'"); out.println(" "); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/mysql.version").isEqualTo("8.0.18"); NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency"); assertThat(dependency).textAtPath("groupId").isEqualTo("mysql"); assertThat(dependency).textAtPath("artifactId").isEqualTo("mysql-connector-java"); // assertThat(dependency).textAtPath("version").isEqualTo("$mysql.version"); assertThat(dependency).textAtPath("scope").isNullOrEmpty(); assertThat(dependency).textAtPath("type").isNullOrEmpty(); NodeAssert exclusion = dependency.nodeAtPath("exclusions/exclusion"); // assertThat(exclusion).textAtPath("groupId").isEqualTo("com.google.protobuf"); // assertThat(exclusion).textAtPath("artifactId").isEqualTo("protobuf-java"); ); @Test void moduleTypesAreIncludedInDependencyManagementOfGeneratedPom() throws IOException try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'Elasticsearch\', \'7.15.2\') "); out.println(" group(\'org.elasticsearch.distribution.integ-test-zip\') "); out.println(" modules = ["); out.println(" \'elasticsearch\' "); out.println(" type = \'zip\'"); out.println(" "); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/elasticsearch.version").isEqualTo("7.15.2"); NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency"); assertThat(dependency).textAtPath("groupId").isEqualTo("org.elasticsearch.distribution.integ-test-zip"); assertThat(dependency).textAtPath("artifactId").isEqualTo("elasticsearch"); // assertThat(dependency).textAtPath("version").isEqualTo("$elasticsearch.version"); assertThat(dependency).textAtPath("scope").isNullOrEmpty(); // assertThat(dependency).textAtPath("type").isEqualTo("zip"); assertThat(dependency).nodeAtPath("exclusions").isNull(); ); @Test void libraryNamedSpringBootHasNoVersionProperty() throws IOException try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) out.println("plugins "); out.println(" id \'org.springframework.boot.bom\'"); out.println(""); out.println("bom "); out.println(" library(\'Spring Boot\', \'1.2.3\') "); out.println(" group(\'org.springframework.boot\') "); out.println(" modules = ["); out.println(" \'spring-boot\'"); out.println(" ]"); out.println(" "); out.println(" "); out.println(""); generatePom((pom) -> assertThat(pom).textAtPath("//properties/spring-boot.version").isEmpty(); NodeAssert dependency = pom.nodeAtPath("//dependencyManagement/dependencies/dependency[1]"); assertThat(dependency).textAtPath("groupId").isEqualTo("org.springframework.boot"); assertThat(dependency).textAtPath("artifactId").isEqualTo("spring-boot"); assertThat(dependency).textAtPath("version").isEqualTo("1.2.3"); assertThat(dependency).textAtPath("scope").isNullOrEmpty(); assertThat(dependency).textAtPath("type").isNullOrEmpty(); ); // @Test // void versionAlignmentIsVerified() throws IOException // try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) // out.println("plugins "); // out.println(" id \'org.springframework.boot.bom\'"); // out.println(""); // out.println("bom "); // out.println(" library(\'OAuth2 OIDC SDK\', \'8.36.1\') "); // out.println(" alignedWith(\'Spring Security\') "); // out.println( // " // source(\'https://github.com/spring-projects/spring-security/blob/$libraryVersion/config/gradle/dependency-locks/optional.lockfile\')"); // out.println(" pattern(\'com.nimbusds:oauth2-oidc-sdk:(.+)\')"); // out.println(" "); // out.println(" group(\'com.nimbusds\') "); // out.println(" modules = ["); // out.println(" \'oauth2-oidc-sdk\'"); // out.println(" ]"); // out.println(" "); // out.println(" "); // out.println(" library(\'Spring Security\', \'5.4.7\') "); // out.println(" "); // out.println(""); // // System.out.println(runGradle(DeployedPlugin.GENERATE_POM_TASK_NAME, // "-s").getOutput()); // private BuildResult runGradle(String... args) return GradleRunner.create().withDebug(true).withProjectDir(this.projectDir).withArguments(args) .withPluginClasspath().build(); private void generatePom(Consumer<NodeAssert> consumer) runGradle(DeployedPlugin.GENERATE_POM_TASK_NAME, "-s"); File generatedPomXml = new File(this.projectDir, "build/publications/maven/pom-default.xml"); assertThat(generatedPomXml).isFile(); consumer.accept(new NodeAssert(generatedPomXml)); public static void main(String[] args) System.out.println("Spring Boot 源码剖析之源码环境搭建验证"); SpringApplication.run(DemoApplication.class, args); System.out.println("Spring Boot 源码剖析之源码环境搭建验证成功");

直接运行,控制台输出如下。


至此 IDEA + Gradle 7.4 +Spring Boot 2.6.3 源码环境就搭建完成了。


5. 例行回顾

本文是玩转 Spring Boot 原理篇的首篇,主要是一起学习了 Spring Boot 源码环境搭建,看似一个简单的过程,中途也确实遇到了不少问题,不过最终还是成功了。

为了后续能够清晰的读源码,还是需要提前制定目标,提前预设一下问题,这样带着问题去分析学习源码,效果会更好,你会关注 Spring Boot 哪些常见的问题呢?不知你脑海里是否会浮现如下问题呢?

  • Spring Boot 的核心注解有哪些?

  • Spring Boot 自动装配的原理是啥?

  • Spring Boot 启动机制,背后都做了哪些操作呢?

  • Spring Boot 内嵌 Tomcat 是如何启动的呢?

  • ... ...

  • 携带这些主流的问题,让我们一起踏入 Spring Boot 源码学习剖析之门

    玩转 Spring Boot 原理篇(自动装配源码剖析)

    * Return the AutoConfigurationEntry based on the AnnotationMetadata* of the importing Configuration * annotationMetadata the annotation metadata of the configuration class* the auto-configurations that should be imported*/AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) (!isEnabled(annotationMetadata)) EMPTY_ENTRY; AnnotationAttributes attributes = getAttributes(annotationMetadata); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); AutoConfigurationEntry(configurations, exclusions);

    能够发现getCandidateConfigurations 方法中会通过 SpringFactoriesLoader 类来加载类路径中的 META-INF 目录下的 spring.factories 文件中针对 EnableAutoConfiguration 的注册配置类。

    为了调试方便,在源码中的 process 方法上里加入了打印输出。


    运行后,此时控制台输出如下。

  • AutoConfigurationImportSelector.AutoConfigurationGroup.process() , entries = org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.aop.AopAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.cache.CacheAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration=smoketest.simple.SampleSimpleApplicationorg.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration=smoketest.simple.SampleSimpleApplication

    2. 例行回顾

    本文采取 Debug 的方式跟了一下 Spring Boot 自动装配的源码,旨在感受一下自动装配的实现方式,其实这种自动装配的思想,在开发轮子时或许能够借鉴一下,会对轮子的扩展带来质的改变。


    为了方便记忆,把 Spring Boot 自动装配繁琐的流程抽象一下。


    另外 Spring Boot 自动装配源码 Debug 主线,感兴趣可以自行跟一下源码。

    一起聊技术、谈业务、喷架构,少走弯路,不踩大坑,会持续输出更多精彩分享,欢迎关注,敬请期待!

    参考资料:

    https://spring.io/

    https://start.spring.io/

    https://spring.io/projects/spring-boot

    https://github.com/spring-projects/spring-boot

    https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

    https://stackoverflow.com/questions/tagged/spring-boot

    《Spring Boot实战》《深入浅出Spring Boot 2.x》

    《一步一步学Spring Boot:微服务项目实战(第二版)》

    《Spring Boot揭秘:快速构建微服务体系》

    以上是关于玩转 Spring Boot 原理篇(源码环境搭建)的主要内容,如果未能解决你的问题,请参考以下文章

    玩转 Spring Boot 原理篇(内嵌Tomcat实现原理&优雅停机源码剖析)

    玩转 Spring Boot 原理篇(核心注解知多少)

    玩转 Spring Boot 原理篇(自动装配前凑之自定义Stater)

    玩转 Spring Boot 集成篇(任务动态管理代码篇)

    玩转 Spring Boot 入门篇

    Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之自动包规则原理