【问题标题】:Spring boot devtools load twice of a class and cause LinkageErrorSpring boot devtools加载两次类并导致LinkageError
【发布时间】:2015-12-03 04:18:51
【问题描述】:

IDE是Idea,Spring boot项目有META-INF/spring-devtools.properties,内容是

restart.include.dozer=/dozer-5.5.1.jar

运行项目时抛出异常

2015-12-03 12:02:49,491 [restartedMain] INFO  org.dozer.DozerBeanMapper - Initializing a new instance of dozer bean mapper.
2015-12-03 12:02:49,494 [restartedMain] WARN  o.s.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dozerBeanMapper' defined in class path resource [com/foo/common/CommonConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dozer.Mapper]: Factory method 'dozerBeanMapper' threw exception; nested exception is java.lang.LinkageError: loader constraint violation: loader (instance of org/springframework/boot/devtools/restart/classloader/RestartClassLoader) previously initiated loading for a different type with name "org/dozer/DozerBeanMapper"
2015-12-03 12:02:49,517 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dozerBeanMapper' defined in class path resource [com/foo/common/CommonConfiguration.class]: Bean instantiation via factory method failed; nested exception is    org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.dozer.Mapper]: Factory method 'dozerBeanMapper' threw exception; nested exception is java.lang.LinkageError: loader constraint violation: loader (instance of org/springframework/boot/devtools/restart/classloader/RestartClassLoader) previously initiated loading for a different type with name "org/dozer/DozerBeanMapper"
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]

奇怪的是,在 Eclipse 中运行项目时,没有出现上述问题。 在idea中添加-XX:+TraceClassLoading配置,发现dozer加载了两次。

9569 [Loaded org.dozer.DozerBeanMapper from file:/C:/Users/otto/.m2/repository/net/sf/dozer/dozer/5.5.1/dozer-5.5.1.jar]

9618 [Loaded org.dozer.DozerBeanMapper from file:/C:/Users/otto/.m2/repository/net/sf/dozer/dozer/5.5.1/dozer-5.5.1.jar]

现在不知道怎么定位这个问题的原因?

【问题讨论】:

  • 你能分享一个重现问题的小样本吗?
  • @Andy Wilkinson 嘿,我创建了一个极简主义样本--gist.github.com/zhugw/63e98f99098156e8173a。运行 FooAplication,你可能会产生那个错误。
  • 您能否将实际项目推送到某个地方的 Git 存储库中?我想确定我正在运行与您完全相同的代码,并且从一个要点将它们拼凑在一起意味着情况可能并非如此。
  • @Andy Wilkinson 对不起!我已经将该项目上传到 github--github.com/zhugw/sample.git

标签: intellij-idea spring-boot classloader


【解决方案1】:

您共享的项目的自述文件中有一些关键信息未包含在问题中:

下载示例项目然后将该项目导入eclipse,关闭栏项目然后运行FooApplication你可能会得到以下错误

关闭 bar 项目意味着它的类将没有资格重新启动。这意味着它们将由主类加载器而不是重新启动类加载器加载。 bar 中的代码依赖于 Dozer,但是您已经配置了 DevTools,以便重新启动类加载器加载 Dozer 的类:

restart.include.dozer=/dozer-5.5.1.jar

我不清楚你为什么要这样配置。如果我删除 spring-devtools.properties,您的示例对我来说很好。也许示例没有说明导致您应用该配置的问题?

如果您确实需要此配置并且不想在 Eclipse 中打开 bar 项目,那么您还需要将 bar 配置为包含在重新启动中,以便重新启动时加载它和它所依赖的 Dozer类加载器:

restart.include.dozer=/dozer-5\.5\.1\.jar
restart.include.bar=/bar-1\.0-SNAPSHOT\.jar

【讨论】:

  • 谢谢!至于为什么要有spring-devtools.properties,请看:github.com/spring-projects/spring-boot/issues/4636
  • 我有点担心spring-devtools.properties的内容会不断增加
  • 见我上面的帖子,DozerBeanMapper 被加载了两次。它在哪里加载?一个地方在 bar 项目中:private final static DozerBeanMapper dozer = new DozerBeanMapper();,另一个地方是 FooApplication 中的@Bean public Mapper fooDozerBeanMapper()。我对吗?哪一个是基础类加载器?因为我已经明确指定restart.include.dozer=/dozer-5\.5\.1\.jar,为什么它们都没有被重启类加载器加载?
  • bar项目由基类加载器加载。发生这种情况是因为项目在 Eclipse 中已关闭,因此 DevTools 知道其类不会更改,因此不会触发重新启动。当 bar 项目中的 new DozerBeanMapper 运行时,它会加载 DozerBeanMapper 类。它必须由基类加载器加载,因为这是加载所有 bar 类的类加载器。 Spring Boot 对此无能为力,这就是 Java 中类加载的工作方式。为了让 Bar 使用重启类加载器加载 Dozer,Bar 也必须由重启类加载器加载。
  • 所以如果 bar 由基类加载器加载(例如在 eclipse 中关闭),它的所有依赖项都由基加载,尽管您明确指定一些依赖项(例如推土机)应该通过重新启动 spring-devtools.properties .但是如果是通过重启类加载器加载的(比如在eclipse中打开bar),情况是diff,这次它的依赖是由基类加载器加载的,所以你必须明确指定重启时应该加载哪个依赖。我说的对吗?
猜你喜欢
  • 2016-02-01
  • 2020-10-24
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多