趣图妹妹趣图妹妹05-15 23:09

Spring SmartInitializingSingleton

使用场景

我们在这样的场景下使用,当ApplicationContext 加载完所有的Bean之后,我们来做一些我们想要的操作。 下面是使用方法:

public class MyRegister implements SmartInitializingSingleton {
    Logger logger = LoggerFactory.getLogger(MyRegister.class);
    private final DefaultListableBeanFactory beanFactory;
    public MyRegister(DefaultListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    @Override
    public void afterSingletonsInstantiated() {
        Map<String, Tiger> redisCacheProviderMap = beanFactory.getBeansOfType(Tiger.class);
        logger.info("tiger pre init");
    }
}

接口说明

/**
 *<p> Callback interface triggered at the end of the singleton pre-instantiation phase
 * during {@link BeanFactory} bootstrap.
 *
 * <p>This callback variant is somewhat similar to
 * {@link org.springframework.context.event.ContextRefreshedEvent} but doesn't
 * require an implementation of {@link org.springframework.context.ApplicationListener},
 * with no need to filter context references across a context hierarchy etc.
 * It also implies a more minimal dependency on just the {@code beans} package
 * and is being honored by standalone {@link ListableBeanFactory} implementations,
 * not just in an {@link org.springframework.context.ApplicationContext} environment.
 */
 public interface SmartInitializingSingleton {}

主要摘取上面一段说明来解答疑惑:

  1. 这个接口的功能类似ContextRefreshedEvent(由ConfiguableApplicationContext.refresh()触发),ContextRefreshedEvent的具体阶段来看下面具体文档。

Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such “hot” refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not.

对比接收事件的优点:就是不需要实现ApplicationListener,引用也更加简单(不需要获取ApplicationContext),对应性能也会更好一点。

  1. 文档里说到 pre-instantiation phase 这个比较疑惑,实际上这里的pre-instantiation对应SmartInitializingSingleton的执行过程·

public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		// 省略其他步骤.........
		// Instantiate all remaining (non-lazy-init) singletons.
		finishBeanFactoryInitialization(beanFactory);
		// Last step: publish corresponding event.
		finishRefresh();
		}
}

finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
{
  //省略其他步骤.......
  // Instantiate all remaining (non-lazy-init) singletons.
  beanFactory.preInstantiateSingletons();
}

所以结论就是SmartInitializingSingleton的方法就是在finishRefresh() 触发ContextRefreshedEvent事件前执行的。所以上面的pre-instantiation phase说的就是beanFactory.preInstantiateSingletons()这方法执行。

实现

beanFactory.preInstantiateSingletons();

// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
}

DefaultListableBeanFactory 这个类的实例是怎么构造注入的?构造函数注入。

另外的方法:Bean获取ApplicationContext是需要实现Aware接口的,类似这样:

//spring @EventListner 处理器
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {
	private ConfigurableApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
				"ApplicationContext does not implement ConfigurableApplicationContext");
		this.applicationContext = (ConfigurableApplicationContext) applicationContext;
	}

官场书屋二维码

000
评论