maven springboot 除去指定的jar包_SpringBoot的运行机制

SpringBoot打包机制

先看一眼spring-boot的maven插件打包后的target目录:

2c77053ba9fb4ebef732a081416c1dda.png

其中有一个.jar.original的文件,一个.jar文件,其中.jar.original才是原始的jar包,而.jar文件是经过spring-boot的maven插件处理过后的jar,springboot的maven插件会将原始jar重命名成.jar.original,然后按springboot自己的规范打出一个可执行的jar包。

将该jar包重命名成.zip文件后打开即可看到文件中的内容:

e973bf68d4fccbc53d24b3e7257b3389.png

可以看到,springboot执行的jar并不是一个java标准的jar,其中包含了springboot自身定义的内容。我们再打开其中的META-INF/MANIFEST.MF文件,看看其中指定的内容:

0b7c6d18017e7309876906e4d74a4986.png

Main-Class是其中的启动类,springboot打包出来的jar,启动类并不是工程中包含main方法的启动类,而是springboot自己的JarLauncher类,而工程中定义的启动类在这里变成了Start-Class,由此也可以看出springboot应用在IDE里通过main方法运行与通过java -jar命令运行的区别。

SpringBoot应用的启动

打开springboot的Main-Class JarLauncher类,其中包含main方法:

6df67e4c646566866ad5c591c4d63955.png

这是springboot启动的入口,JarLauncher类继承自ExecutableArchiveLauncher类,进入到launch方法可以看到springboot有一个archive的概念,archive是归档的意思,springboot打出来的jar包就是一个archive。

ExecutableArchiveLauncher类的createArchive方法可以看到启动时的archive创建,通过当前类找到jar包的路径,并创建JarFileArchive:

7a7de14505e08c7f29d4c9b63cf0c5ef.png

通过getNestedArchives方法可以看到Archive是一个递归的概念,JarFileArchive中可以有其它嵌套的Archive:

a2d20aafa81d104deb873b3ef72fe2cf.png

在launch方法中,有getClassPathArchives方法的调用,此方法中调用了前面的getNestedArchives方法,传入的Filter是lambda表达式,通过isNestedArchive方法对JarFileArchive中的Entry做过滤:

17ad093897d41e248b0b878bccbee9fd.png

b05f95620491059b1251b5120f165f84.png

可以得知,springboot打出的archive jar包中,ROOT-INF/classes/目录被认为是一个嵌套的Archive,ROOT-INF/lib/下的每一个jar包也被认为是一个Archive

因为springboot的archive不是一个标准的jar包,java提供的ClassLoader无法加载到archive中的依赖以及class,springboot提供了新的classloader的实现用来做Archive中类的加载:

119f71103a77f464095641e826e61fc0.png

可以看到springboot使用的是LaunchedURLClassLoader这个ClassLoader做的类的加载。创建好类加载器后,需要调用Start-Class,即应用中的main方法:

8454bead573c3814e8ce049c324f7abc.png

MainMethodRunner.run方法非常简单,逻辑是通过反射调用应用的main:

8e259a8db4bbbd842bedae693c27d971.png

这一步之后,进入到了应用代码中。

SpringBoot应用初始化

在使用spring boot时,我们在main方法中调用SpringApplication.run进行初始化:

a7061af8d13cc428cb8b0562d6df6c60.png

在SpringApplication.run方法中做了Spring的初始化:

2efcb0a2b179f8ecaeff35f4c420d612.png

可以清晰的看到spring的初始化。创建ApplicationContext对象使用的是createApplicationContext方法,此方法实现如下:

929bfc7494a7ac054039f16b79576d33.png

对于非WEB应用,使用的ApplicationContext的实例是DEFAULT_CONTEXT_CLASS,此常用定义为AnnotationConfigApplicationContext类,在依赖注入中提到的这个类,此类用于实现注解配置的ApplicationContext。

回到启动类,启动类上加了一个@SpringBootApplication注解,此注解的定义:

b8a5b6dceb52a8ad2c6c3e1384c53864.png

前面Annotation的解析机制中提到,spring能通过解析@ComponentScan注解注册bean,但是springboot中,@ComponentScan标识在SpringBootApplication注解上,AnnotationConfigApplicationContext是定义在spring-context包中的,而@SpringBootApplication是定义在spring-boot-autoconfig中的注解,spring-context包并不依赖于spring-boot-autoconfig包,AnnotationConfigApplicationContext能通过@SpringBootApplication注解完成初始化是因为spring的注解处理工具类能识别出@SpringBootApplication的元注解@ComponentScan以及@AliasFor注解标识 的属性,实现逻辑在spring-core包中的AnnotationConfigUtils类中。

最后看看SpringApplication类的构造器:

33cda21c87f120b282d9208ce425d0d0.png

在getSpringFactoriesInstances方法中,使用了SpringFactoriesLoader:

d46189021e6832700e5c7b760a8a5123.png

打开spring-boot相关的包,能看到spring.factories文件:

bcc128571036eb4f221e9eda43de689b.png

SpringFactoriesLoader是spring-core中提供的类,用于处理spring.factories文件,SpringFactoriesLoader.loadFactoryNames方法会读取类路径下的所有META-INF/spring.factories文件:

83dec6ad003ae81952832467e7203c9c.png

AutoConfiguration

回到@SpringBootApplication注解的定义:

41b344ced7acd6935af33964f6ae7cd5.png

其注解上被标记了@EnableAutoConfiguration注解,此注解用于实现autoconfiguration。打开此注解可以看到它实际上是使用了注解解析机制中的@Import注解:

87f86f655d82ba97232f83bbfd0340b7.png

如何使用ImportSelector就回到了@Import的处理逻辑中(见前面的文章)。可以看到,实现autoconfiguration的入口正是这个EnableAutoConfigurationImportSelector类,此类的selectImports方法返回的是需要作为配置类的类,这里的具体作用就是拿到类路径下的所有的自动配置的类,其代码实现:

e1cef2ca870c3a32d6dbda62b28bcb7c.png

AutoConfigurationMetadataLoader.loadMetadata方法的实现比较简单

77ca243fb8aef5ac5f5ccfbac0a5c069.png

即从META-INF/spring-autoconfigure-metadata.properties文件中加载数据,此文件在classpath下可以有多个(每个jar包中都可以有一个),随便打开一个配置文件,看看其中的内容:

d435aefd328e77b528a19070b1d036a0.png

里面都是一些配置类。@Import将这些配置类导入给spring的注解处理器ConfigurationClassParser就完成了bean的配置。

另外,在@SpringBootApplication注解上,还标记了一个@ComponentScan注解,其中的excludeFilters中有一个是AutoConfigurationExcludeFilter:

bd09df209e4d4ac502c4dee81ef8bf05.png

此类会判断如果class标了@Configuration并且此类包含spring.factories文件中,则在处理@Configuration注解时排除此类:

9fb24234eb1c1abb4f5f1ae000aa0c75.png

上面的代码中,getAutoConfigurations方法是从SpringFactoriesLoader中取的EnableAutoConfiguration关联的类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Spring Boot中,如果两个不同的jar含相同的名和类名,那么在加载这两个jar时可能会出现冲突。 当我们启动Spring Boot应用时,会依次加载classpath下的所有jar。在加载过程中,如果发现两个jar中存在相同的名和类名,Spring Boot将无法区分它们,从而导致加载冲突。 为了解决这个问题,可以使用以下方式之一: 1. 排除冲突类:在pom.xml(maven项目)或build.gradle(gradle项目)中,对引入的冲突jar进行排除的操作。可以将其中一个jar排除掉,从而避免加载冲突。 2. 修改名:如果可以更改jar中的代码,可以尝试修改其中一个jar中的名或类名,使其与另一个jar中的名或类名不再冲突。然后重新打并引入修改后的jar。 3. 使用ClassLoader隔离:可以自定义一个ClassLoader来加载其中一个冲突的jar。通过使用不同的ClassLoader,可以实现对每个jar的独立加载,从而避免冲突。 需要注意的是,在解决这个问题时,我们应该谨慎确保冲突的jar不会影响应用程序的运行,同时应该尽量避免引入具有相同名和类名的jar。如果无法避免这种情况,我们可以使用上述方法中的一种来解决冲突问题。 ### 回答2: 当一个应用程序中存在两个含相同的名和类名的jar时,Spring Boot会根据默认的类加载机制来加载类。默认情况下,Spring Boot使用的是Java的标准类加载器来加载类,而标准类加载器遵循委派模型。 根据委派模型,当需要加载一个类时,标准类加载器会首先检查自身是否已经加载了这个类。如果已经加载,则直接返回该类的实例;如果没有加载,则会将这个类的加载请求委派给父类加载器进行处理。 在这种情况下,当存在两个含相同名和类名的jar时,标准类加载器会根据类加载路径的顺序逐个加载这些jar,直到找到所需的类。如果两个jar中的类都符合要求,那么标准类加载器会选择路径上先出现的那个jar中的类作为被加载的类。 如果开发者想要显式地选择使用其中一个jar中的类,可以通过修改类加载路径的顺序来实现。可以在Spring Boot的配置文件(application.properties或application.yml)中将依赖的jar添加到`spring.autoconfigure.exclude`属性中,使得在加载类时跳过不需要的jar。 总结起来,当存在两个含相同名和类名的jar时,Spring Boot会根据标准类加载器的委派机制来加载类。可以通过配置类加载路径的顺序来明确选择所需的jar中的类。 ### 回答3: 在Spring Boot中存在两个含完全相同名和类名的jar,这会导致加载问题。当程序运行时,ClassLoader会按照特定的顺序搜索类文件并加载到内存中。由于这两个jar中的类名和名完全相同,ClassLoader会优先加载位于Classpath中的第一个jar中的类。 如果这两个jar中的类内容相同,不会出现任何问题,因为ClassLoader只会加载其中一个类。但是,如果这两个类内容不同,将会导致加载错误,程序可能无法正常运行。 为了解决这个问题,我们可以采取以下几种方法: 1. 删除重复的jar:如果这两个jar是由于误操作或其他原因导致了重复的,我们可以通过删除其中一个来解决问题。 2. 修改类的名:如果两个类内容不同但名相同,我们可以通过修改其中一个类的名来避免冲突。 3. 使用不同的ClassLoader加载:我们可以通过自定义ClassLoader加载其中一个jar,从而避免冲突。可以通过在Spring Boot的启动类中指定ClassLoader实现这一点。 总之,解决Spring Boot中两个含完全相同名和类名的jar加载问题的方法是删除重复的jar、修改名或使用不同的ClassLoader加载。通过这些方式,我们可以避免加载错误并确保程序的正常运行

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值