【Spring源码】- 06 扩展点之SmartInitializingSingleton
执行时机SmartInitializingSingleton主要用于在IoC容器基本启动完成时进行扩展,这时非Lazy的Singleton都已被初始化完成。所以,在该扩展点执行Lista...
执行时机
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
注解,并在方法参数上指定需要监听的事件类型即可:
@EventListener
public 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();
@Bean
public 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
@ConditionalOnMissingBean
public 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));
}
长按识别关注,持续输出原创
更多推荐
所有评论(0)