字节伙伴生成的类对Orika不可见(Javaassist)

字节伙伴生成的类对Orika不可见(Javaassist),java,byte-buddy,orika,Java,Byte Buddy,Orika,我使用它在Spring引导应用程序中生成一些DTO类。我还使用映射器库将实体映射到DTO类或从DTO类映射到DTO类。该库使用另一个运行时代码生成工具生成映射器类,即 我的问题是,应该映射到Byte Buddy生成的类或从中映射的Orika映射器无法找到它们。此时将引发异常 我不确定这里发生了什么,因为类类型参数不是null。下面是如何生成我的DTO类: final ClassLoader classLoader = getClass().getClassLoader(); // This o

我使用它在Spring引导应用程序中生成一些DTO类。我还使用映射器库将实体映射到DTO类或从DTO类映射到DTO类。该库使用另一个运行时代码生成工具生成映射器类,即

我的问题是,应该映射到Byte Buddy生成的类或从中映射的Orika映射器无法找到它们。此时将引发异常

我不确定这里发生了什么,因为
类类型
参数不是null。下面是如何生成我的DTO类:

final ClassLoader classLoader = getClass().getClassLoader();

// This object holds the information required to generate classes
// for a use case (it also contains information about a generated
// @Repository, @Service and @RestController)
final DynamicBeansDefinition def = ...
// Create the DTO class from the entity class
final Class<?> entityClass = ClassUtils.forName(def.getEntityClassName(), classLoader);

final Builder<BaseDto> dtoBuilder =
      new ByteBuddy().subclass(BaseDto.class).name(def.getDtoClassName());

// Copy all the entity class properties (excluding inherited fields),
// adding @JsonView(Views.Public.class) on each field.
final Field[] fields = entityClass.getDeclaredFields();
for (final Field field : fields) {
  dtoBuilder
      .defineProperty(field.getName(), field.getType())
      .annotateField(
          AnnotationDescription.Builder.ofType(JsonView.class)
              .defineTypeArray("value", Views.Public.class)
              .build());
}
final Class<?> dtoClass = dtoBuilder.make().load(classLoader).getLoaded();
我想生成以下内容:

  public class FooDtoA extends BaseDto {

    @JsonView(Views.Public.class)
    private String columnA;

    // Getters and setters omitted for brevity
  }

  @Repository(value = "fooRepositoryA")
  public interface FooRepositoryA extends BaseRepository<FooEntityA> {
    // WILL ALWAYS BE EMPTY, everything is defined in the base interface
  }

  @Component(value = "fooMapperA")
  public class FooMapperA extends BaseMapper<FooEntityA, FooDtoA> {

    public FooMapperA(SomeRepositoryA someRepositoryA, SomeRepositoryB someRepositoryB) {
      super(someRepositoryA, someRepositoryB);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }

  public interface FooServiceA extends BaseService<FooEntityA, FooDtoA> {
    // WILL ALWAYS BE EMPTY, everything is defined in the base interface
  }

  @Service(value = "fooServiceAImpl")
  public class ServiceAImpl extends BaseServiceImpl<FooEntityA, FooDtoA> implements FooServiceA {

    public ServiceAImpl(
        @Autowired @Qualifier(value = "fooRepositoryA") BaseRepository<FooEntityA> repository,
        SomeRepositoryA someRepositoryA) {
      super(repository, someRepositoryA);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }

  @RestController(value = "fooControllerA")
  @RequestMapping(path = "fooPathA")
  public class FooControllerA extends BaseController<FooEntityA, FooDtoA> {

    public FooControllerA(
        @Autowired @Qualifier(value = "fooServiceAImpl") BaseService<FooEntityA, FooDtoA> service) {
      super(service);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }

可能是因为什么?目前,我的解决方案是以与实体类相同的方式创建DTO类,并使用
load(classLoader)
。但是我希望以与所有其他类相同的方式生成DTO类。

Javassist可能依赖于定位类文件来完成其工作。对于Byte Buddy,这不一定是可能的,因为类文件被注入,因此类加载器无法使用
.getResource
API从jar文件中找到类文件


您是否尝试过
.load(classLoader,ClassLoadingStrategy.Default.WRAPPER\u PERSISTENT)
?此策略保留类文件,以便Javassist能够找到它。

谢谢!遗憾的是,它没有起作用。我遇到了另一个问题。我只是编辑了我的原始问题,以便详细解释这个新问题是什么,以及我到底想做什么。
  @Entity
  public class FooEntityA extends BaseEntity {

    @Column private String columnA;

    // Getters and setters omitted for brevity
  }
  public class FooDtoA extends BaseDto {

    @JsonView(Views.Public.class)
    private String columnA;

    // Getters and setters omitted for brevity
  }

  @Repository(value = "fooRepositoryA")
  public interface FooRepositoryA extends BaseRepository<FooEntityA> {
    // WILL ALWAYS BE EMPTY, everything is defined in the base interface
  }

  @Component(value = "fooMapperA")
  public class FooMapperA extends BaseMapper<FooEntityA, FooDtoA> {

    public FooMapperA(SomeRepositoryA someRepositoryA, SomeRepositoryB someRepositoryB) {
      super(someRepositoryA, someRepositoryB);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }

  public interface FooServiceA extends BaseService<FooEntityA, FooDtoA> {
    // WILL ALWAYS BE EMPTY, everything is defined in the base interface
  }

  @Service(value = "fooServiceAImpl")
  public class ServiceAImpl extends BaseServiceImpl<FooEntityA, FooDtoA> implements FooServiceA {

    public ServiceAImpl(
        @Autowired @Qualifier(value = "fooRepositoryA") BaseRepository<FooEntityA> repository,
        SomeRepositoryA someRepositoryA) {
      super(repository, someRepositoryA);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }

  @RestController(value = "fooControllerA")
  @RequestMapping(path = "fooPathA")
  public class FooControllerA extends BaseController<FooEntityA, FooDtoA> {

    public FooControllerA(
        @Autowired @Qualifier(value = "fooServiceAImpl") BaseService<FooEntityA, FooDtoA> service) {
      super(service);
    }

    // WILL ALWAYS BE EMPTY, everything is defined in the base class
  }
  public void createBeans(
      final ConfigurableListableBeanFactory beanFactory, final BeansDefinition def) {

    final ClassLoader classLoader = getClass().getClassLoader();

    try {
      // Create the DTO class from the entity class
      final Class<?> entityClass = ClassUtils.forName(def.getEntityClassName(), classLoader);
      // final Class<?> dtoClass = ClassUtils.forName(def.getDtoClassName(), classLoader);

      final Builder<BaseDto> dtoBuilder =
          new ByteBuddy().subclass(BaseDto.class).name(def.getDtoClassName());

      // Copy all the entity class properties, adding
      // @JsonView(Views.Public.class) on each field.
      final Field[] fields = entityClass.getDeclaredFields();
      for (final Field field : fields) {
        dtoBuilder
            .defineProperty(field.getName(), field.getType())
            .annotateField(
                AnnotationDescription.Builder.ofType(JsonView.class)
                    .defineTypeArray("value", Views.Public.class)
                    .build());
      }
      final Class<?> dtoClass =
          dtoBuilder
              .make()
              // .load(classLoader)
              .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
              .getLoaded();

      // Create the repository
      new ByteBuddy()
          .makeInterface(
              TypeDescription.Generic.Builder.parameterizedType(BaseRepository.class, entityClass)
                  .build())
          .name(def.getRepositoryClassName())
          .annotateType(
              AnnotationDescription.Builder.ofType(Repository.class)
                  .define("value", def.getRepositoryBeanName())
                  .build())
          .make()
          // .load(classLoader)
          .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
          .getLoaded();

      // This is an ugly hack in order to create the same BeanDefinition for
      // our created Repository as if it was auto configured by spring
      // boot. There is no other way (AFAIK) to do this, since Spring
      // won't scan the dynamically created classes. See:
      // https://stackoverflow.com/questions/37402782
      // So the hack is to create a RootBeanDefinition from a known
      // existing repository RootBeanDefinition, and then to change
      // the argument that will be used to create the
      // JpaRepositoryFactoryBean.
      final RootBeanDefinition repositoryABeanDefinition =
          (RootBeanDefinition) beanFactory.getBeanDefinition("someRepositoryA");
      final RootBeanDefinition repositoryBeanDefinition =
          new RootBeanDefinition(repositoryABeanDefinition);
      repositoryBeanDefinition.getConstructorArgumentValues().clear();
      repositoryBeanDefinition
          .getConstructorArgumentValues()
          .addIndexedArgumentValue(0, def.getRepositoryClassName());
      ((DefaultListableBeanFactory) beanFactory)
          .registerBeanDefinition(def.getRepositoryBeanName(), repositoryBeanDefinition);

      // Create the service mapper
      final Class<?> mapperClass =
          new ByteBuddy()
              .subclass(
                  TypeDescription.Generic.Builder.parameterizedType(
                          BaseMapper.class, entityClass, dtoClass)
                      .build(),
                  ConstructorStrategy.Default.NO_CONSTRUCTORS)
              .name(def.getMapperClassName())
              .annotateType(
                  AnnotationDescription.Builder.ofType(Component.class)
                      .define("value", def.getMapperBeanName())
                      .build())
              .defineConstructor(Modifier.PUBLIC)
              .withParameters(SomeRepositoryA.class, SomeRepositoryB.class)
              .intercept(
                  MethodCall.invoke(
                          BaseMapper.class.getDeclaredConstructor(
                                  SomeRepositoryA.class, SomeRepositoryB.class))
                      .withArgument(0, 1))
              .make()
              // .load(classLoader)
              .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
              .getLoaded();

      final BeanDefinition mapperBeanDefinition = new RootBeanDefinition(mapperClass);
      ((DefaultListableBeanFactory) beanFactory)
          .registerBeanDefinition(def.getMapperBeanName(), mapperBeanDefinition);

      // Create the service interface
      final Class<?> serviceInterfaceClass =
          new ByteBuddy()
              .makeInterface(
                  TypeDescription.Generic.Builder.parameterizedType(
                          BaseService.class, entityClass, dtoClass)
                      .build())
              .name(def.getServiceInterfaceClassName())
              .make()
              //.load(classLoader)
               .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
              .getLoaded();

      // Create the service implementation
      final Class<?> serviceImplClass =
          new ByteBuddy()
              .subclass(
                  TypeDescription.Generic.Builder.parameterizedType(
                          BaseServiceImpl.class, entityClass, dtoClass)
                      .build(),
                  ConstructorStrategy.Default.NO_CONSTRUCTORS)
              .name(def.getServiceImplementationClassName())
              .implement(serviceInterfaceClass)
              .annotateType(
                  AnnotationDescription.Builder.ofType(Service.class)
                      .define("value", def.getServiceBeanName())
                      .build())
              .defineConstructor(Modifier.PUBLIC)
              .withParameter(BaseRepository.class)
              .annotateParameter(
                  AnnotationDescription.Builder.ofType(Autowired.class).build(),
                  AnnotationDescription.Builder.ofType(Qualifier.class)
                      .define("value", def.getRepositoryBeanName())
                      .build())
              .withParameter(SomeRepositoryA.class)
              .intercept(
                  MethodCall.invoke(
                          BaseServiceImpl.class.getDeclaredConstructor(
                                  BaseRepository.class, SomeRepositoryA.class))
                      .withArgument(0, 1))
              .make()
              //.load(classLoader)
              .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
              .getLoaded();

      final BeanDefinition serviceBeanDefinition = new RootBeanDefinition(serviceImplClass);
      ((DefaultListableBeanFactory) beanFactory)
          .registerBeanDefinition(def.getServiceBeanName(), serviceBeanDefinition);

      // Create the rest controller
      final Class<?> controllerClass =
          new ByteBuddy()
              .subclass(
                  TypeDescription.Generic.Builder.parameterizedType(
                          BaseController.class, entityClass, dtoClass)
                      .build(),
                  ConstructorStrategy.Default.NO_CONSTRUCTORS)
              .name(def.getControllerClassName())
              .annotateType(
                  AnnotationDescription.Builder.ofType(RestController.class)
                      .define("value", def.getControllerBeanName())
                      .build(),
                  AnnotationDescription.Builder.ofType(RequestMapping.class)
                      .defineArray("value", def.getControllerPath())
                      .build())
              .defineConstructor(Modifier.PUBLIC)
              .withParameter(BaseService.class)
              .annotateParameter(
                  AnnotationDescription.Builder.ofType(Autowired.class).build(),
                  AnnotationDescription.Builder.ofType(Qualifier.class)
                      .define("value", def.getServiceBeanName())
                      .build())
              .intercept(
                  MethodCall.invoke(
                          BaseController.class.getDeclaredConstructor(BaseService.class))
                      .withArgument(0))
              .make()
              //.load(classLoader)
              .load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
              .getLoaded();

      final BeanDefinition controllerBeanDefinition = new RootBeanDefinition(controllerClass);
      ((DefaultListableBeanFactory) beanFactory)
          .registerBeanDefinition(def.getControllerBeanName(), controllerBeanDefinition);

    } catch (Exception ex) {
      throw new FatalBeanException("Unable to create beans for entity " + def.getEntityName(), ex);
    }
  }
java.lang.TypeNotPresentException: Type com.example.FooDtoA not present