Проблема с получением Spring Bean, созданного ByteBuddy - PullRequest
1 голос
/ 25 мая 2020

Я создаю свой собственный ORM и нахожусь в точке, где я хочу избавиться от необходимости создавать классы репозитория. В настоящее время они выглядят так:

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

}

Не углубляясь слишком глубоко во внутреннюю работу моей ORM, класс AbstractDao использует bean-компонент, который представляет собой карту, содержащую класс сущности в качестве ключа и класс dao в качестве стоимость. Без добавления 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;
}

Опять же, это отлично работает, но создавать эти пустые классы очень раздражает. Я пытаюсь использовать библиотеки Reflections и ByteBuddy для генерации этих классов во время выполнения и динамического внедрения их в контекст 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;
}

l oop, который создает классы и внедряет их, поскольку beans, похоже, работает нормально. Он не создает исключений и влияет на правильные классы. Однако я получаю исключение в строке 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

Похоже, это связано с тем, что Spring не смогла автоматически подключить конструктор, хотя технически это не обязательно. Есть идеи, как решить эту проблему?

1 Ответ

1 голос
/ 31 мая 2020

Ваша проблема не связана с byte-buddy. Ошибка также возникает, если вы пытаетесь зарегистрировать уже существующий класс.

Вам необходимо зарегистрировать новые bean-компоненты на более ранней стадии запуска контекста. Один из способов сделать это - использовать BeanFactoryPostProcessor. Разделите создание карты на две части. Тот, у которого есть класс в этом BeanFactoryPostProcessor. Тот, у которого есть поколение Map, вы можете сохранить в заводском методе @Bean

например:

@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();
        }
   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...