package com.stanley.borisovSpringDeep.circularDependency;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Main {
}
a couple of classes with business logic
@Component
public class Husband {
@Getter
private int money;
@Autowired
private Wife wife;
@Main
public void doStuff() {
init();
System.out.println(String.format("got love from wife %d", wife.getLove()));
}
@PostConstruct
public void init() {
money = 1000;
}
}
@Component
public class Wife {
@Getter
private int love;
@Autowired
private Husband husband;
@Main
public void doStuff() {
init();
System.out.println(String.format("got money from husband %d", husband.getMoney()));
}
@PostConstruct
public void init() {
love = 1000;
}
}
ContextRefreshedEvent listener
@Component
public class MainRunnerListener {
@Autowired
private ConfigurableListableBeanFactory configurableListableBeanFactory;
@SneakyThrows
@EventListener
public void handleListen(ContextRefreshedEvent contextRefreshedEvent) {
var context = contextRefreshedEvent.getApplicationContext();
var beanDefinitionIds = context.getBeanDefinitionNames();
for (String beanDefinitionId : beanDefinitionIds) {
var beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanDefinitionId);
var beanClassName = beanDefinition.getBeanClassName();
if (beanClassName == null) {
continue;
}
Class<?> beanClass = ClassUtils.resolveClassName(beanClassName, ClassLoader.getSystemClassLoader());
Method[] methods = beanClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Main.class)) {
var bean = context.getBean(beanDefinitionId);
method.invoke(bean);
}
}
}
}
}
not required class
@Component
public class BeanDefinitionAppenderBeanPostProcessor implements BeanPostProcessor {
@Autowired
ConfigurableListableBeanFactory configurableListableBeanFactory;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Arrays.stream(configurableListableBeanFactory.getBeanDefinitionNames()).parallel().forEach(name -> {
if (neededMainBehaviour(bean)) {
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanName);
if (beanDefinition.getBeanClassName() == null) {
beanDefinition.setBeanClassName(bean.getClass().getCanonicalName());
}
}
});
return bean;
}
private boolean neededMainBehaviour(Object bean) {
return Arrays.stream(bean.getClass().getMethods()).anyMatch(method -> method.isAnnotationPresent(Main.class));
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}