ASPEN3
ASPEN3

Reputation: 13

SpringBoot Auto-wiring Issue: Proxy Beans via ImportBeanDefinitionRegistrar and FactoryBean via custom annotation

Created a custom annotation to register proxy beans via ImportBeanDefinitionRegistrar and FactoryBean. At runtime, application seems to start and auto wire the beans as expected. However, IDE (IntelliJ) shows error on auto wired bean.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(CustomBeansRegistrar.class)
public @interface EnableCustomBeans {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CustomBean {
}
public class CustomBeanFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {

    private Class<?> type;
    private String name;
    private ApplicationContext applicationContext;
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getObject() {
        return getTarget();
    }

    <T> T getTarget() {
        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
                new CustomBeanInvocationHandler());
    }

    @Override
    public Class<?> getObjectType() {
        return type;
    }

    @Override
    public void afterPropertiesSet() {
        Assert.hasText(name, "Name must be set");
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
        beanFactory = context;
    }


}
class CustomBeansRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private ResourceLoader resourceLoader;
    private Environment environment;

    CustomBeansRegistrar() {
    }

    @Override
    public void setEnvironment(@NotNull Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(@NotNull ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(@NotNull AnnotationMetadata metadata, @NotNull BeanDefinitionRegistry registry) {
        registerCustomBeans(metadata, registry);
    }

    private void registerCustomBeans(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ... // logic to scan CustomBean annotation classes
        registerCustomBean(registry, annotationMetadata, attributes)
    }

    private void registerCustomBean(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {

        ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
                ? (ConfigurableBeanFactory) registry : null;
        assert beanFactory != null;

        String className = annotationMetadata.getClassName();
        Class<?> clazz = ClassUtils.resolveClassName(className, null);

        String name = (String) attributes.get("name");

        CustomBeanFactoryBean factoryBean = new CustomBeanFactoryBean();
        factoryBean.setBeanFactory(beanFactory);
        factoryBean.setType(clazz);
        factoryBean.setName(name);

        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> factoryBean.getTarget());
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        definition.setLazyInit(true);

        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
        beanDefinition.setAttribute("customBeansRegistrarFactoryBean", factoryBean);
        beanDefinition.setPrimary(true);

        String[] qualifiers = new String[]{name + "CustomBean"};

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

}
@SpringBootApplication
@EnableCustomBeans
public class MyApplication {
    ...
}
@CustomBean(name = "myServiceCustomBean")
public interface MyServiceCustomBean {
@Component
@RequiredArgsConstructor
public class SomeService {

    private final MyServiceCustomBean myServiceCustomBean;

}

Above code works as expected, however, IDE shows error Could not autowire. No beans of 'MyServiceCustomBean' type found. .

Also, have Spring attached to the project module. Tried invalidating cache and restarting IDE as well.

I have even tried using @Autowired along with @Qualifier ("myServiceCustomBean").

Using @Resource does hide the error.

What I am missing here in order to have these proxy beans recognized by IDE as Spring beans?

Upvotes: 0

Views: 28

Answers (0)

Related Questions