检索 ByteBuddy 生成的 Spring Bean 时出现问题
Issue retrieving ByteBuddy-generated Spring Bean
我正在制作自己的 ORM,现在我想消除创建存储库 classes 的需要。他们目前看起来像这样:
@Repository
public class CustomerDao extends AbstractDao<Customer, Long>
{
}
在不深入我的 ORM 内部工作的情况下,AbstractDao class 使用一个 bean,它是一个包含实体 class 作为键和 dao class 的映射作为一个值。在不混合 ByteBuddy 的情况下,该 bean 的声明如下所示:
@Bean
public Map<Class<?>, AbstractDao<?, ?>> daoMap()
{
context.getBeansWithAnnotation(Repository.class).forEach((s, c) ->
{
if (c instanceof AbstractDao<?, ?>) map.put(ClassUtils.getSuperClassTypeArgument(c.getClass(), 0), (AbstractDao<?, ?>) c);
});
return map;
}
同样,这很好用,但是必须创建这些空的 classes 非常烦人。我正在尝试使用 Reflections 和 ByteBuddy 库在运行时生成这些 classes 并将它们作为存储库 bean 动态注入到 Spring 上下文中。这是我修改后的方法:
@Bean
public Map<Class<?>, AbstractDao<?, ?>> daoMap() throws InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, ConstructorMissingException, AnnotationMissingException, NoSuchMethodException, SecurityException
{
var map = new HashMap<Class<?>, AbstractDao<?, ?>>();
var byteBuddy = new ByteBuddy();
for (var entityClass : new Reflections("com.somepackage.entity").getSubTypesOf(Entity.class))
{
var daoClass = byteBuddy
.subclass(TypeDescription.Generic.Builder.parameterizedType(AbstractDao.class, entityClass,
ReflectionUtils.getIdField(entityClass).getType()).build())
.annotateType(AnnotationDescription.Builder.ofType(Repository.class).build())
.make();
var clazz = daoClass.load(getClass().getClassLoader()).getLoaded();
((GenericApplicationContext) context).registerBean(clazz, clazz.getConstructor().newInstance());
}
context.getBeansWithAnnotation(Repository.class).forEach((s, c) ->
{
if (c instanceof AbstractDao<?, ?>) map.put(ClassUtils.getSuperClassTypeArgument(c.getClass(), 0), (AbstractDao<?, ?>) c);
});
return map;
}
创建 classes 并将它们作为 bean 注入的循环似乎工作正常。它不会抛出异常并影响正确的 classes。但是,我在 getBeansWithAnnotation 行遇到异常:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mdenis.carbon.carbon_orm.dao.AbstractDao$ByteBuddy$jzMtXq5b': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:278) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:323) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:672) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1264) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at com.mdenis.carbon.carbon_orm.config.ORMConfig.daoMap(ORMConfig.java:55) ~[classes/:na]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34.CGLIB$daoMap(<generated>) ~[classes/:na]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34$$FastClassBySpringCGLIB$$a25eec90.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34.daoMap(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 85 common frames omitted
这似乎与 Spring 未能自动装配构造函数有关,即使它在技术上不必这样做。知道如何解决这个问题吗?
您的问题与byte-buddy无关。如果您尝试注册预先存在的 class.
,也会出现该错误
您需要在上下文启动的早期阶段注册新 bean。一种方法是使用 BeanFactoryPostProcessor
。将您的地图创建分成两个部分。 class代的那个变成了BeanFactoryPostProcessor
代。您可以在 @Bean
工厂方法
中保留 Map
代的那个
例如:
@Configuration
public class RepositoryConfigurationBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
// Replace X1.class with the one from bytebuddy
// The name should be unique
configurableListableBeanFactory.registerSingleton("abc", X1.class.getConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
我正在制作自己的 ORM,现在我想消除创建存储库 classes 的需要。他们目前看起来像这样:
@Repository
public class CustomerDao extends AbstractDao<Customer, Long>
{
}
在不深入我的 ORM 内部工作的情况下,AbstractDao class 使用一个 bean,它是一个包含实体 class 作为键和 dao class 的映射作为一个值。在不混合 ByteBuddy 的情况下,该 bean 的声明如下所示:
@Bean
public Map<Class<?>, AbstractDao<?, ?>> daoMap()
{
context.getBeansWithAnnotation(Repository.class).forEach((s, c) ->
{
if (c instanceof AbstractDao<?, ?>) map.put(ClassUtils.getSuperClassTypeArgument(c.getClass(), 0), (AbstractDao<?, ?>) c);
});
return map;
}
同样,这很好用,但是必须创建这些空的 classes 非常烦人。我正在尝试使用 Reflections 和 ByteBuddy 库在运行时生成这些 classes 并将它们作为存储库 bean 动态注入到 Spring 上下文中。这是我修改后的方法:
@Bean
public Map<Class<?>, AbstractDao<?, ?>> daoMap() throws InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, ConstructorMissingException, AnnotationMissingException, NoSuchMethodException, SecurityException
{
var map = new HashMap<Class<?>, AbstractDao<?, ?>>();
var byteBuddy = new ByteBuddy();
for (var entityClass : new Reflections("com.somepackage.entity").getSubTypesOf(Entity.class))
{
var daoClass = byteBuddy
.subclass(TypeDescription.Generic.Builder.parameterizedType(AbstractDao.class, entityClass,
ReflectionUtils.getIdField(entityClass).getType()).build())
.annotateType(AnnotationDescription.Builder.ofType(Repository.class).build())
.make();
var clazz = daoClass.load(getClass().getClassLoader()).getLoaded();
((GenericApplicationContext) context).registerBean(clazz, clazz.getConstructor().newInstance());
}
context.getBeansWithAnnotation(Repository.class).forEach((s, c) ->
{
if (c instanceof AbstractDao<?, ?>) map.put(ClassUtils.getSuperClassTypeArgument(c.getClass(), 0), (AbstractDao<?, ?>) c);
});
return map;
}
创建 classes 并将它们作为 bean 注入的循环似乎工作正常。它不会抛出异常并影响正确的 classes。但是,我在 getBeansWithAnnotation 行遇到异常:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mdenis.carbon.carbon_orm.dao.AbstractDao$ByteBuddy$jzMtXq5b': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:278) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:323) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:672) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1264) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at com.mdenis.carbon.carbon_orm.config.ORMConfig.daoMap(ORMConfig.java:55) ~[classes/:na]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34.CGLIB$daoMap(<generated>) ~[classes/:na]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34$$FastClassBySpringCGLIB$$a25eec90.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$fd9cb34.daoMap(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 85 common frames omitted
这似乎与 Spring 未能自动装配构造函数有关,即使它在技术上不必这样做。知道如何解决这个问题吗?
您的问题与byte-buddy无关。如果您尝试注册预先存在的 class.
,也会出现该错误您需要在上下文启动的早期阶段注册新 bean。一种方法是使用 BeanFactoryPostProcessor
。将您的地图创建分成两个部分。 class代的那个变成了BeanFactoryPostProcessor
代。您可以在 @Bean
工厂方法
Map
代的那个
例如:
@Configuration
public class RepositoryConfigurationBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
try {
// Replace X1.class with the one from bytebuddy
// The name should be unique
configurableListableBeanFactory.registerSingleton("abc", X1.class.getConstructor().newInstance());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}