Spring Boot 基于 SmartInitializingSingleton 扩展点的应用
什么是 SmartInitializingSingleton?
SmartInitializingSingleton 接口定义如下:
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();
}
SmartInitializingSingleton 是 Spring 框架提供的一个接口,用于在所有单例 Bean 初始化完成后执行一些特定的操作。具体来说,当所有非延迟加载的单例 Bean 都已经创建并初始化完毕后,实现了 SmartInitializingSingleton 接口的 Bean 将会被调用 afterSingletonsInstantiated() 方法。
与 InitializingBean 的区别在于,InitializingBean 是针对每一个 Bean 初始化时都会调用一次,而 SmartInitializingSingleton 则是在所有非延迟加载的单例 Bean 初始化完成后只调用一次。
自定义扩展
下面是一个自定义的 SmartInitializingSingleton 实现示例,展示了如何在所有单例 Bean 初始化完成后进行一些自定义操作。
@Component
@Slf4j
public class CustomSmartInitializingSingleton implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
private ApplicationContext applicationContext;
private ConfigurableListableBeanFactory factory;
@Override
public void afterSingletonsInstantiated() {
String[] names = this.factory.getBeanNamesForType(Object.class);
for (String name : names) {
// 按照你的需求进行扩展, 比如找到你标记了你的注解的bean之类的
log.info("Custom SmartInitializingSingleton afterSingletonsInstantiated - {}",name);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
}
开源框架应用场景
@EventListener 注解的核心原理
在 Spring 中,传统的事件监听方式是实现 ApplicationListener 接口。Spring IoC 容器启动时会自动收集系统中所有的 ApplicationListener 并将其注册到 Spring 的事件广播器上,采用订阅/发布模式。Spring 4.2 引入了 @EventListener 注解,使得事件监听更加方便。使用 @EventListener 注解的方法会在方法参数上指定需要监听的事件类型。
背后的实现机制是通过 EventListenerMethodProcessor 类来实现的,该类继承了 SmartInitializingSingleton 接口。以下是其核心代码:
- 定义一个扩展类 EventListenerMethodProcessor,继承 SmartInitializingSingleton 接口:
public class EventListenerMethodProcessor
implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor{}
- IoC 容器完成所有单实例 Bean 的初始化工作后,触发 afterSingletonsInstantiated()方法执行:
@Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
// 忽略其他代码
processBean(beanName, type);
// 忽略其他代码
}
}
}
}
private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
// 查找类上标记了@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
// 遍历标记@EventListener注解的方法
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 创建一个ApplicationListener实例
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
// /将上面的ApplicationListener实例注册到Spring事件广播器上
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
XXL-Job 的应用
XXL-Job 是一个分布式任务调度平台,它也利用了 SmartInitializingSingleton 接口来完成一些初始化工作。XxlJobSpringExecutor 继承自 XxlJobExecutor 并实现了 SmartInitializingSingleton 接口。在 afterSingletonsInstantiated 方法中,它完成了提取 @XxlJob 注解标注的方法,并初始化 Netty 服务器。
以下是 XxlJobSpringExecutor 的核心代码:
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
// 忽略其他代码
@Override
public void afterSingletonsInstantiated() {
// init JobHandler Repository
/*initJobHandlerRepository(applicationContext);*/
// init JobHandler Repository (for method)
initJobHandlerMethodRepository(applicationContext);
// refresh GlueFactory
GlueFactory.refreshInstance(1);
// super start
try {
super.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
if (applicationContext == null) {
return;
}
// init job handler from method
String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
for (String beanDefinitionName : beanDefinitionNames) {
// get bean
Object bean = null;
Lazy onBean = applicationContext.findAnnotationOnBean(beanDefinitionName, Lazy.class);
if (onBean!=null){
logger.debug("xxl-job annotation scan, skip @Lazy Bean:{}", beanDefinitionName);
continue;
}else {
bean = applicationContext.getBean(beanDefinitionName);
}
// filter method
Map<Method, XxlJob> annotatedMethods = null; // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
try {
annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
new MethodIntrospector.MetadataLookup<XxlJob>() {
@Override
public XxlJob inspect(Method method) {
return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
}
});
} catch (Throwable ex) {
logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
}
if (annotatedMethods==null || annotatedMethods.isEmpty()) {
continue;
}
// generate and regist method job handler
for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
Method executeMethod = methodXxlJobEntry.getKey();
XxlJob xxlJob = methodXxlJobEntry.getValue();
// regist
registJobHandler(xxlJob, bean, executeMethod);
}
}
}
}
总结
SmartInitializingSingleton 接口提供了一种强大的方式,在所有非延迟加载的单例 Bean 初始化完成后执行一些特定的操作。这种机制不仅适用于自定义扩展,也被广泛应用于开源框架中。通过理解和使用 SmartInitializingSingleton,你可以更好地控制和扩展 Spring 应用的行为。