Skip to content

Spring Boot 基于 SmartInitializingSingleton 扩展点的应用

什么是 SmartInitializingSingleton?

SmartInitializingSingleton 接口定义如下:

java
public interface SmartInitializingSingleton {

	void afterSingletonsInstantiated();
}

SmartInitializingSingleton 是 Spring 框架提供的一个接口,用于在所有单例 Bean 初始化完成后执行一些特定的操作。具体来说,当所有非延迟加载的单例 Bean 都已经创建并初始化完毕后,实现了 SmartInitializingSingleton 接口的 Bean 将会被调用 afterSingletonsInstantiated() 方法。

与 InitializingBean 的区别在于,InitializingBean 是针对每一个 Bean 初始化时都会调用一次,而 SmartInitializingSingleton 则是在所有非延迟加载的单例 Bean 初始化完成后只调用一次。

自定义扩展

下面是一个自定义的 SmartInitializingSingleton 实现示例,展示了如何在所有单例 Bean 初始化完成后进行一些自定义操作。

java
@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 接口。以下是其核心代码:

  1. 定义一个扩展类 EventListenerMethodProcessor,继承 SmartInitializingSingleton 接口:
java
public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor{}
  1. IoC 容器完成所有单实例 Bean 的初始化工作后,触发 afterSingletonsInstantiated()方法执行:
java
	@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 的核心代码:

java

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 应用的行为。

Released under the MIT License.