一些關於Spring的思考

public class RedisMessageListenerContainer implements InitializingBean, DisposableBean, BeanNameAware, SmartLifecycle

InitializingBean

生命週期接口

BeanFactory接口
================================================

The root interface for accessing a Spring bean container. This is the basic client view of a bean container; further interfaces such as ListableBeanFactory and org.springframework.beans.factory.config.ConfigurableBeanFactory are available for specific purposes.
This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name. Depending on the bean definition, the factory will return either an independent instance of a contained object (the Prototype design pattern), or a single shared instance (a superior alternative to the Singleton design pattern, in which the instance is a singleton in the scope of the factory). Which type of instance will be returned depends on the bean factory configuration: the API is the same. Since Spring 2.0, further scopes are available depending on the concrete application context (e.g. "request" and "session" scopes in a web environment).
The point of this approach is that the BeanFactory is a central registry of application components, and centralizes configuration of application components (no more do individual objects need to read properties files, for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and Development" for a discussion of the benefits of this approach.
Note that it is generally better to rely on Dependency Injection ("push" configuration) to configure application objects through setters or constructors, rather than use any form of "pull" configuration like a BeanFactory lookup. Spring's Dependency Injection functionality is implemented using this BeanFactory interface and its subinterfaces.
Normally a BeanFactory will load bean definitions stored in a configuration source (such as an XML document), and use the org.springframework.beans package to configure the beans. However, an implementation could simply return Java objects it creates as necessary directly in Java code. There are no constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties file, etc. Implementations are encouraged to support references amongst beans (Dependency Injection).
In contrast to the methods in ListableBeanFactory, all of the operations in this interface will also check parent factories if this is a HierarchicalBeanFactory. If a bean is not found in this factory instance, the immediate parent factory will be asked. Beans in this factory instance are supposed to override beans of the same name in any parent factory.
Bean factory implementations should support the standard bean lifecycle interfaces as far as possible. The full set of initialization methods and their standard order is: 1. BeanNameAware's setBeanName 2. BeanClassLoaderAware's setBeanClassLoader 3. BeanFactoryAware's setBeanFactory 4. ResourceLoaderAware's setResourceLoader (only applicable when running in an application context) 5. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context) 6. MessageSourceAware's setMessageSource (only applicable when running in an application context) 7. ApplicationContextAware's setApplicationContext (only applicable when running in an application context) 8. ServletContextAware's setServletContext (only applicable when running in a web application context) 9. postProcessBeforeInitialization methods of BeanPostProcessors 10. InitializingBean's afterPropertiesSet 11. a custom init-method definition 12. postProcessAfterInitialization methods of BeanPostProcessors
On shutdown of a bean factory, the following lifecycle methods apply: 1. DisposableBean's destroy 2. a custom destroy-method definition


==================================================

BeanFactory是訪問Spring bean 容器的root 接口。這是bean容器的基本客戶端視圖

主要的實現類有ListableBeanFactory和ConfigurableBeanFactory。

Spring單例模式是線程安全的嗎?
這兩個概念完全不同,因爲線程安全與bean本身的代碼相關,與其實例化無關。

單例模式是一種實例創建模式。
餓漢單例不存在多線程同時實例化問題,因爲在系統啓動的時候就創建單例。

懶漢單例有線程安全問題,需要使用同步機制。

線程安全是關於執行過程的,多線程在同一時刻修改共享資源的時候會存在數據競爭,如果不採用同步機制,就會有線程安全的問題。

即使使用的是線程安全的容器,不正確的編碼,也會造成線程安全問題。

比如使用atomic包下的類進行運算,如果多個原子類計算的結果算一個事務,那需要爲這一個事務加鎖,否則不能保證整體的計算結果是對的。

https://stackoverflow.com/questions/17342850/are-spring-singleton-beans-thread-safe/17344009#17344009

Spring中 設置lazy-init後(懶加載),是如何保證線程安全的。


我們知道,使用@Service、@Controller、@Component等Spring註解後,Spring註解掃描時會將這些類的作用域定義爲Singleton。
即Spring管理的Bean默認的作用域就是單例的bean,即在系統中只存在一個實例。
如果使用註解@Scope(ConfigurableListableBeanFactory.SCOPE_SINGLETON)可以改變作用域。
@Lazy(true)

Spring默認的單例是使用餓漢模式實現,本身在實例化的過程中,不存在線程安全問題。

使用過程中的線程安全問題,涉及到全局變量(類變量和實例變量)、局部變量自身的線程安全問題。

如果是一個全局變量,多線程訪問存在線程安全問題,Spring中使用ThreadLocal解決,將全局變量轉化爲每個線程的本地變量,從而解決線程安全問題。


另一方面,在使用Singleton時,可以指定lazy-init懶漢模式,那麼就可能在初始化的時候存在問題,Spring是如何解決的?

SimpleInstantiationStrategy.java

@Override
    public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (bd.getMethodOverrides().isEmpty()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor((Class[]) null);
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
    
Spring將要管理的bean都會先加載,把類加載和實例化分開了。
實例化的時候調用默認構造方法或者cglib代理生成代理子類實例化子類

想要了解實例化的過程是否存在線程安全問題,首先需要知道類加載和實例化的過程

類加載jvm會提供同步保護,加鎖

實例化不會提供同步,實例化分爲三個步驟

1、分配內存
2、初始化對象
3、將內存空間的地址賦值給對象引用

其中二三步會重排序優化

如果不想重排序優化,可以將實例屬性使用volatile,阻止重排序
如果允許重排序,但是隻讓線程自己可見這個重排序,這樣就不影響其他線程,可以使用類加載機制的實現實例化的線程安全

Class.forName默認需要初始化,觸發對象的靜態屬性和靜態代碼塊的執行

ClassLoader.loadclass默認不會進行鏈接,只加載類,所以不會觸發static修飾的類成員和類方法的初始化

=================================================

說說Spring中bean的作用域
singleton、prototype、sesseion、request、global session

Spring中的bean默認作用域是什麼
singleton

Spring中的singleton是線程安全的嗎
Spring中如果管理的bean是無狀態的,天然線程安全,但是如果bean有成員變量,就變成有狀態的bean,就需要藉助threadlocal解決線程安全問題

Spring中bean的生命週期和擴展點

Spring中使用的設計模式例舉和講解

=========================

爲什麼我們不自己寫BeanFactory,而是使用Spring提供的?

Spring提供bean的聲明週期的統一管理,提供一系列擴展點,可以在bean的聲明週期內搞事情,比如實現AOP攔截,屬性注入,時間監聽等等

=========================

Spring中屬性注入的原理:

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

/**
 * Allows for custom modification of an application context's bean definitions,
 * adapting the bean property values of the context's underlying bean factory.
 *
 * <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
 * their bean definitions and apply them before any other beans get created.
 *
 * <p>Useful for custom config files targeted at system administrators that
 * override bean properties configured in the application context.
 *
 * <p>See PropertyResourceConfigurer and its concrete implementations
 * for out-of-the-box solutions that address such configuration needs.
 *
 * <p>A BeanFactoryPostProcessor may interact with and modify bean
 * definitions, but never bean instances. Doing so may cause premature bean
 * instantiation, violating the container and causing unintended side-effects.
 * If bean instance interaction is required, consider implementing
 * {@link BeanPostProcessor} instead.
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

postProcessBeanFactory方法,傳入beanFactory,可以對其中的bean進行修改,

PropertyPlaceHolderConfigurer類的祖父類PropertyResourceConfigurer中實現了postProcessBeanFactory方法

/**
	 * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
	 * {@linkplain #processProperties process} properties against the given bean factory.
	 * @throws BeanInitializationException if any properties cannot be loaded
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}

processProperties方法是一個模板方法,子類實現,實現邏輯在PropertyPlaceHolderConfigurer類中

/**
	 * Visit each bean definition in the given bean factory and attempt to replace ${...} property
	 * placeholders with values from the given properties.
	 */
	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {

		StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);

		this.doProcessProperties(beanFactoryToProcess, valueResolver);
	}

剩下的就是,遍歷beanFactory中的bean,替換屬性值(Visit each bean definition in the given bean factory and attempt to replace ${...} property placeholders with values from the given properties.)

protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {

		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
		for (String curName : beanNames) {
			// Check that we're not parsing our own bean definition,
			// to avoid failing on unresolvable placeholders in properties file locations.
			if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
					visitor.visitBeanDefinition(bd);
				}
				catch (Exception ex) {
					throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
				}
			}
		}

		// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
		beanFactoryToProcess.resolveAliases(valueResolver);

		// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
		beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
	}

總結:

寫了這麼多,上面都是細節。

明確知道一點很重要:屬性配置文件自動注入,是使用BeanFactoryPostProcessor擴展點的,擴展時機是:bean定義加載完,bean初始化前

 

編碼啓示:

編碼的過程中,如果想要在bean定義完,bean初始化前做一些事,可以使用BeanFactoryPostProcessor擴展點搞事情。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章