【问题标题】:Use custom annotation on @bean factory methods在 @bean 工厂方法上使用自定义注解
【发布时间】:2016-01-17 14:15:40
【问题描述】:

目前我直接在类上使用@CustomAnnotation,并使用 BeanFactoryPostProcessor 将带注释的 bean 的 bean 定义更改为我的需要。

    @CustomAnnotation
    public class MyBean implements IMybean{
    }

    @Configuration
    public class MyConfiguration {

       @Bean
        public MyBean myBean(){
            return new myBean();
        }

    }

我想要做的是能够将@CustomAnnotation 放在配置文件的@Bean 方法上,如下所示:

    public class MyBean implements IMybean{
    }

    @Configuration
    public class MyConfiguration {

        @Bean
        @CustomAnnotation
        public MyBean myBean(){
            return new myBean();
        }

     }

从 BeanDefinition 中我可以从 beanFactory 中获取,我知道我可以获取工厂 bean 和创建 myBean 的工厂方法,并检查方法上是否有 @CustomAnnotation。

我不确定这样做是否会违反任何 Spring 原则,或者是否是常规操作。

我的初衷是工作。 但是我现在有另一个问题。 我无法使用我想要的类型自动装配来自我想要的工厂的 bean。 存在解决依赖关系的问题。 这是我用来尝试解决问题的测试代码。 test code on github

@Configuration
public class MainConfiguration implements BeanDefinitionRegistryPostProcessor, Ordered {

    private SayenBeanDefinitionRegistryPostProcessor sayenBeanDefinitionRegistryPostProcessor = new SayenBeanDefinitionRegistryPostProcessor();

    public int getOrder() {
        return sayenBeanDefinitionRegistryPostProcessor.getOrder();
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        sayenBeanDefinitionRegistryPostProcessor.postProcessBeanFactory(beanFactory);
    }

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        sayenBeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(beanFactory);
    }

    @Bean
    public AutowiredBean autowiredBean() {
        return new AutowiredBean();
    }

    @Bean
    @Transform(type = MegaSuperKarim.class)
    public Karim Karim() {
        return new Karim();
    }

    @Bean
    @Transform(type = SuperGuillaume.class)
    public Guillaume Guillaume() {
        return new Guillaume();
    }

    @Bean
    public Yoann Yoann() {
        return new Yoann();
    }

    @Bean
    public Nicolas Nicolas() {
        return new Nicolas();
    }

    @Bean
    public BeanHolder beanHolder() {
        return new BeanHolder();
    }
}

public class TransformFactoryBean implements FactoryBean<Object> {

    @Autowired
    private AutowiredBean pouet;

    private Class<?> objectType;

    boolean singleton = true;

    @Override
    public Object getObject() throws Exception {
        return objectType.getConstructor().newInstance();
    }

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

    @Override
    public boolean isSingleton() {
        return singleton;
    }

    public void setObjectType(Class<?> objectType) {
        this.objectType = objectType;
    }

    public AutowiredBean getPouet() {
        return pouet;
    }

    public void setSingleton(boolean singleton) {
        this.singleton = singleton;
    }

}

public class SayenBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, Ordered {

    private static Logger logger = LoggerFactory.getLogger(MainConfiguration.class);

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        logger.debug("rien");
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        //DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) beanFactory;

        for (String originalBeanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition originalBeanDefinition = beanFactory.getBeanDefinition(originalBeanName);
            logger.debug("original beanName=" + originalBeanName + ", " + originalBeanDefinition.toString());
            if (originalBeanDefinition.isAbstract()) {
                continue;
            }

            Transform sayenAnnotation = getMethodAnnotation(beanFactory, originalBeanDefinition);

            /*if (sayenAnnotation == null) {
                Class<?> originalBeanClass = beanFactory.getType(originalBeanName);
                sayenAnnotation = AnnotationUtils.findAnnotation(originalBeanClass, Transform.class);
                */if (sayenAnnotation == null) {
                    continue;
                }/*
            }*/

            Class<? extends Sayan> sayenClass = sayenAnnotation.type();

            RootBeanDefinition sayenFactoryBeanDefinition = new RootBeanDefinition(TransformFactoryBean.class, 3/*Autowire.BY_TYPE.value()*/, true);
            sayenFactoryBeanDefinition.getPropertyValues().add("objectType", sayenClass);
            sayenFactoryBeanDefinition.getPropertyValues().add("singleton", true);

            String factoryBeanName = originalBeanName;

            logger.debug("remove beanName=" + originalBeanName + ", " + originalBeanDefinition.toString());
            beanFactory.removeBeanDefinition(originalBeanName);

            logger.debug("register beanName=" + factoryBeanName + ", " + sayenFactoryBeanDefinition.toString());
            beanFactory.registerBeanDefinition(factoryBeanName, sayenFactoryBeanDefinition);

        }

    }

    private Transform getMethodAnnotation(BeanDefinitionRegistry beanFactory, BeanDefinition originalBeanDefinition) {
        String originalBeanFactoryBeanName = originalBeanDefinition.getFactoryBeanName();
        String originalBeanFactoryMethodName = originalBeanDefinition.getFactoryMethodName();

        if (originalBeanFactoryBeanName == null || originalBeanFactoryMethodName == null) {
            return null;
        }

        Class<?> originalBeanFactoryBeanClass = ClassUtils.getUserClass(((DefaultListableBeanFactory)beanFactory).getType(originalBeanFactoryBeanName));
        try {
            Method method = originalBeanFactoryBeanClass.getMethod(originalBeanFactoryMethodName);
            return AnnotationUtils.getAnnotation(method, Transform.class);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }

【问题讨论】:

    标签: java spring


    【解决方案1】:

    如果@CustomAnnotation 的唯一目的是影响类/b​​ean 的创建方式,那么您采用了正确的方法。在这种情况下,注释的范围应该在创建时(这是您在 @Configuration MyConfiguration 中将其移动到的位置)而不是类范围 (@Target(ElementType.TYPE))。
    您实际上是在说 @CustomAnnotation 在创建后对 MyBean 没有进一步的用途,并且您的框架将不再需要检查 MyBean 以获取此注释。这也意味着@CustomAnnotation 可能用于创建其他 bean(MyBean2 等)。

    【讨论】:

    • 感谢您的回复。
    • 我有 50 个需要这个注释的 bean。我正在考虑这种方法,因为如果 @Bean 返回接口类型,则 BeanFactoryPostProcessor 在处理时无法知道实现。除非接口持有注解。这确实是错误的来源,但这是不可避免的。
    • 如果您仍然不确定什么是最好的方法,也许可以修改您的原始帖子以显示更多细节,以及您可能如何设计它(BeanFactoryPostProcessor),我相信您会得到很好的反馈。
    • 我设法解决了我最初的问题。唯一需要注意的是绕过 cglib 代理来获取原始类注释。但现在我遇到了依赖解决问题。
    • 到目前为止,您在工厂代码方面做得很好。您是否尝试过实现 BeanDefinitionRegistryPostProcessor(在 PostBean 之前捕获工厂)?我不知道这是否会帮助现在确切的依赖问题是什么(您可以发布弹簧错误)吗?
    猜你喜欢
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多