Spring Boot与外部JAR包的“不是托管类型”问题

7
我有一个Spring应用程序,它引入了一个commons jar。这个jar包含有带注释的DTO类。使用“mvn clean build”命令可以成功运行并构建该jar包。但是一旦我运行“java -jar target/MyApp-1.0.0.BUILD-SNAPSHOT.jar”,就会出现以下错误。
我已经打开了MyApp-1.0.0.BUILD-SNAPSHOT.jar,并发现所有的类都在该jar包中。
我已经打开了包含MyApplicationJobDTO的jar文件并验证该文件是否存在。
尽管类已经存在,但在运行时Java似乎无法找到该类。我不认为我需要将任何内容添加到Java类路径中,因为该JAR位于快照JAR内部。我陷入了困境,不确定为什么会出现运行时错误。
Caused by: java.lang.IllegalArgumentException: Not an managed type: class com...MyApplicationJobDTO

带有注解的类

@Component
@Entity
public class MyApplicationJobDTO implements Serializable {

我的运行命令

mvn clean package && java -jar target/MyApp-1.0.0.BUILD-SNAPSHOT.jar 

我的主类

@EnableJpaRepositories
@ComponentScan
@EntityScan
@Configuration
@EnableAutoConfiguration
public class Application {
    public static void main(String[] args) {

        ApplicationContext context = 
                new ClassPathXmlApplicationContext("file:src/main/java/com/myapp/beans.xml");

        SpringApplication.run(Application.class, args);
    }
}

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

</beans>

错误
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyApplicationJobController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.myapp.xx.yy.models.MyApplicationJobDAO com.myapp.xx.yy.web.MyApplicationJobController.MyApplicationJobDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyApplicationJobDAO': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.myapp.aa.bb.myapptest.commons.dto.MyApplicationJobDTO
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:293)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1186)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
    at com.myapp.xx.yy.Application.main(Application.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:53)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.myapp.xx.yy.models.MyApplicationJobDAO com.myapp.xx.yy.web.MyApplicationJobController.MyApplicationJobDAO; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyApplicationJobDAO': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.myapp.aa.bb.myapptest.commons.dto.MyApplicationJobDTO
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
    ... 22 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyApplicationJobDAO': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not an managed type: class com.myapp.aa.bb.myapptest.commons.dto.MyApplicationJobDTO
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1554)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1021)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:964)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:481)
    ... 24 common frames omitted
Caused by: java.lang.IllegalArgumentException: Not an managed type: class com.myapp.aa.bb.myapptest.commons.dto.MyApplicationJobDTO
    at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:219)
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:68)
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:149)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:88)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:68)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:158)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:224)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:210)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1613)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1550)

编辑:如果我手动添加所有需要在我的Spring项目中使用的外部JAR中的所有类,则可以正常工作。但是我仍然无法从外部JAR中加载这些类。

6个回答

5
我在这里给出了相同的答案:https://dev59.com/3Jvga4cB1Zd3GeqP7MhR#41145229,但是这个问题更清楚地说明了只有在将spring boot应用程序作为JAR运行时才会出现问题。
在我的项目中,我们遇到了类似的问题。当使用mvn spring-boot:start运行时,@EntityScan可以正常工作,并且可以找到包括库中的所有实体。当我们使用java -jar运行springboot应用程序时,应用程序无法找到通过maven依赖引用的实体。当以jar形式运行springboot应用程序时,类加载方式不同。
您可以指示spring-boot“解压缩”包含无法找到的实体的JAR依赖项。您可以将以下部分添加到pom.xml
<project>
  ...

  <build>
      <plugins>
          <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
              <configuration>
                  <requiresUnpack>
                      <dependency>
                          <groupId>org.example.pkg.commons</groupId>
                          <artifactId>domain-jar</artifactId>
                      </dependency>
                  </requiresUnpack>
              </configuration>
          </plugin>
      </plugins>
  </build>

</project>

4

移除@EntityScan,因为它已经过时了。如果你使用 @EntityScan,它会覆盖 @ComponentScan。

或者

你应该指定要扫描的JPA实体的基本包。@EntityScan("com.myapp.xx.yy.models")


1
我尝试删除@EntityScan并只使用@ComponentScan,但仍然遇到了相同的错误。我添加了@EntityScan("com.myapp.xx.yy.models")并删除了@ComponentScan,得到了相同的结果,但如果我添加了@EntityScan("my.external.jar.package.DTO"),它会找到正确的DTO,但抛出了相同的错误,但是针对本地项目中的一个类。我尝试添加@EntityScan({"my.external.jar.package.DTO","my.local.project.package.DTO"}),但似乎@EntityScan只会扫描其中一个而不是两个。 - rubio
如果您删除了“EntityScan”,仍然需要在xxxx.yyyy.zzz中提供basePackage给@ComponentScan。 - kuhajeyan
我添加了以下内容,但仍然出现相同的错误 @ComponentScan(basePackages={"com.myapp.xx.yy.dto","com.myapp.xx.yy.myapp.commons.dto"}) @EntityScan({"com.myapp.xx.yy.dto","com.myapp.xx.yy.myapp.commons.dto"}) - rubio

3

您的应用程序包可能与DTO类的不同,因此Spring无法找到它。您可以执行以下操作:

@ComponentScan("com.myapp.xx.yy.models")

我已经特别添加了@ComponentScan("com.myapp.xx.yy.myproject.commons.dto.MyDTO")和@ComponentScan("com.myapp.xx.yy.myproject.commons.dto"),但在运行我的jar包时仍然出现java.lang.IllegalArgumentException: Not an managed type。 - rubio
将其放入组件扫描中...更好的做法是将您的应用程序类移动到顶级包中,以便扫描每个包。默认情况下,组件扫描(和实体扫描)从应用程序类所在的包及其子包开始。它不会查找或扫描其范围之外的包。 - M. Deinum
@tiggles,你尝试在@ComponentScan扫描列表中添加所有受影响/依赖的包了吗? - Reimeus
我尝试添加了@ComponentScan("com.myapp.xx.yy.models"),也尝试了@ComponentScan("com.externaljar.dto")@ComponentScan("com.externaljar.dto.ClassName")都出现了java.lang.IllegalArgumentException: Not an managed type: class...的异常。另外,我的主类Applicaiton.java位于com.myproject.xx.yy下,而其他所有类都在com.myproject.xx.yy.something下。 - rubio
@Reimeus,我DTO的所有依赖类都在同一个包中,所以我认为如果我添加“com.externaljar.dto”,它会捕获到我所有的类。我将指定它们并尝试。 - rubio

1

我遇到了类似的问题,我通过添加这个类来解决它:

@EntityScan(basePackages = { "com.internal.model", "com.external.model" })
@Configuration
public class ScanJPA {}

请确保在JPA实体中同时包含根包,即内部和外部依赖项中定义的根包。


1

我相信答案有点晚了,不过我也遇到了同样的情况,我的实体和仓库都存放在外部jar文件中。经过多次尝试,我通过使用带有basePackage参数的@SpringBootApplication@ComponentScan来解决了这个问题。

@ComponentScan(basePackages = { "com.external.yourExternalPackage.*" })
@EnableJpaRepositories
@EntityScan(basePackages = { "com.external.model.foo.entity" })
@SpringBootApplication
@EnableConfigurationProperties
public class Application {
    public static void main(String[] args) {


    SpringApplication.run(Application.class, args);
  }
}

感谢您。

我需要在应用程序类中添加 @EntityScan (basePackages = { "com.external.lib" }) @EnableJpaRepositories (basePackages = { "com.external.lib" }) @SpringBootApplication (basePackages = { "com.external.lib" }),以使其正常工作。有没有简单的方法? - Harun

0

与dvtoever类似,我也在链接的问题中添加了我的答案,但为了帮助将来遇到此问题的人,我会在这里再次添加。


我的项目在Spring Boot应用程序中遇到了类似的问题。我将@Entity类放在一个名为com.foo.jpa的公共jar包中,然后是一个依赖于该公共jar包的应用程序jar包。应用程序jar包在一个名为com.foo.bar.appname的包中有主要的@SpringBootApplication类。无论我提供给注释的包名称是什么,@EntityScan@ComponentScan@EnableJpaRepositories都无法检测到@Entity类,并显示“Not a managed type”错误。

最终,我通过重命名包名称来解决了这个问题。我将@SpringBootApplicationapplication类放在com.foo.bar中,将@Entity类放在公共jar包中的com.foo.bar.whatever中。只要公共jar包中的@Entity类与@SpringBootApplication类在同一包或子包中,它就会被自动检测到,而不管它在哪个jar包中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接