【Spring源码】- 06 扩展点之SmartInitializingSingleton

1,702 阅读5分钟
原文链接: mp.weixin.qq.com

执行时机

SmartInitializingSingleton主要用于在 IoC容器基本启动完成时进行扩展,这时非LazySingleton都已被初始化完成。所以,在该扩展点执行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;    }   }  } }}
                                                                                                                    

大致逻辑就是:

  1. 查找Class里有被 @EventListener注解的方法,存储到Map<Method, EventListener> annotatedMethods中;
  2. 然后遍历Map,对每个 @EventListener注解方法,使用EventListenerFactory工厂模式创建一个 ApplicationListener实例,默认这里是ApplicationListenerMethodAdapter类型;
  3. 最后使用context.addApplicationListenerSpring注册事件监听器;

所以,当Spring中触发事件时,会调用ApplicationListenerMethodAdapter#onApplicationEvent()方法,而该方法内部通过反射最终可以调用到 @EventListener注解方法,因为ApplicationListenerMethodAdapter内部持有 @EventListener注解方法对应的Method,以及该方法所处 Beanname信息。这样,就间接实现了将 @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));}
                                                                                                                                                                                                                    

   长按识别关注,持续输出原创