3

Curently I use @CustomAnnotation on the classes directly, and use a BeanFactoryPostProcessor to change the bean definition of the annotated beans to my needs.

 @CustomAnnotation public class MyBean implements IMybean{ } @Configuration public class MyConfiguration { @Bean public MyBean myBean(){ return new myBean(); } } 

What I want to do is instead be able to put the @CustomAnnotation on @Bean method of the configuration file, like this:

 public class MyBean implements IMybean{ } @Configuration public class MyConfiguration { @Bean @CustomAnnotation public MyBean myBean(){ return new myBean(); } } 

From the BeanDefinition I can get from the beanFactory, I know I can get the factory bean and the factory method that creates myBean, and check if there is a @CustomAnnotation on the method.

What I am not sure is if doing that would break any spring principles, or if it is a regular thing to do.

My original intention is working. However I have another issue now. I can't autowire the bean that comes from the factory where I want, with the type I want. There are problems to resolve the depencies. This is the test code I use to try to solve the issue. 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); } } 

1 Answer 1

1

If the only purpose of @CustomAnnotation is to effect how the class/bean is created, then you have taken the correct approach. In this case, the scope of the annotation should be at creation (which is where you have moved it to in the @Configuration MyConfiguration) and not the class scope (@Target(ElementType.TYPE)).
You are effectively saying that @CustomAnnotation has no further use for MyBean after creation, and your framework will never again need to examine MyBean for this annotation. This also implies that @CustomAnnotation might be used for creation of other beans (MyBean2, etc).

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the reply.
I have like 50 beans that will need this annotation. I am considering this approch, because if the @Bean returns an interface type, the BeanFactoryPostProcessor can't know the implementation when it's used processed. Unless the interface holds the annotation. This is really a source of error, but it is unavoidable.
If you are still unsure what is the best approach, perhaps modify your original post to show more details, how you might design it (the BeanFactoryPostProcessor), and I'm sure you will get good feedback.
I managed to solve my initial question. The only caveat was to bypass the cglib proxy to get the original class annotations. But now I have depency resolution problems.
You have done well so far with the factory code. Have you tried implementing BeanDefinitionRegistryPostProcessor instead (catch the factory earlier than PostBean)? I don't know if that will help What is the exact dependency issue now (could you post the spring error)?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.