Spring4.1新しい特性SmartInitializingSingletonのRibbonへの応用

8076 ワード

SmartInitializingSingletonはspring 4.1に導入された新しい特効であり、InitializingBeanの機能と同様に、beanがインスタンス化された後にカスタム初期化が実行され、spring beanのライフサイクルの強化に属します.しかし、SmartInitializingSingletonの定義とトリガ方式にはいくつかの違いがあり、その定義は現在のbean(a bean's local construction phase)ではなく、コールバックインタフェース(lazyではない単一のBeanの場合)であり、コールバックの操作はspringイベントContextRefreshedEventによってトリガされる.
SmartInitializingSingletonはspring beanのもう一つの進歩であり、beanとbeanのカスタム初期化をインスタンス化することは、クラス内またはパッケージ内でなくてもよい.
Callback interface triggered at the end of the singleton pre-instantiation phase  during {@link BeanFactory} bootstrap. This interface can be implemented by singleton beans in order to perform some initialization after the regular singleton instantiation algorithm, avoiding side effects with accidental early initialization (e.g. from {@link ListableBeanFactory#getBeansOfType} calls). In that sense, it is an alternative to {@link InitializingBean} which gets triggered right at the end of a bean's local construction phase.
This callback variant is somewhat similar to  {@link org.springframework.context.event.ContextRefreshedEvent} but doesn't require an implementation of {@link org.springframework.context.ApplicationListener}, with no need to filter context references across a context hierarchy etc. It also implies a more minimal dependency on just the {@code beans} package and is being honored by standalone {@link ListableBeanFactory} implementations, not just in an {@link org.springframework.context.ApplicationContext} environment.
ContextRefreshedEventトリガとコールバックの理解には2つの意味があります.
  • イベントがトリガーされ、ContextRefreshedEventイベントを介してAbstractApplicationContextが実際に実行する.refresh
  • コールバックは、すべてのbeanがインスタンス化された後、SmartInitializingSingletonを呼び出して
  • を初期化することを指す.
    ソースを見て
    public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    	@Override
    	public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    				...
    				// 
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);
    				// Last step: publish corresponding event.
    				finishRefresh();
    			}
    			catch (BeansException ex) {
    				if (logger.isWarnEnabled()) {
    					logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);
    				}
    				// Destroy already created singletons to avoid dangling resources.
    				destroyBeans();
    				// Reset 'active' flag.
    				cancelRefresh(ex);
    				// Propagate exception to caller.
    				throw ex;
    			}
    			finally {
    				// Reset common introspection caches in Spring's core, since we
    				// might not ever need metadata for singleton beans anymore...
    				resetCommonCaches();
    			}
    		}
    	}
    	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    		// Initialize conversion service for this context.
    		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
    				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
    			beanFactory.setConversionService(
    					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    		}
    		// Register a default embedded value resolver if no bean post-processor
    		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
    		// at this point, primarily for resolution in annotation attribute values.
    		if (!beanFactory.hasEmbeddedValueResolver()) {
    			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    		}
    		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    		for (String weaverAwareName : weaverAwareNames) {
    			getBean(weaverAwareName);
    		}
    		// Stop using the temporary ClassLoader for type matching.
    		beanFactory.setTempClassLoader(null);
    		// Allow for caching all bean definition metadata, not expecting further changes.
    		beanFactory.freezeConfiguration();
    		// :DefaultListableBeanFactory.preInstantiateSingletons
    		// Instantiate all remaining (non-lazy-init) singletons.
    		beanFactory.preInstantiateSingletons();
    	}
    }
    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    	@Override
    	public void preInstantiateSingletons() throws BeansException {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Pre-instantiating singletons in " + this);
    		}
    		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
    		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    		List beanNames = new ArrayList<>(this.beanDefinitionNames);
    		// Trigger initialization of all non-lazy singleton beans...
    		for (String beanName : beanNames) {
    			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
    				if (isFactoryBean(beanName)) {
    					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
    					if (bean instanceof FactoryBean) {
    						final FactoryBean> factory = (FactoryBean>) bean;
    						boolean isEagerInit;
    						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
    							isEagerInit = AccessController.doPrivileged((PrivilegedAction)
    											((SmartFactoryBean>) factory)::isEagerInit,
    									getAccessControlContext());
    						}
    						else {
    							isEagerInit = (factory instanceof SmartFactoryBean &&
    									((SmartFactoryBean>) factory).isEagerInit());
    						}
    						if (isEagerInit) {
    							getBean(beanName);
    						}
    					}
    				}
    				else {
    					getBean(beanName);
    				}
    			}
    		}
    		// SmartInitializingSingleton afterSingletonsInstantiated
    		// Trigger post-initialization callback for all applicable beans...
    		for (String beanName : beanNames) {
    			Object singletonInstance = getSingleton(beanName);
    			if (singletonInstance instanceof SmartInitializingSingleton) {
    				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
    				if (System.getSecurityManager() != null) {
    					AccessController.doPrivileged((PrivilegedAction) () -> {
    						smartSingleton.afterSingletonsInstantiated();
    						return null;
    					}, getAccessControlContext());
    				}
    				else {
    					smartSingleton.afterSingletonsInstantiated();
    				}
    			}
    		}
    	}
    }

    Ribbonでの応用
    Ribbon SmartInitializingSingletonを使用してカスタマイズされたRestTemplate
    @Configuration
    @ConditionalOnClass({RestTemplate.class})
    @ConditionalOnBean({LoadBalancerClient.class})
    @EnableConfigurationProperties({LoadBalancerRetryProperties.class})
    public class LoadBalancerAutoConfiguration {
        @LoadBalanced
        @Autowired(
            required = false
        )
        private List restTemplates = Collections.emptyList();
        public LoadBalancerAutoConfiguration() {
        }
    
        @Bean
        public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List customizers) {
            return new SmartInitializingSingleton() {
                public void afterSingletonsInstantiated() {
                    Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator();
    
                    while(var1.hasNext()) {
                        RestTemplate restTemplate = (RestTemplate)var1.next();
                        Iterator var3 = customizers.iterator();
    
                        while(var3.hasNext()) {
                            RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next();
                            customizer.customize(restTemplate);
                        }
                    }
    
                }
            };
        }
    }

    Ribbonについてまだよく知らない場合は、私のもう一つの章Spring Cloud Ribbonソースコード解析を参照してください.