Spring Core IOC 隨手筆記

The most flexible variant is GenericApplicationContext in combination with reader delegates — for example, with XmlBeanDefinitionReader for XML files, as the following example shows:
最靈活的變體是GenericApplicationContext與讀取器委託結合使用,例如,與XML文件的XmlBeanDefinitionReader結合使用,如以下示例所示:

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

The bean definition

Property Explained in…​
Class Instantiating Beans
Name Naming Beans
Scope Bean Scopes
Constructor arguments Dependency Injection
Properties Dependency Injection
Autowiring mode Autowiring Collaborators
Lazy initialization mode Lazy-initialized Beans
Initialization method Initialization Callbacks
Destruction method Destruction Callbacks

Bean 註冊 補充

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

除了包含有關如何創建特定bean的信息的bean定義之外,ApplicationContext實現還允許由用戶創建的Spring 託管類。這是通過通過getBeanFactory()方法訪問ApplicationContext的BeanFactory來完成的,該方法返回BeanFactory DefaultListableBeanFactory實現。DefaultListableBeanFactory通過registerSingleton(…)和registerBeanDefinition(…)方法支持此註冊。但是,典型的應用程序只能與通過常規bean定義元數據定義的bean一起使用。

Spring 標籤

P-NameSpace

簡化內部屬性XML的配置 since Spring 3.1

xml 簡化配置 ,達到相同效果

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

C-NameSpace

簡化構造函數XML屬性的配置 since Spring 3.1

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="[email protected]"/>
    </bean>

    <!-- c-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="[email protected]"/>

</beans>

depends-on

如果一個bean是另一個bean的依賴項,則通常意味着將一個bean設置爲另一個bean的屬性。通常,您可以使用基於XML的配置元數據中的元素來完成此操作。但是,有時bean之間的依賴性不太直接。一個示例是何時需要觸發類中的靜態初始值設定項,例如用於數據庫驅動程序註冊。依賴屬性可以顯式地強制初始化一個或多個使用該元素的bean之前的bean。以下示例使用depends-on屬性來表示對單個bean的依賴關係:

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao" p:manager-ref="manager"></bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

idref

The idref element is simply an error-proof way to pass the id (a string value - not a reference) of another bean in the container to a <constructor-arg/> or <property/> element. The following example shows how to use it:

Google翻譯:

idref元素只是一種防錯方法,可以將容器中另一個bean的id(字符串值-不是引用)傳遞給<constructor-arg />或<property />元素。以下示例顯示瞭如何使用它:

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
替代:
<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

第一種形式優於第二種形式,因爲使用idref標記可使容器在部署時驗證所引用的命名bean實際存在。

在第二個變體中,不對傳遞給客戶端bean的targetName屬性的值執行驗證。

拼寫錯誤僅在實際實例化客戶端bean時才發現(可能會導致致命的結果)。如果客戶端bean是原型bean,則可能在部署容器很長時間之後才發現此錯字和所產生的異常。

null

元素處理空值。以下清單顯示了一個示例:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

Spring bean 範圍

Scope Description 中文
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. (默認值)將每個Spring IoC容器的單個bean定義範圍限定爲單個對象實例。
prototype Scopes a single bean definition to any number of object instances. 將單個bean定義的作用域限定爲任意數量的對象實例。
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. 將單個bean定義的範圍限定爲單個HTTP請求的生命週期。也就是說,每個HTTP請求都有一個在單個bean定義後面創建的bean實例。僅在可感知網絡的Spring ApplicationContext上下文中有效。
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. 將單個bean定義的作用域限定爲HTTP會話的生命週期。僅在可感知網絡的Spring ApplicationContext上下文中有效。
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. 將單個bean定義的作用域限定爲ServletContext的生命週期。僅在可感知網絡的Spring ApplicationContext上下文中有效。。
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. 將單個bean定義的作用域限定爲WebSocket的生命週期。僅在可感知網絡的Spring ApplicationContext上下文中有效。

prototype

與其他作用域相反,Spring不管理prototype Bean的完整生命週期。容器實例化,配置或組裝原型對象,然後將其交給客戶端,而沒有該原型實例的進一步記錄。因此,儘管在不考慮範圍的情況下在所有對象上都調用了初始化生命週期回調方法,但對於原型而言,不會調用已配置的銷燬生命週期回調。客戶端代碼必須清除prototype作用域內的對象並釋放原型Bean擁有的昂貴資源。爲了使Spring容器釋放原型作用下的bean所擁有的資源,請嘗試使用自定義bean後處理器,該處理器具有對需要清理的bean的引用。

但是,假設您希望單例作用域的bean在運行時重複獲取原型作用域的bean的新實例。您不能將prototype作用域的bean依賴項注入到您的單例bean中,因爲當Spring容器實例化單例bean並解析並注入其依賴項時,該注入僅發生一次。如果在運行時不止一次需要原型bean的新實例,請參見方法注入

The request, session, application, and websocket

僅當您使用可感知網絡的Spring ApplicationContext實現(例如XmlWebApplicationContext)時request, session, application, and websocket 範圍纔可用。如果您將這些作用域與常規的Spring IoC容器(例如ClassPathXmlApplicationContext)一起使用,則會拋出一個IllegalStateException,拋出未知的bean作用域。

自定義Scope

  • 實現org.springframework.beans.factory.config.Scope 接口

  • org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope 注入

  • code

public class SimpleMapScope implements Scope, Serializable {

	private final Map<String, Object> map = new HashMap<>();

	private final List<Runnable> callbacks = new LinkedList<>();


	public SimpleMapScope() {
	}

	public final Map<String, Object> getMap() {
		return this.map;
	}


	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		synchronized (this.map) {
			Object scopedObject = this.map.get(name);
			if (scopedObject == null) {
				scopedObject = objectFactory.getObject();
				this.map.put(name, scopedObject);
			}
			return scopedObject;
		}
	}

	@Override
	public Object remove(String name) {
		synchronized (this.map) {
			return this.map.remove(name);
		}
	}

	@Override
	public void registerDestructionCallback(String name, Runnable callback) {
		this.callbacks.add(callback);
	}

	@Override
	public Object resolveContextualObject(String key) {
		return null;
	}

	public void close() {
		for (Iterator<Runnable> it = this.callbacks.iterator(); it.hasNext();) {
			Runnable runnable = it.next();
			runnable.run();
		}
	}

	@Override
	public String getConversationId() {
		return null;
	}

}

-----
@Test
	public void testScopedOverride() throws Exception {
		GenericApplicationContext ctx = new GenericApplicationContext();
		new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
		SimpleMapScope scope = new SimpleMapScope();
		ctx.getBeanFactory().registerScope("request", scope);
		ctx.refresh();

		ITestBean bean = (ITestBean) ctx.getBean("testBean");
		assertThat(bean.getName()).isEqualTo("male");
		assertThat(bean.getAge()).isEqualTo(99);

		assertThat(scope.getMap().containsKey("scopedTarget.testBean")).isTrue();
		assertThat(scope.getMap().get("scopedTarget.testBean").getClass()).isEqualTo(TestBean.class);
	}
	

Spring Bean 自定義特性

Spring框架提供了許多接口,可用於自定義Bean特性。

生命週期回調

爲了與容器對bean生命週期的管理進行交互,您可以實現Spring InitializingBean和DisposableBean接口。容器爲前者調用afterPropertiesSet()併爲後者調用destroy(),以使Bean在初始化和銷燬​​Bean時執行某些操作。

JSR-250 @PostConstruct和@PreDestroy批註通常被認爲是在現代Spring應用程序中接收生命週期回調的最佳實踐。使用這些註釋意味着您的bean沒有耦合到特定於Spring的接口。有關詳細信息,請參見使用@PostConstruct和@PreDestroy。
如果不想使用JSR-250批註,但仍然想刪除耦合,請考慮使用init-method和destroy-method Bean定義元數據。

ApplicationContextAware and BeanNameAware

org.springframework.context.annotation.AnnotationConfigApplicationContext源碼:

繼承圖;
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gazOWPyz-1587892064597)(leanote://file/getImage?fileId=5ea3d418ab6441676901cb4b)]

實例化過程:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-j2D6Pm3M-1587892064601)(leanote://file/getImage?fileId=5ea3d418ab6441676901cb4a)]

Spring 註釋掃描內部框架 手動註冊自身bean:

org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)

Aware 初始化

org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces

啓動和關機回調 Startup and Shutdown Callbacks

Lifecycle接口爲具有自己的生命週期要求(例如啓動和停止某些後臺進程)的任何對象定義基本方法:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0mcjO8r6-1587892064603)(leanote://file/getImage?fileId=5ea45920ab64416769030979)]
任何Spring管理的對象都可以實現Lifecycle接口。然後,當ApplicationContext本身接收到啓動和停止信號時(例如,對於運行時的停止/重新啓動方案),它將把這些調用級聯到在該上下文中定義的所有Lifecycle實現。它通過委派給LifecycleProcessor來做到這一點,如以下清單所示:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}

Spring 源碼:

org.springframework.context.support.AbstractApplicationContext#finishRefresh

初始化LifeCycle

org.springframework.context.support.AbstractApplicationContext#initLifecycleProcessor

初始化 BeanPostProcess

org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory

BeanPostProcess

BeanPostProcessor接口定義了回調方法,您可以實施這些回調方法以提供自己的(或覆蓋容器的默認值)實例化邏輯,依賴關係解析邏輯等。
如果您想在Spring容器完成實例化,配置和初始化bean之後實現一些自定義邏輯,則可以插入一個或多個自定義BeanPostProcessor實現

  • 系統初始化 第一步:允許在上下文子類中對bean工廠進行後處理
org.springframework.context.support.AbstractApplicationContext#postProcessBeanFactory

子類 實現 之一

org.springframework.web.context.support.GenericWebApplicationContext#postProcessBeanFactory
/**
	 * Register ServletContextAwareProcessor.
	 * @see ServletContextAwareProcessor
	 */
	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		if (this.servletContext != null) {
			beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
			beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		}
		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
	}
  • 調用在上下文中註冊爲bean的工廠處理器。
org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

委託實現類:

org.springframework.context.support.PostProcessorRegistrationDelegate  

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Pp9IlW2m-1587892064606)(leanote://file/getImage?fileId=5ea4f71bfba2f45278000001)]

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-q8faysGT-1587892064607)(leanote://file/getImage?fileId=5ea4f827fba2f45278000002)]

beforeSingletonCreation(beanName);
//默認設置不會新建 單例的Bean
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					singletonObject = singletonFactory.getObject();
				//當 getBean 不會獲得時,註冊標識爲true
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
/**
	 * Add the given singleton object to the singleton cache of this factory.
	 * <p>To be called for eager registration of singletons.
	 * @param beanName the name of the bean
	 * @param singletonObject the singleton object
	 */
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

創建Bean :

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
//Bean 定義文件
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target 
			//依賴BeanPostProcess生成Java帶路對象
			bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

  • 註冊攔截Bean創建的BeanProcess。
org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors

ApplicationListener 檢測BeanPostProcess

org.springframework.beans.factory.config.ConfigurableBeanFactory#addBeanPostProcessor
254:		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));

  • Bean 初始化後的 BeanPostProcess

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object,
org.springframework.beans.factory.support.RootBeanDefinition)

//繼承了 BeanPostProcess 的 初始化
	if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
//使用 @PostConstruct 初始化
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

如果使用的是註解

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods

ApplicationListener

Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
 * that it is interested in. When registered with a Spring ApplicationContext, events
 * will be filtered accordingly, with the listener getting invoked for matching event
 * objects only.
google翻譯
由應用程序事件偵聽器實現的接口。 *基於觀察者設計模式的標準{@code java.util.EventListener}接口。 * * <p>從Spring 3.0開始,ApplicationListener可以一般性地聲明它感興趣的事件類型*。在Spring ApplicationContext中註冊時,事件*將被相應地過濾,而偵聽器將被調用以匹配事件*對象
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
// Instantiate all remaining (non-lazy-init)singletons
//初始化非懶加載的單例Bean.
877:		beanFactory.preInstantiateSingletons();
org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons

實現:

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

Spring Aop 代理對象 生成

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
/**
	 * Apply before-instantiation post-processors, resolving whether there is a
	 * before-instantiation shortcut for the specified bean.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @return the shortcut-determined bean instance, or {@code null} if none
	 */
	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
//屬性可見性 公開的 纔可以初始化
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
//返回此bean定義是否是“合成的”,即*不是由應用程序本身定義的。  且返回此工廠是否擁有InstantiationAwareBeanPostProcessor *,它將在關機時應用於單例bean。
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor。
該接口的語義與BeanPostProcessor的語義相似,但有一個主要區別:BeanFactoryPostProcessor對Bean配置元數據進行操作。
也就是說,Spring IoC容器允許BeanFactoryPostProcessor讀取配置元數據,並有可能在容器實例化除BeanFactoryPostProcessor實例以外的任何bean之前更改它。

您可以配置多個BeanFactoryPostProcessor實例,並且可以通過設置order屬性來控制這些BeanFactoryPostProcessor實例的運行順序。但是,僅當BeanFactoryPostProcessor實現Ordered接口時,纔可以設置此屬性。如果您編寫自己的BeanFactoryPostProcessor,則也應該考慮實現Ordered接口。有關更多詳細信息,請參見BeanFactoryPostProcessor和Ordered接口的javadoc。

Using Filters to Customize Scanning 自定義掃描器

Filter Type Example Expression Description Google 翻譯
annotation (default) org.example.SomeAnnotation An annotation to be present or meta-present at the type level in target components. 在目標組件中的類型級別上存在或元存在的註釋。
assignable org.example.SomeClass A class (or interface) that the target components are assignable to (extend or implement). 目標組件可分配給(擴展或實現)的類(或接口)。
aspectj org.example…*Service+ An AspectJ type expression to be matched by the target components. 目標組件要匹配的AspectJ類型表達式。
regex org.example.Default.* A regex expression to be matched by the target components’ class names. 要與目標組件的類名匹配的正則表達式。
custom org.example.MyTypeFilter A custom implementation of theorg.springframework.core.type.TypeFilterinterface.org.springframework.core.type.TypeFilter 接口的自定義實現。
@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

@Configuration

常規Spring組件中的@Bean方法的處理方式與Spring @Configuration類中的@Bean方法不同。區別在於@Component類沒有使用CGLIB增強,無法攔截方法和字段的調用。CGLIB代理是調用@Configuration類中@Bean方法中的方法或字段的方法,用於創建Bean元數據引用來協作對象。此類方法不是用普通的Java語義調用的,而是通過容器進行的,以提供Spring Bean的常規生命週期管理和代理,即使通過@Bean方法的編程調用引用其他Bean時也是如此。相反,在普通@Component類內的@Bean方法中調用方法或字段具有標準Java語義,而無需特殊的CGLIB處理或其他約束。

Spring Bean 自動命名

若在 @Component, @Repository, @Service, and @Controller 等 提供了 bean 名稱 ,Spring 則使用自定義名稱 ,否則 使用才用自動命名(默認 爲 類名首字母小寫)

如果不想依賴默認的Bean命名策略,則可以提供自定義Bean命名策略。首先,實現BeanNameGenerator接口,並確保包括默認的無參數構造函數。然後,在配置掃描程序時提供完全限定的類名,如以下示例註釋和Bean定義所示。
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    // ...
}

生成候選組件的索引

儘管類路徑掃描非常快,但是可以通過在編譯時創建候選靜態列表來提高大型應用程序的啓動性能。在這種模式下,作爲組件掃描目標的所有模塊都必須使用此機制。

您現有的@ComponentScan或<context:component-scan指令必須保留原樣,以請求上下文掃描某些程序包中的候選對象。當ApplicationContext檢測到這樣的索引時,它將自動使用它而不是掃描類路徑。
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.5.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

該過程將生成一個包含在jar文件中的META-INF / spring.components文件。

在類路徑上找到META-INF / spring.components時,將自動啓用索引。如果某些庫(或用例)的索引部分可用,但無法爲整個應用程序構建,則可以通過將spring.index.ignore設置爲來回退到常規的類路徑安排(好像根本沒有索引)。true,可以是系統屬性,也可以是classpath根目錄下的spring.properties文件。

使用AnnotationConfigWebApplicationContext支持Web應用程序

AnnotationConfigApplicationApplicationContext的WebApplicationContext變體可用於AnnotationConfigWebApplicationContext。在配置Spring ContextLoaderListener Servlet偵聽器,Spring MVC DispatcherServlet等時,可以使用此實現。以下web.xml代碼段配置了一個典型的Spring MVC Web應用程序(請注意contextClass context-param和init-param的使用):
<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

註冊 LoadTimeWeaver

Spring使用LoadTimeWeaver在將類加載到Java虛擬機(JVM)中時對其進行動態轉換。

要啓用加載時編織,可以將@EnableLoadTimeWeaving添加到您的@Configuration類之一,如以下示例所示:

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}

爲ApplicationContext配置後,該ApplicationContext中的任何bean都可以實現LoadTimeWeaverAware,從而接收到對加載時間weaver實例的引用。與Spring的JPA支持結合使用時,該功能特別有用,因爲在JPA類轉換中可能需要進行加載時編織。有關更多詳細信息,請查閱LocalContainerEntityManagerManagerBean javadoc。有關AspectJ加載時編織的更多信息,請參見Spring框架中的AspectJ加載時編織。

Spring 標準事件

Event Explanation Google 翻譯
ContextRefreshedEvent Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such “hot” refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not. 在初始化或刷新ApplicationContext時發佈(例如,通過使用ConfigurableApplicationContext接口上的refresh()方法)。在這裏,“已初始化”是指所有Bean均已加載,檢測到並激活了後處理器Bean,已預先實例化單例並且可以使用ApplicationContext對象。只要尚未關閉上下文,只要選定的ApplicationContext實際上支持這種“熱”刷新,就可以多次觸發刷新。例如,XmlWebApplicationContext支持熱刷新,但GenericApplicationContext不支持。
ContextStartedEvent Published when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext interface. Here, “started” means that all Lifecycle beans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization). 在ConfigurableApplicationContext接口上使用start()方法啓動ApplicationContext時發佈。在這裏,“啓動”表示所有Lifecycle bean都收到一個明確的啓動信號。通常,此信號用於在顯式停止後重新啓動Bean,但也可以用於啓動尚未配置爲自動啓動的組件(例如,尚未在初始化時啓動的組件)。
ContextStoppedEvent Published when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext interface. Here, “stopped” means that all Lifecycle beans receive an explicit stop signal. A stopped context may be restarted through a start() call. 通過使用ConfigurableApplicationContext接口上的stop()方法停止ApplicationContext時發佈。在這裏,“已停止”表示所有Lifecycle bean都收到一個明確的停止信號。停止的上下文可以通過start()調用重新啓動。
ContextClosedEvent Published when the ApplicationContext is being closed by using the close() method on the ConfigurableApplicationContext interface or via a JVM shutdown hook. Here, “closed” means that all singleton beans will be destroyed. Once the context is closed, it reaches its end of life and cannot be refreshed or restarted. 通過使用ConfigurableApplicationContext接口上的close()方法或通過JVM關閉鉤子關閉ApplicationContext時發佈。在這裏,“封閉”意味着所有單例Bean將被銷燬。關閉上下文後,它將達到使用壽命,無法刷新或重新啓動。
RequestHandledEvent A web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s DispatcherServlet. 一個特定於Web的事件,告訴所有Bean HTTP請求已得到服務。請求完成後,將發佈此事件。此事件僅適用於使用Spring的DispatcherServlet的Web應用程序。
ServletRequestHandledEvent A subclass of RequestHandledEvent that adds Servlet-specific context information. RequestHandledEvent的子類,添加了特定於Servlet的上下文信息。
  • 事件發佈
org.springframework.context.ApplicationEventPublisher
  • 事件監聽
org.springframework.context.ApplicationListener
  • 基於註釋的事件偵聽器
public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
    @EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    // ...
}
}
  • 異步偵聽器
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

BeanFactory

BeanFactory API爲Spring的IoC功能提供了基礎。它的特定合同主要用於與Spring的其他部分以及相關的第三方框架集成,並且其DefaultListableBeanFactory實現是更高級別的GenericApplicationContext容器中的關鍵委託。

BeanFactory和相關接口(例如BeanFactoryAware,InitializingBean,DisposableBean)是其他框架組件的重要集成點。通過不需要任何註釋,甚至不需要反射,它們可以在容器及其組件之間進行非常有效的交互。應用程序級Bean可以使用相同的回調接口,但通常更喜歡通過註釋或通過程序配置進行聲明式依賴注入。

請注意,核心BeanFactory API級別及其DefaultListableBeanFactory實現不對配置格式或要使用的任何組件註釋進行假設。所有這些風味都是通過擴展(例如XmlBeanDefinitionReader和AutowiredAnnotationBeanPostProcessor)引入的,並以核心元數據表示形式對共享的BeanDefinition對象進行操作。這就是使Spring的容器如此靈活和可擴展的本質。

因爲ApplicationContext包含BeanFactory的所有功能,所以通常建議在純BeanFactory上使用,除非需要對Bean處理的完全控制。在ApplicationContext(例如GenericApplicationContext實現)中,按照約定(即,按bean名稱或按bean類型(尤其是後處理器))檢測到幾種bean,而普通的DefaultListableBeanFactory則與任何特殊bean無關。

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