执行时机
SmartInitializingSingleton主要用于在 IoC容器基本启动完成时进行扩展,这时非Lazy的 Singleton都已被初始化完成。所以,在该扩展点执行ListableBeanFactory#getBeansOfType()等方法不会出现因过早加载 Bean出现副作用。这个扩展点Spring 4.1开始引入,其定义如下:
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();}
这个扩展点,可能和我们平时采用事件监听机制ApplicationListener<ContextRefreshedEvent>监听容器启动完成事件功能很类似。现在我们来看下该扩展点触发代码位置,是在 DefaultListableBeanFactory#preInstantiateSingletons()方法中最后执行:
for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); // 如果实例实现了SmartInitializingSingleton,执行afterSingletonsInstantiated方法。 if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { smartSingleton.afterSingletonsInstantiated(); return null; }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } }}
使用场景
Spring中有个 SmartInitializingSingleton接口实现类:EventListenerMethodProcessor,主要用于完成 @EventListener注解方式的事件监听。在Spring中需要监听某个事件常规方式是实现 ApplicationListener接口,Spring IoC容器启动时自动会收集系统中所有
ApplicationListener资料,并将其注册到Spring的事件广播器上,采用典型的订阅/发布模式。 Spring 4.2引入了@EventListener注解方式,可以更加方便的对事件进行监听,使用方式如下如下,只需要在方法上使用 @EventListener注解,并在方法参数上指定需要监听的事件类型即可:
@EventListenerpublic void onEvent(ContextRefreshedEvent event){ System.out.println("===receive ContextRefreshedEvent===");}
@EventListener注解的背后, Spring做了哪些工作以支持该注解功能呢?
1、首先,定义一个扩展类EventListenerMethodProcessor,继承 SmartInitializingSingleton接口:
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor
2、当IoC容器完成所有单实例Bean的初始化工作后,触发afterSingletonsInstantiated()方法执行:
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); ... } }}
该方法中,获取所有IoC容器中的实例,然后遍历使用processBean()方法进行处理,其核心代码见下面:
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; //查找Class上所有被@EventListener注解的方法 annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); ConfigurableApplicationContext context =
this.applicationContext; Assert.state(context != null, "No ApplicationContext set"); List<EventListenerFactory> factories =
this.eventListenerFactories; Assert.state(factories != null, "EventListenerFactory List not initialized");
for (Method method : annotatedMethods.keySet()) {//遍历所有被@EventListener注解的方法
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; } } } }}
大致逻辑就是:
-
查找
Class里有被@EventListener注解的方法,存储到Map<Method, EventListener> annotatedMethods中; -
然后遍历
Map,对每个@EventListener注解方法,使用EventListenerFactory工厂模式创建一个ApplicationListener实例,默认这里是ApplicationListenerMethodAdapter类型; -
最后使用
context.addApplicationListener向Spring注册事件监听器;
所以,当Spring中触发事件时,会调用ApplicationListenerMethodAdapter#onApplicationEvent()方法,而该方法内部通过反射最终可以调用到 @EventListener注解方法,因为ApplicationListenerMethodAdapter内部持有 @EventListener注解方法对应的Method,以及该方法所处
Bean的name信息。这样,就间接实现了将 @EventListener方法包装成了ApplicationListener对象。
还比如,Spring Cloud Ribbon组件中, LoadBalancerAutoConfiguration自动装配类中,就向Spring中注入了一个 SmartInitializingSingleton实现类,利用该实现类,当IoC容器要启动完成时,将所有带有@LoadBalanced注解的 RestTemplate对象利用RestTemplateCustomizer进行定制处理:
@LoadBalanced@Autowired(required =
false)private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Beanpublic SmartInitializingSingleton
loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.
this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } });}
而常见的处理就是,给RestTemplate添加一个 LoadBalancerInterceptor类型的拦截器:
@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); };}
该拦截器会拦截RestTemplate请求,从 url地址中解析出需要请求的serviceId,当该 serviceId对应多台主机时,Ribbon就会根据一定策略指定其中一台,这样就实现负载均衡功能。
public ClientHttpResponse
intercept(final HttpRequest request,
final byte[] body,
final ClientHttpRequestExecution execution)
throws IOException {
final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName !=
null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));}
长按识别关注,持续输出原创
