简体   繁体   English

检索 ByteBuddy 生成的 Spring Bean 的问题

[英]Issue retrieving ByteBuddy-generated Spring Bean

I am making my own ORM and I'm at a point where I want to to eliminate the need to create repository classes.我正在制作自己的 ORM 并且我正处于想要消除创建存储库类的需要的地步。 They currently look like this:它们目前看起来像这样:

@Repository
public class CustomerDao extends AbstractDao<Customer, Long>
{

}

Without going too deep into the inner workings of my ORM, the AbstractDao class uses a bean which is a map containing the entity class as a key and the dao class as a value. Without going too deep into the inner workings of my ORM, the AbstractDao class uses a bean which is a map containing the entity class as a key and the dao class as a value. Without bringing ByteBuddy in the mix, the declaration of that bean looks like this:如果不将 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;
}

Again this works great but it's very annoying to have to create these empty classes.同样,这很好用,但是必须创建这些空类非常烦人。 I am trying to use the Reflections and ByteBuddy libraries to generate these classes at runtime and dynamically inject them into the Spring context as repository beans.我正在尝试使用 Reflections 和 ByteBuddy 库在运行时生成这些类,并将它们作为存储库 bean 动态注入到 Spring 上下文中。 Here is my modified method:这是我修改后的方法:

@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;
}

The loop that creates the classes and injects them as beans seems to work fine.创建类并将它们作为 bean 注入的循环似乎工作正常。 It doesn't throw exceptions and affects the right classes.它不会抛出异常并影响正确的类。 I am however getting an exception at the getBeansWithAnnotation line:但是,我在 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$0(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$$8fd9cb34.CGLIB$daoMap$1(<generated>) ~[classes/:na]
at com.mdenis.carbon.carbon_orm.config.ORMConfig$$EnhancerBySpringCGLIB$$8fd9cb34$$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$$8fd9cb34.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

This seems to be related to Spring failing to autowire the constructor even though it technically doesn't have to.这似乎与 Spring 未能自动装配构造函数有关,即使它在技术上不必这样做。 Any idea how to resolve this?知道如何解决这个问题吗?

Your problem is not related to byte-buddy.您的问题与字节伙伴无关。 The error also occurs if you try to register a preexisting class.如果您尝试注册预先存在的 class,也会出现此错误。

You need to register the new beans in an earlier phase of the context startup.您需要在上下文启动的早期阶段注册新 bean。 One way of doing that is to use a BeanFactoryPostProcessor .一种方法是使用BeanFactoryPostProcessor Split your map creation into two sections.将您的 map 创建分成两个部分。 The one with the class generation into the BeanFactoryPostProcessor .将 class 生成到BeanFactoryPostProcessor的那个。 The one with the Map generation you can keep in the @Bean factory method具有Map一代的那个可以保留在@Bean工厂方法中

eg:例如:

@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();
        }
   }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM