Reputation: 59
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);
}
}
Upvotes: 3
Views: 1139
Reputation: 5829
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).
Upvotes: 1