title: Spring————程序員的春天
tags: Spring
categories: Spring
若圖片無法顯示,請前往我的博客查看,相應(yīng)文章鏈接:http://codingxiaxw.cn/2016/11/11/39-Spring/
當客戶端向服務(wù)器發(fā)出請求時,服務(wù)器把得到的請求發(fā)送給控制器Servlet,而在Servlet中需要創(chuàng)建Service對象來調(diào)用業(yè)務(wù)層相關(guān)功能(故說控制器層Servlet依賴于業(yè)務(wù)層Service),而在Service中又需要創(chuàng)建數(shù)據(jù)庫層DAO對象來對數(shù)據(jù)庫進行操作(故說業(yè)務(wù)層Service依賴于數(shù)據(jù)庫層DAO)。
思考:針對上述過程,我們需要考慮這樣幾個問題。1.Servlet、Service以及Dao對象的創(chuàng)建時間、創(chuàng)建數(shù)量。2.Servlet、Service以及Dao之間的依賴關(guān)系。如何處理這些問題呢?Spring就是用來處理對象的創(chuàng)建、以及對象之間依賴關(guān)系的一個開發(fā)框架。它打破了我們傳統(tǒng)開發(fā)的觀念,我們不再需要像以前那樣在具體的類中創(chuàng)建具體的對象,而是將對象的創(chuàng)建交給它去完成。它是我們所要學習的框架中最重要的框架,請務(wù)必好好學習。
1.Spring框架中的專業(yè)術(shù)語
1.1組件/框架設(shè)計
- 侵入式設(shè)計:對現(xiàn)有類的結(jié)構(gòu)有影響,即需要實現(xiàn)或繼承某些特定類。 如Struts框架。
- 非侵入式設(shè)計:引入了框架,對現(xiàn)有的類結(jié)構(gòu)沒有影響。如Spring框架/Hibernate框架。
1.2控制反轉(zhuǎn)
Inversion On Control,簡稱IOC。對象的創(chuàng)建交給外部容器自動完成,這個就叫做控制反轉(zhuǎn)。(有控制反轉(zhuǎn)就有控制正轉(zhuǎn),控制正轉(zhuǎn):對象的創(chuàng)建由我們自己創(chuàng)建)
依賴注入dependency injection,簡稱DI,用于處理對象間的依賴關(guān)系。
二者區(qū)別:控制反轉(zhuǎn)(IOC):解決對象創(chuàng)建的問題,(對象的創(chuàng)建交給別人)。依賴注入(DI):在創(chuàng)建完對象后,對象關(guān)系的處理就是依賴注入,(通過set方法依賴注入。)
1.3AOP
面向切面編程。切面,簡單來說可以理解為一個類,由很多重復(fù)代碼形成的類。切面舉例:事務(wù)、日志、權(quán)限。 關(guān)于AOP的詳細講解我推薦你們看這篇博客:Spring AOP實現(xiàn)原理與應(yīng)用 ,往后我會單獨寫篇博客為你們詳細介紹AOP。
2.Spring框架概述
Spring框架,可以解決對象創(chuàng)建以及對象之間依賴關(guān)系的一種框架。且可以和其它框架一起使用,例如spring與struts、spring和hibernate。(起到整合/粘合作用的一個框架)。
spring提供了一站式解決方案:
1)SpringCore:是Spring的核心功能:IOC容器,解決對象創(chuàng)建及依賴關(guān)系。
2)SpringWeb:Spring對web模塊的支持。
可以與struts整合,讓struts的action創(chuàng)建交給spring。
Spring mvc模式,用springmvc整合了就不用struts了。
3)Spring DAO:是Spring對Jdbc操作的支持。(Jdbc Template模塊工具類)
4)Spring ORM:是Spring對ORM的支持。
既可以與hibernate整合(使用原始的session)
也可以使用Spring對Hibernate操作的封裝(對上面的session又進行了一層封裝)
5)Spring AOP:關(guān)于AOP的詳細講解我推薦你們看這篇博客:Spring AOP實現(xiàn)原理與應(yīng)用 ,往后我會單獨寫篇博客為你們詳細介紹AOP。
6)SpringEE:Spring對javaEE其它模塊的支持
3.Spring開發(fā)步驟
1)導(dǎo)入jar包:
[圖片上傳失敗...(image-b4a912-1526379695715)]
寫在前面的話:當你運行程序出現(xiàn)org.springframework.beans.factory.BeanDefinitionStoreException的報錯信息時,不要想了,出現(xiàn)這種報錯的信息原因絕對是因為jdk版本和你導(dǎo)入的spring jar包不兼容的問題。由于spring3.x與jdk1.7兼容,而spring4.x與jdk1.8兼容,所以這里提供兩種解決方案:
- 1.將jdk版本調(diào)為1.7,我用的開發(fā)工具為IDEA,它默認下的JDK使用1.8版本,所以我需要在三個地方將jdk的版本改過來(前提是你已經(jīng)下載了jdk1.7版本),修改IDEA配置中Project的jdk版本、Modules的jdk版本、SDKs的版本,如果你用到leTomcat還需要修改Tomcat配置的jdk版本。這樣jdk1.7與spring3.x才兼容。
- 2.將spring3.x.jar換成spring4.x.jar包。這種方式比較繁瑣,建議大家使用第一種方式。spring4.x與jdk1.8才兼容。
2)配置核心文件applicationContext.xml(文件名稱隨意):
[圖片上傳失敗...(image-7a1797-1526379695715)]
代碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
3)使用:
首先我們創(chuàng)建一個pojo對象User.java:
[圖片上傳失敗...(image-2f351-1526379695715)]
然后創(chuàng)建測試類使用這個User對象,以前我們要使用User對象時直接像這樣User user=new User();new一個對象即可:[圖片上傳失敗...(image-5b08e6-1526379695715)]
而當我們使用Spring后就應(yīng)該這樣使用User對象,首先在applicationContext.xml中添加<bean>標簽,一個<bean>標簽代表一個pojo對象:
[圖片上傳失敗...(image-46d4bd-1526379695715)]
其中各個屬性的說明見注釋。然后我們通過如下步驟獲取該pojo對象:
[圖片上傳失敗...(image-ddfc39-1526379695715)]
運行測試類:[圖片上傳失敗...(image-fdb87e-1526379695715)]
說明成功獲取到User對象。上述是通過工廠類獲取的IOC容器創(chuàng)建的User對象,下面我們看看使用Spring框架獲取pojo對象的第二種方式直接得到IOC容器的對象:
[圖片上傳失敗...(image-535e45-1526379695715)]
運行程序,成功打印出user信息,說明我們通過IOC容器成功獲取到user對象。
4.<bean>創(chuàng)建的細節(jié)
對上述代碼進行改進,對于IOC容器對象,我們只需創(chuàng)建一次即可,所以將創(chuàng)建IOC對象的代碼改為成員變量。
[圖片上傳失敗...(image-3f4ace-1526379695716)]
代碼中我們通過IOC對象創(chuàng)建了兩個User對象,運行測試類得到打印結(jié)果:[圖片上傳失敗...(image-fdbf2a-1526379695716)]
發(fā)現(xiàn)答應(yīng)的這兩個User對象id都一樣,說明我們獲取到的是同一個對象,也說明通過<bean>標簽設(shè)置的pojo對象是單例的。為什么呢?其實<bean>標簽?zāi)J有一個scope="singleton"的屬性,代表該<bean標簽對象的pojo對象是單例的。我們可以將該屬性值改為scope="prototype",如下:[圖片上傳失敗...(image-1248f9-1526379695716)]
然后再運行測試類,輸出如下內(nèi)容:
[圖片上傳失敗...(image-ca28d3-1526379695716)]
說明此時獲取的兩個User不再是同一個對象。
那么由IOC容器管理的pojo對象應(yīng)該在何時創(chuàng)建呢?我們來看看,首先在User.java中添加一個無參構(gòu)造器:[圖片上傳失敗...(image-de5439-1526379695716)]
然后修改測試類:[圖片上傳失敗...(image-414035-1526379695716)]
運行測試類:[圖片上傳失敗...(image-83cb7c-1526379695716)]
從控制臺中輸出內(nèi)容我們可以得知:當程序運行時,IOC容器首先創(chuàng)建,然后當我們需要得到IOC容器中的pojo對象時我們通過語句ac.getBean("user");得到,此時就會在IOC中創(chuàng)建由它管理的pojo對象。當我們刪除ac.getBean("user");語句時,再次運行程序,得到如下內(nèi)容:[圖片上傳失敗...(image-fa6230-1526379695716)]
這說明什么呢?說明IOC容器中沒有創(chuàng)建pojo對象(因為一旦創(chuàng)建就會有"---User對象創(chuàng)建---"的語句輸出)。綜上情況,即只有當我們用到pojo對象時,IOC容器才會在自己內(nèi)部創(chuàng)建它。此種情況為<bean>標簽的屬性為scope="prototype"的結(jié)果,那么我們再來看看當屬性為scope="singleton"時的輸出結(jié)果為:[圖片上傳失敗...(image-d82575-1526379695716)]
打印臺的內(nèi)容說明該User對象在程序啟動時就創(chuàng)建在IOC容器中了,不信我們把通過IOC容器得到User對象的代碼注釋掉再看輸出結(jié)果:[圖片上傳失敗...(image-10be2e-1526379695716)]
發(fā)現(xiàn)此時即使我們不通過ac.getBean("user");語句得到User對象,它也在程序啟動時就創(chuàng)建了。
總結(jié):在<bean>標簽中設(shè)置bean對象為單例時,該對象在系統(tǒng)啟動時就會創(chuàng)建;設(shè)置為多例時,該對象在我們需要使用時才創(chuàng)建。
4.1<bean>標簽中的其它屬性說明
- 1.
lazy-init:延遲初始化bean對象,默認值為false,即不延遲創(chuàng)建bean對象,在程序啟動時就在IOC中創(chuàng)建bean對象;若其值為true則延遲創(chuàng)建bean對象,即在我們需要對象時才在IOC容器中創(chuàng)建該對象。此屬性只對單例bean對象有效。 - 2.
init-method:可以給該屬性傳遞一個在pojo對象中創(chuàng)建的方法例如A方法的方法名A作為init-method的屬性值,表示當該pojo對象在IOC容器中被創(chuàng)建后就立刻執(zhí)行這個A方法。 - 3.
destoy-method:同上,給該屬性傳遞一個在pojo對象中創(chuàng)建的方法例如B方法的方法名B作為destoy-method的屬性值,表示當IOC容器被銷毀時(該pojo對象也會在IOC中銷毀)會立刻調(diào)用這個B方法。當然我們通過ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");創(chuàng)建的IOC對象ac是沒有destoy()方法的,我們需要這個創(chuàng)建IOC對象ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");這樣創(chuàng)建出來的IOC對象才有destoy()方法。
5.Spring IOC容器
Spring IOC容器,是Spring的核心內(nèi)容,用于創(chuàng)建對象和處理對象間的依賴關(guān)系。
5.1對象的創(chuàng)建
利用IOC容器創(chuàng)建對象的方式有如下幾種:1.調(diào)用無參數(shù)構(gòu)造器。2.調(diào)用帶參數(shù)構(gòu)造器。3.工廠創(chuàng)建對象。包括工廠類的靜態(tài)方法創(chuàng)建對象和工廠類的非靜態(tài)方法創(chuàng)建對象4.反射。(IOC的原理就是通過反射來創(chuàng)建對象)
5.1.1調(diào)用無參數(shù)構(gòu)造器
在配置文件中加入如下內(nèi)容:[圖片上傳失敗...(image-8b27a1-1526379695716)]
5.1.2調(diào)用帶參數(shù)構(gòu)造器
[圖片上傳失敗...(image-eae437-1526379695716)]
<constructor-arg>標簽中還有一個ref的屬性,屬性值代表引用配置文件(即IOC容器)中的相應(yīng)對象。
故還可以采用這種方法調(diào)用帶參數(shù)構(gòu)造器創(chuàng)建對象:[圖片上傳失敗...(image-9f1284-1526379695716)]
5.1.3工廠創(chuàng)建對象
首先創(chuàng)建一個工廠類: [圖片上傳失敗...(image-87b97a-1526379695716)]
調(diào)用工廠的實例方法創(chuàng)建對象:[圖片上傳失敗...(image-bc49d8-1526379695716)]
調(diào)用工廠靜態(tài)方法創(chuàng)建對象:[圖片上傳失敗...(image-4aa7cd-1526379695716)]
5.2處理對象的依賴關(guān)系
在IOC容器的配置文件中我們有如下給對象注入屬性的方法:1.通過構(gòu)造方法。2.通過set方法給屬性注入值。3.p名稱空間。4.自動裝配。5.注解。
5.2.1通過構(gòu)造方法
首先我們來看看如何通過構(gòu)造方法來給對象的屬性賦值,在配置文件中添加如下標簽即可通過構(gòu)造器給該User對象的屬性賦值:
[圖片上傳失敗...(image-b68d3c-1526379695716)]
5.2.2通過set方法
通過set方法給屬性賦值,前提是在User對象中給它的屬性添加了set方法:
[圖片上傳失敗...(image-e26906-1526379695716)]
接下來我們看個案例,以前我們開發(fā)時根據(jù)MVC模式都會像下面這樣建立相應(yīng)的Service.java、Servlet.java和dao.java:[圖片上傳失敗...(image-eee4f4-1526379695716)]
[圖片上傳失敗...(image-8c08a3-1526379695716)]
[圖片上傳失敗...(image-1f7faa-1526379695716)]
都需要我們自己在.java文件中添加A a=new A();來創(chuàng)建其所需要的依賴對象,而現(xiàn)在我們就將對象的創(chuàng)建交給IOC了,選擇set給屬性賦值的方式來給它們注入其所需依賴對象,修改它們的代碼:
[圖片上傳失敗...(image-38602c-1526379695716)]
[圖片上傳失敗...(image-92bdb4-1526379695716)]
然后我們需要在application.xml中進行配置:[圖片上傳失敗...(image-4752a3-1526379695716)]
ref的屬性值代表給該對象注入它所依賴的對象,即我們上述講到的依賴注入(dependency injection),通過上述步驟我們便完成了將對象的創(chuàng)建交給IOC的操作。
上述三個對象的創(chuàng)建我們需要寫三個<bean>標簽才能完成,接下來我將介紹第二種方法通過內(nèi)部bean的操作一次性完成它們的創(chuàng)建以及它們之間的依賴關(guān)系,修改配置文件中的內(nèi)容:
[圖片上傳失敗...(image-bf581-1526379695716)]
通過上述內(nèi)部<bean>標簽的方式我們便可實現(xiàn)和set注入依賴相同的效果。我們來看看它們兩者的相同和區(qū)別:
- 相同:都可以創(chuàng)建Service對象,并處理了之間的依賴關(guān)系。
- 區(qū)別:set注入創(chuàng)建的Service對象可以給另一個Servlet對象調(diào)用,而內(nèi)部bean將Service對象寫在Servlet內(nèi)部導(dǎo)致該Service對象只能被該Servlet使用,所以內(nèi)部bean標簽的使用場景在只需要一個Servlet對象的項目中。
5.2.3通過p名稱空間給對象的屬性注入值
此中方法只有在Spring3.0版本及以上版本才能用。首先在配置文件的<beans>根標簽中加入屬性:xmlns:p="http://www.springframework.org/schema/p"。然后我們便可以在配置文件中這樣給對象的屬性賦值:[圖片上傳失敗...(image-7b6a48-1526379695716)]
當我們在配置文件中輸入p:時,會出現(xiàn)兩個屬性1.p:userDao 和p:userDao-ref,這里有必要說明一下二者區(qū)別:
- p:userDao:代表直接給UserService對象的userDao屬性賦值
- p:userDao-ref:代表引用的userDao對象
例如使用p名稱空間給傳統(tǒng)的對象屬性賦值時我們這樣寫:
<bean id="user" class="pojo.User" p:id="xxx"/>
<bean id="user" class="pojo.User" p:name="xxx"/>
5.2.4.自動裝配
當我們在配置文件中用<bean>標簽指明相應(yīng)對象的同時就將這個對象放入到了IOC容器中(其中標簽中的id屬性唯一指示一個對象),當我們給該bean標簽添加了autowrite="byName"的屬性后,對于該標簽對應(yīng)的對象注入的屬性,會去IOC容器中自動查找與屬性同名的對象。
例如如下代碼:[圖片上傳失敗...(image-9a7503-1526379695716)]
通過上述三個<bean>標簽我們就將userDao、userService、userServlet三個對象添加到了IOC容器中。我們在UserService對象的bean標簽中加上了autowrite="byName"的屬性,這樣我們查看UserService.java的代碼,它有一個UserDao對象名為userDao的屬性,此時就會自動去IOC容器中尋找與userDao同名的對象(即在bean標簽中尋找id為userDao的對象),然后進行注入,此時我們?nèi)魧?code><bean id="userDao" class="pojo.UserDao">的id="userDao"屬性值改為userDao1或者其它名字,則運行系統(tǒng)會出現(xiàn)空指針異常,道理上述已分析。UserServlet注入userService的屬性道理同此。
我們也可以將該屬性定義到全局<beans>標簽中,設(shè)置default-autowrite="byName"的屬性,這樣就不用每個bean標簽中都寫上autowrite="byName"屬性了。
上述是根據(jù)名稱自動裝配,其實autowrite的屬性值還可以為byType即根據(jù)類型自動裝配。對于<bean id="userService" class="pojo.UserService">,當添加了autowrity="byType"的屬性后,此時尋找它依賴的屬性userDao的過程如下:查看UserService.java代碼,它需要注入的屬性類型為UserDao類型,所以就會自動去IOC容器中查找UserDao類型的對象并自動為UserService對象注入該屬性,此時各bean標簽的id屬性值便可以隨便寫了如果根據(jù)類型自動裝配,則要保證保證該類型的對象只有一個,否則會報錯。該屬性同樣可以在全局beans標簽中進行配置。
利用自動裝配的優(yōu)缺點:簡化了配置,但不利用系統(tǒng)維護。所以一般不推薦此中用法,下面我們再來介紹第5中非常簡單的配置。
5.2.5注解
注解方式可以簡化Spring的IOC容器的配置。
使用步驟:
- 1.先引入context名稱空間
- 2.開啟注解掃描
- 3.使用注解:通過注解的方式,把對象加入到IOC容器中。
首先在IOC配置文件中引入context名稱空間,即在<beans>全局標簽中添加xmlns:context="http://www.springframework.org/schema/context"屬性。
然后在配置文件中添加如下標簽[圖片上傳失敗...(image-e5ff0a-1526379695716)]
base-package:表示該掃描器只掃描此包下所有類。
最后我們便可以使用注解了,在pojo對象的.java文件中分別加入如下注解:[圖片上傳失敗...(image-edf2eb-1526379695716)]
[圖片上傳失敗...(image-2f61c0-1526379695716)]
[圖片上傳失敗...(image-9bcaa9-1526379695716)]
@Componet注解:代表將該對象放入到IOC容器中,括號里面的名字代表該對象在IOC容器中的唯一標識名字,名字任意取。該注解寫在代碼第一行。
@Resource注解:用于將該對象依賴的屬性從IOC容器中找到并注入,括號里面的name屬性值必須跟@Compenent注解里填入的名字相同。
通過注解方法便可去掉各.java文件中為屬性創(chuàng)建設(shè)置的set方法。
繼續(xù)對上述注解方式進行配置優(yōu)化,去掉括號中的內(nèi)容:[圖片上傳失敗...(image-ab3901-1526379695716)]
[圖片上傳失敗...(image-c06366-1526379695716)]
[圖片上傳失敗...(image-ab8faa-1526379695716)]
在測試類中運行依然可以正常運行。
說明:利用@Compenent注解的方式是通用的將對象加入到IOC容器中的方式,而有時候我們需要區(qū)別各層對象添加的方式,所以這里我們將Dao層對象添加到IOC容器的注解方式改為:@Repository表示持久層的組件;修改Service層對象添加到IOC容器的注解方式:@Service表示業(yè)務(wù)邏輯層的組件;修改Servlet層對象添加到IOC容器的注解方式為:@Controller表示控制層的組件。
另外需要說明的是使用注解的方式將對象添加到IOC容器中和在xml文件中添加配置的方式是可以共存的。但通過@Resource不帶括號的注解,必須要保證該類型只有一個變量,所以一般情況下我們還是優(yōu)先使用@Resource(name="")注解。
到此,Spring框架的學習我們已完成。
2018.3.19更
歡迎加入我的Java交流1群:659957958。群里目前已有1800人,每天都非?;钴S,但為了篩選掉那些不懷好意的朋友進來搞破壞,所以目前入群方式已改成了付費方式,你只需要支付9塊錢,即可獲取到群文件中的所有干貨以及群里面各位前輩們的疑惑解答;為了鼓勵良好風氣的發(fā)展,讓每個新人提出的問題都得到解決,所以我將得到的入群收費收入都以紅包的形式發(fā)放到那些主動給新手們解決疑惑的朋友手中。在這里,我們除了談技術(shù),還談生活、談理想;在這里,我們?yōu)槟愕膶W習方向指明方向,為你以后的求職道路提供指路明燈;在這里,我們把所有好用的干貨都與你分享。還在等什么,快加入我們吧!
2018.4.21更:如果群1已滿或者無法加入,請加Java學習交流2群:305335626 。群2作為群1的附屬群,除了日常的技術(shù)交流、資料分享、學習方向指明外,還會在每年互聯(lián)網(wǎng)的秋春招時節(jié)在群內(nèi)發(fā)布大量的互聯(lián)網(wǎng)內(nèi)推方式,話不多說,快上車吧!
6.聯(lián)系
If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.