原理:一般指的是代碼的實現 以及涉及到的技術
一:以xml的形式來說
1.主要涉及到xml解析技術 以及 反射機制 還有實例存儲的數據結構(容器)
1)xml解析技術: 包括dom4j 以及jdom支持xpath… 功能更強大 推薦使用
2)反射機制: 首先得獲得類的全路徑,該類還得有構造器,如果你要是寫了有參構造器的話,必須得顯示寫無參構造器。默認情況下,該類是自帶無參構造器。
然後Class.forName(“xxx.xxx.xxx.User”).newInstance 會構建一個實例 利用無參構造器。
3)容器:
特點:根據唯一標示 獲取該實例
容器底層的數據結構:採用Map集合構架承載實例與ID的角色
二 :以註解的形式來說
利用反射機制獲得註解 進行對該類實例化
IOC 和 D I
IOC:控制反轉 控制指的是什麼?反轉指的是什麼?
控制:指的是構建實例的控制權 以前構建實例的權利由我們開發人員來new出來,包括依賴關係的注入,此時實例的控制權爲引用類,現在有了spring,構建實例的權利交給spring來接管,反轉控制就是反轉了對象的創建方式,從我們自己創建反轉給力程序(spring)
反轉:控制權由其他類(引用類)轉爲spring了
D I:依賴注入 依賴是什麼?注入又是什麼?
依賴:指的是A類 要引用B類 那麼我們就說A依賴B
注入:A既然依賴B 那麼B類何時初始化呢?爲了解決耦合度的問題 不能再A類中new B了 需要靠spring將B類的實例傳遞給A,該過程叫做注入
耦合度問題:A依賴B才能完成特定的功能。該B爲一個藉口,實現類有多個Bimpl_1 Bimpl_2各有不同。
以前A可以直接使用Bimpl_1 那麼我們就說A與Bimpl_1 緊密聯繫在一起了,耦合度很高
現在A依賴Bimpl_1 的接口即B,B接口的實現由Spring來控制注入Bimpl_1 或者Bimpl_2.即A與Bimpl_1 實現瞭解耦(多態 多種形式表現)
IOC 和 DI的區別:
先有了IOC 將實例構建出來 才能DI,就是去ioc容器裏即concurrentHashMap里根據依賴類的 Bean 以 beanName 爲鍵去保存實例的HashMap 中去取該類的實例化對象,注入
(原來就這些,spring的代碼實現,看看下面的就好了,不用去費心記,你也記不住,沒必要,不過還是寫下,方便理解)
IOC容器的設計與實現
1,IoC容器設計中,兩個主要的接口
1)BeanFactory:可以理解爲IoC容器的抽象,提供了ioc容器的最基本API。
2)ApplicationContext:IoC容器的高級形態,在基礎IoC容器上加了許多特性。
從繼承圖來理解:
DefaultListableBeanFactory是IoC容器的最基礎實現,是一個最基礎最簡單的IoC容器對象,其高級容器ApplicationContext也是通過持有DefaultListableBeanFactory引用,在基礎IoC容器之上進行特性增強。
容器設計原理
XmlBeanFactory這個IoC容器便是在DefaultListableBeanFactory基礎容器之上,添加了如xml讀取的附加功能。
編程式使用IoC容器示例
ClassPathResource res = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
大致過程如下幾步:
1)讀取bean.xml配置文件資源
2)創建基礎BeanFactory容器(DefaultListableBeanFactory)
3)創建資源讀取器(XmlBeanDefinitionReader),並將其與BeanFactory容器對象進行關聯
4)將bean.xml配置文件中的信息解析成BeanDefinition元數據信息對象,並註冊到BeanFactory(IoC容器)中
ApplicationContext容器特點
ApplicationContext通過對不同特性接口的實現爲基礎IoC容器添加了許多特性
1)MessageSource接口:支持不同的信息源。
2)ResourceLoader和Resource接口:支持從不同的途徑獲取資源信息。
3)ApplicationEventPublisher接口:支持應用事件。
4)ApplicationContext接口:在基本IoC容器基礎上增加附加服務。
IoC容器的初始化過程
IoC容器啓動的三個基本過程
1)Resource資源信息定位
2)BeanDefinition的載入(將資源中的信息初始化爲BeanDefinition對象)
3)向IoC容器註冊生成的BeanDefinition對象
下面我們通過FileSystemXmlApplicationContext爲例,進行IoC容器初始化過程的解析
FileSystemXmlApplicationContext是一個支持xml定義BeanDefinition的ApplicationContext,我們看一下
FileSystemXmlApplicationContext的具體實現
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
// 調用refresh函數載入和註冊BeanDefinition對象,完成IoC容器的初始化過程
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
// 構造一個FileSystemResource來獲取資源文件,此方法是在BeanDefinitionReader的loadBeanDefintion中被調用
// loadBeanDefintion方法是一個抽象,具體的資源定位的實現由具體的子類來完成
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
這裏的refresh方法是IoC容器初始化的唯一入口
初始化調用過程大致如下:IoC容器執行refresh的過程
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 初始化準備工作
prepareRefresh();
// 獲取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// BeanFactory的預準備工作
prepareBeanFactory(beanFactory);
try {
// beanFactory準備工作完成之後進行的後置處理工作。
postProcessBeanFactory(beanFactory);
// 調用BeanFactoryPostProcessor各個實現類的postProcessFactory(factory)方法。
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊BeanPostProcessor的實現類
registerBeanPostProcessors(beanFactory);
// 初始化當前ApplicationContext的MessageSource
initMessageSource();
//初始化ApplicationContext事件廣播器
initApplicationEventMulticaster();
// 鉤子方法,在這裏可以初始化一些特殊的bean
onRefresh();
// 註冊事件監聽器
registerListeners();
// 初始化所有的singleton beans
finishBeanFactoryInitialization(beanFactory);
//廣播初始化完成
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();
}
}
}
其中最主要的BeanDefintion對象的載入和註冊過程是在
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()中refreshBeanFactory方法中完成
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 創建ioc容器
// new DefaultListableBeanFactory(getInternalParentBeanFactory())
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 啓動BeanDefinition的載入和註冊過程
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
這裏的loadBeanDefinitions方法中會創建XmlBeanDefinitionReader對象,並啓動BeanDefinitionReader的getResourceLoader方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 以Resource的方式獲取配置文件的資源位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 以String的方式獲取配置文件的資源位置
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
BeanDefinitionReader抽象中會完成配置文件的資源的加載
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 調用具體實現容器的getResourceByPath方法獲取Resource handle對象並根據具體實現加載配置資源
Resource resource = resourceLoader.getResource(location);
// 加載和註冊BeanDefinition元信息對象,具體由XmlBeanDefinitionReader實現
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
XmlBeanDefinitionReader中loadBeanDefinitions的具體實現
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 準備IO流來獲取配置資源中的數據
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
// 真正開始從指定的資源文件中獲取信息,加載並註冊BeanDefinition到IoC中
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 通過DefaultDocumentLoader對象獲取xml配置文件的Document對象
Document doc = doLoadDocument(inputSource, resource);
// 將獲取的Document對象解析成BeanDefinition對象,並註冊到IoC容器中
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
將獲取的Document對象解析成BeanDefinition對象的具體實現由BeanDefinitionParserDelegate解析器具體實現完成
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將Document對象解析成BeanDefinition對象的具體方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//將解析好的BeanDefinition註冊到DefaultListableBeanFactory容器對象中
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
具體的Document解析成BeanDefinition的過程是通過不斷的遞歸調用完成,具體實現這裏不做分析
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 獲取定義的<bean>中設置的bean的name,設置到BeanDefinition對象中去
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 創建BeanDefinition對象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 對bean元素的屬性進行解析
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析<bean>的構造函數設置
parseConstructorArgElements(ele, bd);
// 解析<bean>的property設置
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
而註冊則是將BeanDefinition對象放入到DefaultListableBeanFactory容器中維護的一個map中
this.beanDefinitionMap.put(beanName, beanDefinition);
至此,IoC容器的初始化過程就完成了。
總結:IoC容器的初始化過程就是將xml配置資源的信息抽象到BeanDefinition信息對象中,再將BeanDefinition設置到基本容器的map中,BeanDefinition中的信息是容器建立依賴反轉的基礎,IoC容器的作用就是對這些信息進行處理和維護。
IoC容器的依賴注入
依賴注入是用戶第一次向IoC容器索要Bean時觸發的,入口爲DefaultListableBeanFactory基類AbstractBeanFactory中的getBean方法
具體依賴注入過程
總結
Spring IOC容器主要有繼承體系底層的BeanFactory、高層的ApplicationContext和WebApplicationContext
Bean有自己的生命週期
容器啓動原理:Spring應用的IOC容器通過tomcat的Servlet或Listener監聽啓動加載;Spring MVC的容器由DispatchServlet作爲入口加載;Spring容器是Spring MVC容器的父容器
容器加載Bean原理:
BeanDefinitionReader讀取Resource所指向的配置文件資源,然後解析配置文件。配置文件中每一個解析成一個BeanDefinition對象,並保存到BeanDefinitionRegistry中;
容器掃描BeanDefinitionRegistry中的BeanDefinition;調用InstantiationStrategy進行Bean實例化的工作;使用BeanWrapper完成Bean屬性的設置工作;
單例Bean緩存池:Spring 在 DefaultSingletonBeanRegistry 類中提供了一個用於緩存單實例 Bean 的緩存器,它是一個用 HashMap 實現的緩存器,單實例的 Bean 以 beanName 爲鍵保存在這個HashMap 中。
spring的依賴注入原理
我們可以看出,對屬性的注入過程分以下兩種情況:
1)、屬性值類型不需要強制轉換時,不需要解析屬性值,直接準備進行依賴注入。
2)、屬性值需要進行類型強制轉換時,如對其他對象的引用等,首先需要解析屬性值,然後對解析後的屬性值進行依賴注入。
對屬性值的解析是在 BeanDefinitionValueResolver 類中的 resolveValueIfNecessary()方法中進行的,對屬性值的依賴注入是通過 bw.setPropertyValues()方法實現的,在分析屬性值的依賴注入之前,我們先分析一下對屬性值的解析過程
解析屬性注入規則
當容器在對屬性進行依賴注入時,如果發現屬性值需要進行類型轉換,如屬性值是容器中另一個 Bean實例對象的引用,則容器首先需要根據屬性值解析出所引用的對象,然後才能將該引用對象注入到目標實例對象的屬性上去,對屬性進行解析的由 resolveValueIfNecessary()方法實現。
Spring IOC 容器是如何將屬性的值注入到 Bean 實例對象中去的:
1)、對於集合類型的屬性,將其屬性值解析爲目標類型的集合後直接賦值給屬性。
2)、對於非集合類型的屬性,大量使用了 JDK 的反射機制,通過屬性的 getter()方法獲取指定屬性注入以前的值,同時調用屬性的 setter()方法爲屬性設置注入後的值。看到這裏相信很多人都明白了 Spring的 setter()注入原理
依賴注入流程補充(這個比較細)
1、getBean第一次調用lazy-init的bean
是以BeanFactory的getBean方法爲入口觸發的(實現在AbstractBeanFactory實現類中)。如果是單例會緩存起來只加載一次,如果是FactoryBean這種特殊的bean會把這個bean的實例傳入getObjectForBeanInstance方法獲得FactoryBean產生的bean(調用FactoryBean的getObject方法,這就是自定義的FactoryBean要重寫的方法,AOP也是這個原理,詳情見下方AOP分析)。在第一次載入時要先判斷這個BeanDefinition在當前BeanFactory有沒有,沒有就從雙親BeanFactory中找,一直遞歸。
找到後要驗證是否存在遞歸依賴,有則報錯無則設置當前bean依賴bean的依賴關係到兩個map中(一個是被依賴map,一個是依賴map),其中:
(1)如果是單例第一次載入就調用getSingleton方法(方法中回調了參數中ObjectFactory的getObject方法,這裏重寫了這個方法調用createBean)獲得實例用getObjectForBeanInstance獲得FactoryBean產生的bean(如果它是FactoryBean的話)。
(2)如果是prototype加載調用createBean後調用getObjectForBeanInstance。
(3)如果是其他scope類型:request、session和global session,這三種就用scope.get獲取實例(和getSingleton類似回調重寫的getObject也就是調用createBean)後調用getObjectForBeanInstance。
最後如果getBean指定了requiredType要檢驗獲取的bean能不能轉化成指定的類型不能的話就報錯。
createBean方法就是生成bean的方法並對一些比如init-method屬性、後置處理器等一些初始化進行了處理。方法中在實例化之前判斷是否有post-processor,如果有這樣的processor則短路指定bean的創建,直接返回一個proxy而不是指定的bean(這種processor可以指定生成一個其他類型的對象)沒有的話用doCreateBean創建bean返回。
doCreateBean是用createBeanInstance生成BeanWrapper(包裝bean)之後用populateBean向其中的bean完成依賴bean的注入(autowire等)。
createBeanInstance創建beanWrapper時分三類進行處理:
(1)如果有工廠方法,調用instantiateUsingFactoryMethod創建。
(2)如果是構造器注入的方式調用autowireConstructor。
(3)簡單方式調用instantiateBean。調用的是策略類(默認SimpleInstantiationStrategy)的instantiate而其中又是通過bean方法是否有跟IOC容器同名的(會被覆蓋)來分兩類處理(沒同名方法的從BeanDefinition中拿出class直接用jdk的反射拿構造器來newinstance一個實例,如果有同名的則是用CGLIB的方式來new一個實例)。
populateBean爲生成的bean依賴注入,先對非簡單類型屬性有autowire的進行處理,判斷這個屬性在之前解析載入beanDefinition時property裏有沒有,有的話進行getBean初始化後放入PropertyValue集合中(這個就是propertyname和value的封裝),然後更新依賴map,再對非autowire的或一般屬性進行注入,有要轉化的要經過valueResolver的轉化(如果是RuntimeBeanReference之前載入時XML中配置是ref的就getBean(如果在雙親BeanFactory中就從雙親中取)獲得後也放到PropertyValue集合中,也要更新依賴map)。最後再注入到bean中,這裏說的注入其實真實發生在最後的BeanWraper的setPropertyValue(propertyValue集合)方法,具體實現就是通過反射的方式獲得setter方法賦值。
2、lazy-init==false初始化(只對singleton,也是默認方式)
在refresh方法中的finishBeanFactoryInitialization方法中進行初始化(實際也是調用getBean方法)。
IoC(控制反轉)思想(其實ioc是個思想)
控制反轉IoC(Inversion of Control)是說創建對象的控制權進行轉移,以前創建對象的主動權和創建時機是由自己把控的,而現在這種權力轉移到第三方,比如轉移交給了IoC容器,它就是一個專門用來創建對象的工廠,你要什麼對象,它就給你什麼對象,有了 IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,通過IoC容器來建立它們之間的關係
spring的ioc就是spring對ioc原理的實現,就是創建對象和索取對象都交給了ioc容器。就是通過bean初始化入後refresh()方法入口,進行一系列的處理,最終通過beanName這一屬性,反射實例化bean對象,在放到concurrentHashMap中緩存起來,這裏可以看出spring的ioc容器就是個map-存儲bean信息的map,用的時候,通過beanName去獲取bean,這裏需要注意,通過beanName獲取bean的時候,是已經初始化的,沒初始化的,還要走一邊bean的初始化流程