字节伙伴生成的类对Orika不可见(Javaassist)
我使用它在Spring引导应用程序中生成一些DTO类。我还使用映射器库将实体映射到DTO类或从DTO类映射到DTO类。该库使用另一个运行时代码生成工具生成映射器类,即 我的问题是,应该映射到Byte Buddy生成的类或从中映射的Orika映射器无法找到它们。此时将引发异常 我不确定这里发生了什么,因为字节伙伴生成的类对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
类类型
参数不是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