Byte Buddy Generated class 对 Orika (Javaassist) 不可见
Byte Buddy Generated class not visible to Orika (Javaassist)
我用Byte Buddy to generate some DTO classes in a Spring Boot application. I also use the Orika mapper librarie to map Entity to/from DTO classes. This library uses another runtime code generation tool to generate mapper classes, which is Javassist.
我的问题是应该映射由 Byte Buddy 生成的 to/from classes 的 Orika 映射器找不到它们。 line in the Orika JavasssistCompilerStrategy class.
抛出异常
我不确定这里发生了什么,因为 Class<?> type
参数不为空。以下是我的 DTO class 的生成方式:
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();
我尝试(但不是不认真地)为 Orika 创建一个 ByteBuddyCompilerStrategy,这将是一个不错且干净的解决方案,但我无法从它们的字符串表示形式创建字段和方法,就像在 Javaassist. Orika holds a generated class definition in the SourceCodeContext class,其中仅包含字段和方法的源代码(字符串表示形式)。
编辑
这是(非动态)基础 classes 和接口的快速表示。这将是一个很长的 post,但也许它可以帮助将来的其他人:
@MappedSuperclass
public class BaseEntity {
@Id private Long id;
@Version private Long version;
// Getters and setters omitted for brevity
}
@Entity
public class SomeEntityA extends BaseEntity {
// Fields, getters and setters omitted for brevity
}
@Entity
public class SomeEntityB extends BaseEntity {
// Fields, getters and setters omitted for brevity
}
public class BaseDto {
@JsonView(Views.Summary.class)
private Long id;
@JsonView(Views.Summary.class)
private Long version;
// Getters and setters omitted for brevity
}
@NoRepositoryBean
public interface BaseRepository<E extends BaseEntity> extends JpaRepository<E, Long> {
// Methods omitted for brevity
}
@Repository
public interface SomeRepositoryA extends BaseRepository<SomeEntityA> {}
@Repository
public interface SomeRepositoryB extends BaseRepository<SomeEntityB> {}
public interface BaseService<E extends BaseEntity, D extends BaseDto> {
// Methods omitted for brevity
}
public class BaseMapper<E extends BaseEntity, D extends BaseDto> {
private SomeRepositoryA someRepositoryA;
private SomeRepositoryB someRepositoryB;
protected BaseMapper(SomeRepositoryA someRepositoryA, SomeRepositoryB someRepositoryB) {
super();
this.someRepositoryA = someRepositoryA;
this.someRepositoryB = someRepositoryB;
}
// Implementation omitted for brevity
}
public class BaseServiceImpl<E extends BaseEntity, D extends BaseDto>
implements BaseService<E, D> {
private BaseRepository<E> repository;
private SomeRepositoryA someRepositoryA;
protected BaseServiceImpl(BaseRepository<E> repository, SomeRepositoryA someRepositoryA) {
super();
this.repository = repository;
this.someRepositoryA = someRepositoryA;
}
// Implementation omitted for brevity
}
public class BaseController<E extends BaseEntity, D extends BaseDto> {
private BaseService<E, D> service;
protected BaseController(final BaseService<E, D> service) {
super();
this.service = service;
}
// Implementation omitted for brevity
}
现在,我希望能够从实体 class 生成 DTO、存储库、映射器、服务声明、服务实现和 RestController。例如,来自:
@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:
//
// 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);
}
}
现在,当我使用load(classLoader)
时,我运行变成了Orika/Javassist问题。当我使用 load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
方式时,在创建服务实现 class:
时 make()
调用抛出异常
java.lang.TypeNotPresentException: Type com.example.FooDtoA not present
可能与有关?现在我的解决方案是像创建实体 class 一样创建 DTO class,并使用 load(classLoader)
。但我想以与所有其他 class 相同的方式生成 DTO class。
Javassist 可能依赖于定位 class 文件来完成它的工作。对于 Byte Buddy,这不一定是可能的,因为 class 文件被注入,因此 class 加载程序无法使用 .getResource
[=16] 从 jar 文件中找到 class 文件=].
你试过.load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
了吗?此策略保留 class 文件,以便 Javassist 可以找到它。
我用Byte Buddy to generate some DTO classes in a Spring Boot application. I also use the Orika mapper librarie to map Entity to/from DTO classes. This library uses another runtime code generation tool to generate mapper classes, which is Javassist.
我的问题是应该映射由 Byte Buddy 生成的 to/from classes 的 Orika 映射器找不到它们。 line in the Orika JavasssistCompilerStrategy class.
抛出异常我不确定这里发生了什么,因为 Class<?> type
参数不为空。以下是我的 DTO class 的生成方式:
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();
我尝试(但不是不认真地)为 Orika 创建一个 ByteBuddyCompilerStrategy,这将是一个不错且干净的解决方案,但我无法从它们的字符串表示形式创建字段和方法,就像在 Javaassist. Orika holds a generated class definition in the SourceCodeContext class,其中仅包含字段和方法的源代码(字符串表示形式)。
编辑
这是(非动态)基础 classes 和接口的快速表示。这将是一个很长的 post,但也许它可以帮助将来的其他人:
@MappedSuperclass
public class BaseEntity {
@Id private Long id;
@Version private Long version;
// Getters and setters omitted for brevity
}
@Entity
public class SomeEntityA extends BaseEntity {
// Fields, getters and setters omitted for brevity
}
@Entity
public class SomeEntityB extends BaseEntity {
// Fields, getters and setters omitted for brevity
}
public class BaseDto {
@JsonView(Views.Summary.class)
private Long id;
@JsonView(Views.Summary.class)
private Long version;
// Getters and setters omitted for brevity
}
@NoRepositoryBean
public interface BaseRepository<E extends BaseEntity> extends JpaRepository<E, Long> {
// Methods omitted for brevity
}
@Repository
public interface SomeRepositoryA extends BaseRepository<SomeEntityA> {}
@Repository
public interface SomeRepositoryB extends BaseRepository<SomeEntityB> {}
public interface BaseService<E extends BaseEntity, D extends BaseDto> {
// Methods omitted for brevity
}
public class BaseMapper<E extends BaseEntity, D extends BaseDto> {
private SomeRepositoryA someRepositoryA;
private SomeRepositoryB someRepositoryB;
protected BaseMapper(SomeRepositoryA someRepositoryA, SomeRepositoryB someRepositoryB) {
super();
this.someRepositoryA = someRepositoryA;
this.someRepositoryB = someRepositoryB;
}
// Implementation omitted for brevity
}
public class BaseServiceImpl<E extends BaseEntity, D extends BaseDto>
implements BaseService<E, D> {
private BaseRepository<E> repository;
private SomeRepositoryA someRepositoryA;
protected BaseServiceImpl(BaseRepository<E> repository, SomeRepositoryA someRepositoryA) {
super();
this.repository = repository;
this.someRepositoryA = someRepositoryA;
}
// Implementation omitted for brevity
}
public class BaseController<E extends BaseEntity, D extends BaseDto> {
private BaseService<E, D> service;
protected BaseController(final BaseService<E, D> service) {
super();
this.service = service;
}
// Implementation omitted for brevity
}
现在,我希望能够从实体 class 生成 DTO、存储库、映射器、服务声明、服务实现和 RestController。例如,来自:
@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:
//
// 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);
}
}
现在,当我使用load(classLoader)
时,我运行变成了Orika/Javassist问题。当我使用 load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
方式时,在创建服务实现 class:
make()
调用抛出异常
java.lang.TypeNotPresentException: Type com.example.FooDtoA not present
可能与load(classLoader)
。但我想以与所有其他 class 相同的方式生成 DTO class。
Javassist 可能依赖于定位 class 文件来完成它的工作。对于 Byte Buddy,这不一定是可能的,因为 class 文件被注入,因此 class 加载程序无法使用 .getResource
[=16] 从 jar 文件中找到 class 文件=].
你试过.load(classLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
了吗?此策略保留 class 文件,以便 Javassist 可以找到它。