'Why are singleton beans created twice in Spring?

I have a spring-boot project that contains a BookMapper bean:

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BookMapper {
    // some CRUD methods here...
}

The bean is created by mybatis, and it's a Singleton. Usually, I use the bean with @Autowired:

@Service
public BookService {

    @Autowired
    private BookMapper bookMapper;
}

I also have a file like this:

@Component
public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext ac;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ac = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz){
        return ac.getBean(clazz);
    }

    public static <T> T getBean(String name, Class<T> clazz){
        return ac.getBean(name, clazz);
    }

}

Sometimes I just fetch the bean using the BeanUtil class:

BookMapper bookMapper = BeanUtil.getBean(BookMapper.class);

That is convenient because I can get a bean from a static method.

Now the problem is, when I run integration tests, I find that BookMapper fetched using the above two ways (@Autowired and BeanUtil) are sometimes different (and they are sometimes the same, I cannot reproduce the problem). Why? It shouldn't happen because BookMapper is a Singleton. Is there a possible reason for it?



Solution 1:[1]

I have kind of similar problem.

In my case beens are created multiple times because couple of tests have different configuration (different profiles, or overriding bean with mock) so when running all integration tests spring context is started multiple times

You have static ApplicationContext ac in BeanUtil so spring may creates different BeanUtil durring creating new context but they store ApplicationContext in static so there may be some wierd interactions between those contexts

Solution 2:[2]

After debugging for a long time, I found out the reason. I use @MockBean in my test. In this case, spring-test will create a second ApplicationContext, which is different from the one used by BeanUtil in my question's code. When you have two contexts, you will have two beans for each class.

I'm not a heavy user of @MockBean, so I just remove it for now, then the problem disappeared.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Searene