使用场景
我们在这样的场景下使用,当ApplicationContext
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 {}
主要摘取上面一段说明来解答疑惑:
这个接口的功能类似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),对应性能也会更好一点。
文档里说到 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;
}