First pitfall

When you call the ListableBeanFactory.getBeansOfType() method, you cannot be sure which beans will be returned. Let's look at the code of the getBeansOfType() method in the DefaultListableBeanFactory.java class:

@Override
@SuppressWarnings("unchecked")
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {

......

if (exBeanName != null && isCurrentlyInCreation(exBeanName)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring match to currently created bean
'" +
exBeanName + "': " +
ex.getMessage());
}
onSuppressedException(ex);
// Ignore: indicates a circular reference when auto wiring
constructors.
// We want to find matches other than the currently created
bean itself.
continue;
}

......

}

In the preceding code, you can see that the getBeansOfType() method silently skips beans under creation, and only returns those already existing. So, when you have circular dependency between beans, use of the getBeansOfType() method during container startup is not recommended. This is because, as per the preceding code, if you are not using DEBUG or TRACE logging-level, then there will be zero information in your log that Spring skipped a particular bean which is under creation.

Let's see the preceding pitfall with the following example. As per the following diagram, we have three beans, Account, Customer, and Bank, and a circular dependency between them:

As per the preceding diagram, the following is the Account, Customer, and Bank class:

@Component
public class Account {

private static final Logger LOGGER = Logger.getLogger(Account.class);

static {
LOGGER.info("Account | Class loaded");
}

@Autowired
public Account(ListableBeanFactory beanFactory) {
LOGGER.info("Account | Constructor");
LOGGER.info("Constructor (Customer?): {}" +
beanFactory.getBeansOfType(Customer.class).keySet());
LOGGER.info("Constructor (Bank?): {}" +
beanFactory.getBeansOfType(Bank.class).keySet());
}

}

@Component
public class Customer {

private static final Logger LOGGER = Logger.getLogger(Customer.class);

static {
LOGGER.info("Customer | Class loaded");
}

@Autowired
public Customer(ListableBeanFactory beanFactory) {
LOGGER.info("Customer | Constructor");
LOGGER.info("Account (Account?): {}" +
beanFactory.getBeansOfType(Account.class).keySet());
LOGGER.info("Constructor (Bank?): {}" +
beanFactory.getBeansOfType(Bank.class).keySet());
}

}

@Component
public class Bank {

private static final Logger LOGGER = Logger.getLogger(Bank.class);

static {
LOGGER.info("Bank | Class loaded");
}

public Bank() {
LOGGER.info("Bank | Constructor");
}

}

Following is the Main class:

public class MainApp {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
Account account = context.getBean(Account.class);
context.close();
}
}

Following is the log where we can show how Spring internally loads beans and resolves classes:

Account | Class loaded
Account | Constructor
Customer | Class loaded
Customer | Constructor
Account (Account?): {}[]
Bank | Class loaded
Bank | Constructor
Constructor (Bank?): {}[bank]
Constructor (Customer?): {}[customer]
Constructor (Bank?): {}[bank]

Spring Framework first loads Account and tries to instantiate a bean; however, when running getBeansOfType(Customer.class), it discovers Customer, so proceeds with loading and instantiating that one. Inside Customer, we can immediately spot the problem: when Customer asks for beanFactory.getBeansOfType(Account.class), it gets no results ([]). Spring will silently ignore Account because it's currently under creation. You can see here that after Bank is loaded, everything is as expected.

Here now we can understand that, we cannot predict the output of the getBeansOfType() method when we have a circular dependency. However, we can avoid it with using DI properly. In circular dependency, getBeansOfType() gives different results, based on factors and we have no any control over it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset