1.Spring簡介
Spring是J2EE開發(fā)中一個很重要的框架。它主要用來解決下面兩個問題。
-
解決大型軟件開發(fā)中,對象之間由于復(fù)雜的依賴關(guān)系導(dǎo)致的牽一發(fā)而動全身的強耦合問題.使用IoC思想解決。 -
使用面向切面編程, 解決控制事務(wù)的繁瑣操作.使用AOP解決。
根據(jù)Spring解決的問題,和其對應(yīng)的專業(yè)術(shù)語。我們也說Spring是一個輕量級的DI/IoC和AOP容器的開源框架, 其提倡以最小侵入式管理應(yīng)用中的代碼,意味著我們可以隨時卸載和安裝Spring.
接下去我們需要理解下Spring以下的專業(yè)術(shù)語,我就不抄寫網(wǎng)絡(luò)上的術(shù)語內(nèi)容,并不是那么通俗,以我的理解如下。
1.2 Spring術(shù)語
-
應(yīng)用程序(Application).- 對于JavaWeb來將,應(yīng)用程序就是一個Web App.例如OA系統(tǒng)等.
-
框架:- 框架是一種抽象, 其抽取了開發(fā)中可以重用和常見的功能的集合。
-
非侵入式設(shè)計:- 非侵入式是編程中常見的一種設(shè)計方式,它保障了別人用你的框架,即使到時候不用了,也不會應(yīng)用刪除你的框架而要更改大量的代碼, 可以不用的時候就直接刪除,不會帶來依賴問題。
- 從代碼的角度來說.非侵入式,保證了不用繼承框架的類.也就不會和依賴于框架的類.是一種解耦的設(shè)計、
-
輕量級和重量級- 所謂的輕量級是相對于重量級來說。一般是由以下幾個特點.
非侵入式、資源占用少、部署簡單、易用.所以所謂的輕是在有重的前提下的對比。
- 所謂的輕量級是相對于重量級來說。一般是由以下幾個特點.
-
POJO:-
Plain Ordinary Java Object / Pure Old Java Object.所謂的POJO是不繼承Java的任何類,不實現(xiàn)任何的接口,但可以包含業(yè)務(wù)邏輯和持久化邏輯
-
-
容器:- 容器主要用來
存放對象.管理對象的整個生命周期.比如Tomcat是Servlet/JSP容器.
- 容器主要用來
1.3 Spring的框架構(gòu)架
之所以要了解Spring構(gòu)架是在學(xué)習(xí)之前我們要對一樣事務(wù)大體的骨架有一個了解。在介紹Spring的構(gòu)架之前,我們簡要的說下Spring的優(yōu)勢。
Spring的優(yōu)勢
- 低侵入、低耦合
- 聲明式事務(wù)管理
- 方便集成其他框架
- Spring框架包含JavaEE 三層的每一層的解決方案(一站式)
構(gòu)架
從上之下。主要分為
-
數(shù)據(jù)訪問層(Data Access)。 -
Web層。 -
AOP模塊。 -
核心容器層(Core Coniainer)。Beans、Core、Context、SpEL -
測試層(Test)。
Spring框架版本
這里簡要介紹下Spring各個版本的變化。其中Spring2.5是變化最大的一個版本,其已經(jīng)完成了大部分Spring的核心功能。而后續(xù)版本都是在其上增加新的語法支持等。
-
Spring2.5:驅(qū)動編程、支持SimpleJdbcTemplate的命名參數(shù)操作 -
Spring3.x:全面支持泛型不支持JDK 1.4。支持SpEL、支持WebService的OXM -
Spring4.x:支持Java8、支持JavaEE6規(guī)范、泛型限定式依賴注入、對Hiberante4的集成和事務(wù)管理提供更好的管理方案.
介紹完Spring的一些基礎(chǔ)常識,我們看是以Spring來寫一個最簡單的HelloWorld,學(xué)習(xí)基于Spring搭建應(yīng)用的基本步驟。
2. 基于Spring如何編寫程序
關(guān)于Spring插件
如果不是用Spring官方提供的Eclipse。需要自己安裝SIS插件.
- Help-->Install New Software-->work with 中輸入http://dist.springsource.com/release/TOOLS/update/e4.6/, 勾掉Contact All update ,回車等待片刻,選中帶spring IDE的四項,一直next直到完成,重啟eclipse即可。如下圖
2.1 基于Spring的HelloWorld編寫步驟
-
導(dǎo)入jar包。并build到classpath。- spring-beans-4.0.0.RELEASE.jar
- spring-core-4.0.0.RELEASE.jar
- commons-logging-1.1.3.jar
-
將我們的控制權(quán)轉(zhuǎn)交給Spring去控制-
創(chuàng)建bean的xml配置文件。 -
注冊bean到容器, 進行屬性注入。注冊HelloWorld到IoC容器中。
-
-
啟動IoC容器, 根據(jù)Bean的id和Bean的類型, 從Ioc容器中取出對應(yīng)的bean實例.bean的獲取方式又有下面三種.一般我們使用最后一種,因為最后這種既能獲取到bean,又無需強轉(zhuǎn).- 根據(jù)ID或者name獲取bean.
ioc.getBean(String) - 根據(jù)類型獲取bean.
ioc.getBean(Class) - 根據(jù)ID和類型獲取bean.
ioc.getBean(String, Class)
- 根據(jù)ID或者name獲取bean.
目錄結(jié)構(gòu)
測試代碼
public class HelloWorldTest {
@Test
public void testHelloWorld() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
}
輸出
可以看到我們使用IoC容器成功的獲取到對應(yīng)的Bean對象,并調(diào)用其say方法,輸出Hello Spring.
2.2 Spring基本配置
使用Spring編寫完第一個HelloWorld演示程序,接下來我細致的分享下Spring基本的配置方式
2.2.1 bean的id和name
在Spring的配置文件中, 也就是配置bean的文件中.我們需要給Bean配置屬性。其中比較常用,用來定位Bean的用兩種方式,一種是id一種是name.
id方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>
測試代碼
@Test
public void testHelloWorldByID() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
ID方式輸出
name方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- <bean id="helloWorld" class="com.sweetcs._01helloworld.HelloWorld"></bean> -->
<bean name="helloWorld,helloWorld2,helloWorld3" class="com.sweetcs._01helloworld.HelloWorld"></bean>
</beans>
測試代碼
@Test
public void testHelloWorldByName() {
Resource resource = new ClassPathResource("com/sweetcs/_01helloworld/hello.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
HelloWorld helloWorld = beanFactory.getBean("helloWorld", HelloWorld.class);
HelloWorld helloWorld2 = beanFactory.getBean("helloWorld2", HelloWorld.class);
HelloWorld helloWorld3 = beanFactory.getBean("helloWorld3", HelloWorld.class);
System.out.println(helloWorld == helloWorld2);
System.out.println(helloWorld2 == helloWorld3);
}
輸出
ID和name的區(qū)別
- 如果是ID方式,一個bean只能配置一個id.并且多個bean的ID值不能重復(fù).如果是Name方式, 一個bean能配置多個別名.
2.3 模塊化xml配置文件
如果我們項目中有很多的bean需要注冊, 那么這時候如果還只是用一個bean的配置文件,那這個文件配置的bean會爆炸性的增長, 這帶來的后果就是十分的難以維護這個配置文件。這個時候我們可以將xml進行拆分, 拆分多個xml文件。一般項目都是按模塊劃分的,我們可以每個模塊下都配置一個xml文件。最后再通過一個統(tǒng)一xml文件,將這些xml文件引入。
引入其他XML配置文件
import元素
使用import元素我們可以將其他的xml配置文件導(dǎo)入。其使用resource屬性代表要導(dǎo)入的文件路徑,并且提供了classpath:和file:協(xié)議分別表示從classpath路徑尋找和從文件系統(tǒng)的路徑尋找.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 導(dǎo)入_01helloworld包下的配置文件 -->
<import resource="classpath:com/sweetcs/_01helloworld/hello.xml"/>
</beans>
對應(yīng)的測試類代碼
注解的意思可以先忽略,看完1.6就明白
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class ApplicationContextTest {
@Autowired
BeanFactory ioc;
@Test
public void testHelloSpring() {
HelloWorld bean = ioc.getBean("helloWorld", HelloWorld.class);
bean.say();
}
}
2.4 Spring中的測試
問題:在編寫Spring的測試用例中, 我們直接使用Spring提供的Test。因為該Test是包含在容器中, 我們不用每一次都創(chuàng)建容器,關(guān)閉容器, 這對系統(tǒng)開銷十分大。
2.4.1 測試步驟
- 導(dǎo)入依賴jar包
-
junit4。需要兩個個jar包junit和hamcrest-core.jar -
spring。需要三個jar包spring-test、spring-aop、spring-expression
-
- 使用Junit提供的注解
@RunWith: 用于指定junit運行環(huán)境,是junit提供給其他框架測試環(huán)境接口擴展.
@ContextConfiguration: 用于指定容器上下文配置文件。該文件用于指定容器創(chuàng)建的時候需要生成哪些bean對象。 - 編寫測試類
// 告訴JVM,Spring測試運行于JVM上.而不是讓Junit運行于JVM上.
@RunWith(SpringJUnit4ClassRunner.class)
// 告訴Spring去哪里尋找配置文件.
@ContextConfiguration("classpath:com/sweetcs/_02springtest/HellWoldTest.xml")
public class HelloWorldTest {
// 自動裝配IoC容器
@Autowired
BeanFactory ioc;
@Test
public void testHelloSpring() {
HelloWorld helloWorld = ioc.getBean("helloWorld", HelloWorld.class);
helloWorld.say();
}
}
配置文件命名的小技巧
-
測試類的配置文件名可以命名為所在的類名-context.放在和測試類一個目錄
原理
@ContextConfiguration也可以不用指定配置文件路徑, 如果不指定,其默認配置文件和所在的類文件在同一級目錄下,且該配置文件名為類名-context.xml.對于上述程序可以的配置文件名就可以改成HellWoroldTest-context
-
務(wù)必指定classpath:.指定該前綴能讓其一定去classpath目錄尋找.
上述主要講了
- 如何像IoC容器轉(zhuǎn)交控制權(quán),讓IoC容器替我們管理這些對象.而不是我們手動去管理。
- 如何編寫Spring中的測試。
接下來我們要比較系統(tǒng)的介紹下IoC容器的內(nèi)容。
3.IoC
IoC容器, 即Inversion Of Control, 中文翻譯就是反轉(zhuǎn)控制。IoC容器反轉(zhuǎn)了哪些控制呢?其實IoC是一種思想,這種思想被引入軟件工程學(xué)中, 主要是為了解決大型軟件項目中的耦合過深問題。
3.1 思想
傳統(tǒng)的思維
傳統(tǒng)的思維下,我們需要哪個對象就去new一個。比如下面的代碼。
public class EmployeeService {
private IEmployeeDAO employeeDAO = new EmployeeDAO();
}
這中代碼本省寫得并沒有錯,但是隨著軟件復(fù)雜度的增加, 你可能導(dǎo)出都存在這種代碼,對于EmployeeService來說它就依賴于EmployeeDAO.如果采用這種方式,可想而知,項目中會存在很多復(fù)雜的依賴,而且這些依賴有的又是重復(fù)引用的.這會導(dǎo)致,其中一個依賴要是存了問題,整個軟件系統(tǒng)就無法正常的運行。
如下圖,是我在網(wǎng)絡(luò)上找的一張圖,傳統(tǒng)軟件開發(fā)思維如下。導(dǎo)致的問題可想而知。齒輪之間相互耦合太強,牽一發(fā)而動全身。
IoC反轉(zhuǎn)控制思想
傳統(tǒng)的軟件設(shè)計是我們需要什么對象需要我們自己去創(chuàng)造,如上分析這隨著軟件復(fù)雜度增加,對于系統(tǒng)的維護和變更是十分困難的,因為存在太多耦合。
IoC的思想
IoC的思想是能不能把這種主動的控制權(quán)轉(zhuǎn)交出來,當(dāng)我們需要某個對象的時候才讓"第三方"幫我們注入我們需要的對象。這就解除了類之間的耦合關(guān)系,類于類之前的聯(lián)系需要通過這個中間容器即IoC容器來橋接。
如下圖, 只要遵守這個"第三方"的規(guī)則,所有的齒輪都能夠轉(zhuǎn)動。這個第三方就相當(dāng)于一個粘合劑,將所有齒輪"粘合"起來,保證整個系統(tǒng)的運轉(zhuǎn)。
這種控制權(quán)顛倒過來的設(shè)計, 我們就是把它稱為“控制反轉(zhuǎn)”。通過這種設(shè)計,我們可以在Spring中很方便的擴充功能,而且還能讓類之間解除耦合。
說了那么多我們先來看下Spring中IoC容器、容器如何創(chuàng)建Bean、Bean如何實例化、Bean的作用域、已經(jīng)Bean的初始化和銷毀方法。
3.2 IoC容器
獲取IoC容器的方式有兩種。一種是BeanFactory提供了最基本的IoC功能。一種是ApplicatioinContext, 基于BeanFactory擴展了更強的功能.
- 使用實現(xiàn)
BeanFacory接口的實現(xiàn)類BeanFacory是Spring底層的接口,只提供了簡單的IoC功能,
負責(zé)配置、創(chuàng)建、管理bean。應(yīng)用中一般不實用BeanFactory, 而推薦使用ApplicationContext。 - 使用實現(xiàn)
ApplicationContext接口的實現(xiàn)類一般開發(fā)中都使用
ApplicationContext來代表容器.ApplicationContext是BeanFactory子接口,除了繼承IoC基本功能,其還提供了更多的功能。消息機制國際化統(tǒng)一的資源加載AOP功能
3.2.1 基于ApplicationContext創(chuàng)建IoC容器(Spring容器)
基于BeanFactory的方式在HelloWorld中已經(jīng)介紹過, 我們就簡單介紹下,基于ApplicatioinContext創(chuàng)建Spring容器的方式。由于ApplicationContext也是一個接口,所以我們只能找其實現(xiàn)類。
通過查看類繼承結(jié)構(gòu)如下圖,可以發(fā)現(xiàn)有如下兩個類。
3.3 Bean創(chuàng)建時機研究
ApplicatioinContext和BeanFactory對bean的創(chuàng)建時機并不是相同的,我們來研究下他們有什么區(qū)別。之所以以下代碼不采用Spring提供的Test來自動裝配IoC容器,是因為如果使用SpringTest,它會自動加載所有Bean.就看不出ApplicationContext創(chuàng)建Bean的時機和BeanFactory的區(qū)別。
以下代碼省略了轉(zhuǎn)交控制權(quán)的步驟,需要配置xml,可以參考上面的配置步驟。
3.3.1 基于BeanFactory的Bean創(chuàng)建時機研究
- testCreateWithBeanFactory用例,是創(chuàng)建IoC容器,并獲取Bean
- testCreateWithBeanFactory2用例, 是只創(chuàng)建IoC容器
@Test
public void testCreateWithBeanFactory() {
Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
BeanFactory ioc = new XmlBeanFactory(resource);
User user = ioc.getBean("user", User.class);
System.out.println(user);
/* 輸出
User 構(gòu)造器被調(diào)用,正在初始化User對象
User [name=SweetCS]
*/
}
@Test
public void testCreateWithBeanFactory2() {
Resource resource = new ClassPathResource("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
BeanFactory ioc = new XmlBeanFactory(resource);
/*
* 無任何輸出
*/
}
使用BeanFacory方式,Bean只會在需要的時候才創(chuàng)建
3.3.2 基于ApplicationContext的Bean創(chuàng)建時機研究
- testCreateWithApplicationContext用例,是創(chuàng)建IoC容器,并獲取Bean
- testCreateWithApplicationContext2用例, 是只創(chuàng)建IoC容器
@Test
public void testCreateWithApplicationContext() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
User bean = ctx.getBean("user", User.class);
System.out.println(bean);
/* 輸出
User 構(gòu)造器被調(diào)用,正在初始化User對象
User [name=SweetCS]
*/
}
@Test
public void testCreateWithApplicationContext2() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/sweetcs/_03ioc/BeanCreateTest-context.xml");
// 輸出 User 構(gòu)造器被調(diào)用,正在初始化User對象
}
運行程序后使用ApplicationContext的方式,無論有沒有取獲取Bean,Bean都會在容器啟動的時候被創(chuàng)建。
BeanFactory和ApplicationContext的應(yīng)用場景
ApplicatioinContext會一次性將所有Bean的創(chuàng)建出來,主要是為了優(yōu)化服務(wù)端的性能,主用應(yīng)用于Web后端開發(fā)BeanFacory只會在需要使用Bean的時候在創(chuàng)建,主要用在客戶端開發(fā)。因為客戶端直接面向用戶,關(guān)注用戶體驗。不能再一啟動創(chuàng)建大量對象,讓應(yīng)用卡頓半天。
3.3.3 能否配置延遲創(chuàng)建
其實這個需求,一般不回用到。對于我們做后端開發(fā)的來說, 最常用的是ApplicationContext, 如果要讓其延遲加載Bean也是可以。有兩種配置方式。
-
全局配置,對所有Bean都生效。<beans default-lazy-init="true"> </beans> -
局部配置,指對指定Bean生效。<bean lazy-init="true"></bean>
任選一種做測試,會發(fā)現(xiàn)在ApplicationContext下的Bean,如果沒獲取也不會創(chuàng)建.
3.4 Bean的實例化方式
之所以要聊Bean的實例化方式,是因為我們可能會在不同的情況下需要使用不同的方式,讓Spring幫我們注入對象。所以我們需要來研究下讓Spring幫我們注入對象的四種方式, 其對應(yīng)的就是Bean的實例化方式。主要有以下四種方式
通過無參構(gòu)造器方式,最標(biāo)準(zhǔn), 使用最多(重點)
轉(zhuǎn)交控制權(quán)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.最標(biāo)準(zhǔn)的方式,通過無參夠照器方式創(chuàng)建 -->
<bean id="user" class="com.sweetcs._04beaninstance.construtor.User"></bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class BeanInstanceTest {
@Autowired
ApplicationContext ctx;
@Test
public void testInstanceBeanWithNoArgConstrutor() {
User bean = ctx.getBean("user", User.class);
System.out.println(bean);
}
}
輸出
通過靜態(tài)工廠實例化方式
其主要解決一些系統(tǒng)留下來的問題。例如有可能你接觸的項目并不是通過最標(biāo)準(zhǔn)的創(chuàng)建Bean的方式創(chuàng)建,它可能都是使用靜態(tài)工程創(chuàng)建,那我們就應(yīng)該在接入Spring的時候,為了兼容性,使用靜態(tài)工廠實例化方式。
SomeBean2StaticFactory
配置bean,id, class ,factory-method
@Test
public void testInstanceBeanWithStaticFactory() {
SomeBean someBean = ctx.getBean("someBean", SomeBean.class);
System.out.println(someBean);
}
輸出
通過實例工廠實例化方式
SomeBean2
package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2 {
}
SomeBean2Factory
package com.sweetcs._04beaninstance.instance_factory;
public class SomeBean2Factory {
public SomeBean2 getSomeBean2() {
return new SomeBean2();
}
}
轉(zhuǎn)交控制權(quán)
<!-- 3.通過實例工廠實例化 -->
<bean id="someBean2Factory" class="com.sweetcs._04beaninstance.instance_factory.SomeBean2Factory"></bean>
<bean id="someBean2" factory-bean="someBean2Factory" factory-method="getSomeBean2"></bean>
測試用例
@Test
public void testInstanceBeanWithInstanceFactory() {
SomeBean2 someBean2 = ctx.getBean("someBean2",SomeBean2.class);
System.out.println(someBean2);
}
需要new出這個工廠
先要轉(zhuǎn)交實例工廠對象創(chuàng)建權(quán)
再要轉(zhuǎn)交Bean對象創(chuàng)建權(quán)
實現(xiàn)FacotryBean接口實例化。實例工廠的變種,主要為了集成其他框架。
如果發(fā)現(xiàn)了這個對象是實現(xiàn)這個接口,Spring容器會自動條用getObject方法返回創(chuàng)建的對象。
package com.sweetcs._04beaninstance.facotry_bean;
import org.springframework.beans.factory.FactoryBean;
public class MyBean {
}
package com.sweetcs._04beaninstance.facotry_bean;
import org.springframework.beans.factory.FactoryBean;
public class MyBeanFactory implements FactoryBean<MyBean>{
@Override
public MyBean getObject() throws Exception {
// TODO Auto-generated method stub
return new MyBean();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return MyBean.class;
}
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
測試用例
@Test
public void testInstanceBeanWithImplementsFactoryBean() {
MyBean myBean = ctx.getBean("myBean", MyBean.class);
System.out.println(myBean);
}
輸出
3.5 Bean的作用域
Bean的作用域研究表示Bean可以存活多久, Bean的標(biāo)簽里有一個scope屬性,要來表示Bean的作用域。這邊只介紹用得最多的兩種方式singleton(單例)和prototype(多例)。
默認方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean"></bean>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyBeanTest {
@Autowired
ApplicationContext ctx;
@Test
public void testDefaultScope() {
MyBean myBean = ctx.getBean("myBean", MyBean.class);
MyBean myBean2 = ctx.getBean("myBean", MyBean.class);
MyBean myBean3 = ctx.getBean("myBean", MyBean.class);
System.out.println(myBean + "\n" + myBean2 + "\n" + myBean3);
}
}
輸出
Singleton方式
更改bean的配置的,添加scope屬性為singleton
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="singleton"></bean>
再運行剛才的用例,輸出
prototype方式
更該bean的配置為prototype
<bean id="myBean" class="com.sweetcs._05beanscope.MyBean" scope="prototype"></bean>
所有作用域的總結(jié)
最常用的是singleton和prototype作用域。其他的可以參考以下
| 作用域 | 描述 |
|---|---|
| singleton | 該作用域?qū)?bean 的定義的限制在每一個 Spring IoC 容器中的一個單一實例(默認)。 |
| prototype | 該作用域?qū)我?bean 的定義限制在任意數(shù)量的對象實例。 |
| request | 該作用域?qū)?bean 的定義限制為 HTTP 請求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
| session | 該作用域?qū)?bean 的定義限制為 HTTP 會話。 只在web-aware Spring ApplicationContext的上下文中有效。 |
| global-session | 該作用域?qū)?bean 的定義限制為全局 HTTP 會話。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
3.6 Bean的初始化和銷毀方法
bean標(biāo)簽中提供了init-method屬性和destory-method屬性。分別會在bean創(chuàng)建之前調(diào)用,在bean銷毀之前做收尾操作。
3.6.1 DataSource需要回收
比如DataSource最終都需要關(guān)閉資源, 在Bean銷毀之前,都要調(diào)用close方法。
MyDataSourceBean
package com.sweetcs._06bean_init_destory;
public class MyDataSourceBean {
public void doingSomeWork() {
System.out.println("處理業(yè)務(wù)邏輯");
}
public void init() {
System.out.println("初始化配置,準(zhǔn)備連接數(shù)據(jù)庫");
}
public void close() {
System.out.println("釋放connection");
}
}
轉(zhuǎn)交控制權(quán)給Spring容器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean"
init-method="init" destroy-method="close"></bean>
</beans>
MyDataSourceBeanTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyDataSourceBeanTest {
@Autowired
ApplicationContext ctx;
@Test
public void testCreateMyDataSourceBean() {
MyDataSourceBean dataSource = ctx.getBean("myDataSource", MyDataSourceBean.class);
dataSource.doingSomeWork();
}
}
輸出
3.6.2 對于多例無效
如果Bean標(biāo)簽的scope屬性配置成scope="prototype"。則容器不負責(zé)銷毀控制,而且這個bean也不會放入Spring容器里取管理, 而是一創(chuàng)建就交給程序員,讓程序員自己去負責(zé)。
<bean id="myDataSource" class="com.sweetcs._06bean_init_destory.MyDataSourceBean"
init-method="init" destroy-method="close"
scope="prototype">
</bean>
4.DI
DI又稱為依賴注入.DI是IoC思想的一種實現(xiàn),在Spring容器中就采用了DI技術(shù)來實現(xiàn)IoC, 即采用DI實現(xiàn)了反轉(zhuǎn)控制。所以我們要來探討下如何注入,注入的幾種方式。記下來我們主要來研究自動裝配、屬性注入、構(gòu)造器注入、屬性占位符的使用
4.1 自動裝配
自動裝配Spring中主要提供了兩種方式。如下
- 基于XML配置的自動裝配.
Bean標(biāo)簽中配置autowire屬性 - 基于注解配置的自動裝配.
@Autowirsed注解
基于XML的自動裝配
我們創(chuàng)建兩個類分別是ABean和CBean,讓ABean依賴于CBean.代碼如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="aBean" class="com.sweetcs._07di.autowise_xml.ABean" autowire="byType"></bean>
<bean id="cBean" class="com.sweetcs._07di.autowise_xml.CBean"></bean>
</beans>
ABean
public class ABean {
private CBean cBean;
public void setcBean(CBean cBean) {
this.cBean = cBean;
}
@Override
public String toString() {
return "ABean [cBean=" + cBean + "]";
}
}
CBean
public class CBean {
}
測試代碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AutoWiseWithXMLTest {
@Autowired
ApplicationContext ctx;
@Test
public void testAutoWiseWithXML() {
ABean bean = ctx.getBean("aBean", ABean.class);
System.out.println(bean);
}
}
輸出
如上輸出, 基于XML方式的自動裝配.自動將CBean注入到了ABean當(dāng)中。使用的是byType的方式,其實還可以使用byName的方式。下面說下二者區(qū)別
byName和byType的區(qū)別
- byName是按照屬性名去容器中找id為這個屬性名的對象,如果找不到則拋出異常。
- byType是會根據(jù)對應(yīng)要注入的字段的類型,去容器里找。如果找到一個匹配類型的對象則直接注入。如果找到多個則拋出異常。
4.2 注入值
4.2.1 setter注入
一般我們不會在xml中使用自動裝配,應(yīng)用這樣很難看出這個bean有哪些屬性。取而代之的是有兩種方法
- 使用屬性注入方式??梢院芮逦目闯鯾ean的屬性。
- 使用注解裝配方式。
注入簡單類型-使用字符串
<bean id="employee" class="com.sweetcs._07di.property.Employee">
<property name="name" value="SweetCS"></property>
<property name="url" value="http://www.baidu.com"></property>
<property name="age" value="1"></property>
</bean>
測試用例
public class Employee {
private String name;
private URL url;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [name=" + name + ", url=" + url + ", age=" + age + "]";
}
}
@Test
public void testDIWithProperyMethod() {
Employee employee = ctx.getBean("employee", Employee.class);
System.out.println(employee);
}
注入引用類型
public class EmployeeDAO {
public void save() {
System.out.println("開始持久化數(shù)據(jù)到數(shù)據(jù)庫");
}
}
public class EmployeeService {
private EmployeeDAO employeeDAO;
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
@Override
public String toString() {
return "EmployeeDAO [employeeDAO=" + employeeDAO + "]";
}
public void save() {
employeeDAO.save();
}
}
xml配置
<bean id="employeeService" class="com.sweetcs._07di.property.EmployeeService" autowire="byType"></bean>
<bean id="employeeDAO" class="com.sweetcs._07di.property.EmployeeDAO"></bean>
注入集合類型
public class CollectionBean {
private Set set;
private List list;
private String[] array;
private Properties properties;
private Map map;
public void setArray(String[] array) {
this.array = array;
}
public void setProperties(Properties p) {
this.properties =p;
}
public void setSet(Set set) {
this.set = set;
}
public void setList(List list) {
this.list = list;
}
public void setMap(Map map) {
this.map = map;
}
@Override
public String toString() {
return "CollectionBean [set=" + set + ", list=" + list + ", arrayList=" + array + ", map=" + map + "]";
}
}
xml配置
<bean id="collectionBean" class="com.sweetcs._07di.property.CollectionBean">
<property name="set">
<set>
<value>"1"</value>
<value>"2"</value>
<value>3</value>
</set>
</property>
<property name="list">
<list>
<value>"1"</value>
<value>"2"</value>
<value>3</value>
</list>
</property>
<property name="array">
<array>
<value>"1"</value>
<value>"2"</value>
<value>"3"</value>
</array>
</property>
<property name="properties">
<value>
key1=value1
key2=value2
key3=value3
</value>
</property>
<property name="map">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
4.2.2 構(gòu)造器注入
構(gòu)造器注入其實和屬性注入差不多, 主要有兩個差別,具體就不演示了。
- 需要為相應(yīng)的Bean類
提供有參的構(gòu)造方法(無參構(gòu)造也要提供) - 需要使用
constructor-arg標(biāo)簽替換property標(biāo)簽。
4.3 property place holder(配置數(shù)據(jù)庫連接池)
屬性占位符是十分有用的一個功能。開發(fā)中一般我們都將有關(guān)數(shù)據(jù)庫的配置做成一個配置文件,方便和運維人員解耦,運維人員只要拿著配置文件取做部署,無需關(guān)心程序員的代碼實現(xiàn)細節(jié)。
步驟
- 使用屬性占位符初始化上下文的環(huán)境變量。
<context:property-placeholder location="classpath:配置文件位置" /> - 使用屬性注入方式,配置相關(guān)信息。
XML配置, 轉(zhuǎn)交控制權(quán)
<context:property-placeholder location="classpath:config.properties"/>
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="initialSize" value="${jdbc.initialSize}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="${jdbc.minIdle}"></property>
</bean>
測試用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DruidCreateTest {
@Autowired
ApplicationContext ctx;
@Test
public void testGetConnWithDruid() {
DruidDataSource ds = ctx.getBean("ds", DruidDataSource.class);
Connection connection = null;
try {
connection = ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(connection);
}
}
輸出
















