杭州政府網(wǎng)站建設(shè)管理seo查詢系統(tǒng)源碼
第3章-第2節(jié)
一、知識點(diǎn)
面向接口編程、什么是spring、什么是IOC、IOC的使用、依賴注入
二、目標(biāo)
-
了解什么是spring
-
理解IOC的思想和使用
-
了解IOC的bean的生命周期
-
理解什么是依賴注入
三、內(nèi)容分析
-
重點(diǎn)
-
了解什么是spring
-
理解IOC的思想
-
掌握IOC的使用
-
-
難點(diǎn)
-
理解IOC的思想
-
掌握IOC的使用
-
四、內(nèi)容
1、Spring
1.1 簡介
spring是分層的java SE/EE應(yīng)用full-stack輕量級開源框架,以IOC(Inverse Of Control:控制反轉(zhuǎn))和AOP(Aspect Oriented Programming:面向切面編程)為內(nèi)核,提供了展現(xiàn)層Spring MVC和持久層Spring JDBC以及業(yè)務(wù)層事務(wù)管理等眾多的企業(yè)級應(yīng)用技術(shù),還能整合開源世界眾多著名的第三方框架和類庫,逐漸成為使用最多的Java EE企業(yè)級應(yīng)用開源框架。
1.2 優(yōu)勢
-
開源免費(fèi)的輕量級框架
-
低侵入式設(shè)計(jì),代碼污染極低
-
支持事務(wù)的處理,對框架整合的支持
-
以控制反轉(zhuǎn)(IOC),面向切面編程(AOP)為內(nèi)核
-
方便解耦,簡化開發(fā):將對象的創(chuàng)建交給spring 無需new
-
提供了展現(xiàn)層SpringMVC和持久層Spring JDBCTemplate以及業(yè)務(wù)層事務(wù)管理等眾多企業(yè)級應(yīng)用技術(shù)
-
能整合開源世界眾多第三方框架和類庫
1.3 引入依賴
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version>
</dependency>
2、Ioc
2.1 概念
控制反轉(zhuǎn)(IoC,Inversion of Control)是一個(gè)概念,是一種思想。指將傳統(tǒng)上由程序代碼直接操控的對象調(diào)用權(quán)交給容器,通過容器來實(shí)現(xiàn)對象的裝配和管理??刂品崔D(zhuǎn)就是對對象控制權(quán)的轉(zhuǎn)移,從程序代碼本身反轉(zhuǎn)到了外部容器。通過容器實(shí)現(xiàn)對象的創(chuàng)建,屬性賦值,依賴的管理。
以前我們在代碼中,使用new 構(gòu)造方法創(chuàng)建對象,現(xiàn)在不用了, 由容器代替開發(fā)人員管理對象。
2.2 傳統(tǒng)方式和ioc方式的區(qū)別
-
傳統(tǒng)
我們要實(shí)現(xiàn)某一個(gè)功能或者說是完成某個(gè)業(yè)務(wù)邏輯時(shí)至少需要兩個(gè)或以上的對象來協(xié)作完成,每個(gè)對象在需要使用他的合作對象時(shí),自己均要使用像new object() 這樣的語法來將合作對象創(chuàng)建出來,這個(gè)合作對象是由自己主動(dòng)創(chuàng)建出來的,創(chuàng)建合作對象的主動(dòng)權(quán)在自己手上,自己需要哪個(gè)合作對象,就主動(dòng)去創(chuàng)建,創(chuàng)建合作對象的主動(dòng)權(quán)和創(chuàng)建時(shí)機(jī)是由自己把控的,而這樣就會使得對象間的耦合度高了,A對象需要使用合作對象B來共同完成一件事,A要使用B,那么A就對B產(chǎn)生了依賴,也就是A和B之間存在一種耦合關(guān)系,并且是緊密耦合在一起。
-
ioc
創(chuàng)建合作對象B的工作是由Spring來做的,Spring創(chuàng)建好B對象,然后存儲到一個(gè)容器里面,當(dāng)A對象需要使用B對象時(shí),Spring就從存放對象的那個(gè)容器里面取出A要使用的那個(gè)B對象,然后交給A對象使用,至于Spring是如何創(chuàng)建那個(gè)對象,以及什么時(shí)候創(chuàng)建好對象的,A對象不需要關(guān)心這些細(xì)節(jié)問題(你是什么時(shí)候生的,怎么生出來的我可不關(guān)心,能幫我干活就行),A得到Spring給我們的對象之后,兩個(gè)人一起協(xié)作完成要完成的工作即可。
下面用制造汽車需要引擎、輪胎等零件的場景解釋含義
-
假設(shè)現(xiàn)在要生產(chǎn)汽車、貨車、客車、出租車,它們都是使用【1代引擎】
?
代碼如下:
public class EngineV1 {
?private String name = "1代引擎";
?public String getName() {return name;}
}
/*** 汽車*/
public class Car {
?private String carName = "小汽車";
?private EngineV1 engineV1;
?public Car(EngineV1 engineV1) {this.engineV1 = engineV1;}
?public void info() {System.out.println(carName + "使用的是" + engineV1.getName());}
}
/*** 貨車*/
public class Van {
?private String carName = "貨車";
?private EngineV1 engineV1;
?public Van(EngineV1 engineV1) {this.engineV1 = engineV1;}
?public void info() {System.out.println(carName + "使用的是" + engineV1.getName());}
}
測試
Car car = new Car(new EngineV1());
car.info();
?
Van van = new Van(new EngineV1());
van.info();
?
// 結(jié)果
// 小汽車使用的是1代引擎
// 貨車使用的是1代引擎
總結(jié):現(xiàn)在可以看出不管是汽車還是貨車,都是依賴于【1代引擎】的,耦合度很高,沒有【1代引擎】就沒有這些車。假如我們還有很多其他類型的車都是依賴于【1代引擎】,有一天我想把所有的【1代引擎】換成 【2代引擎】我該怎么做?全局搜索【1代引擎】修改為 【2代引擎】,想想都有點(diǎn)麻煩。
-
我們需要換一種思路來實(shí)現(xiàn),解決上述問題。
?
/*** 引擎接口*/
public interface IEngine {String getName();
}
public class EngineV1 implements IEngine{
?private String name = "1代引擎";
?public String getName() {return name;}
}
public class EngineV2 implements IEngine{
?private String name = "2代引擎";
?public String getName() {return name;}
}
/*** 汽車*/
public class Car {
?private String carName = "小汽車";
?// 現(xiàn)在不依賴于具體哪個(gè)引擎,直接對接引擎接口private IEngine engine;
?public Car(IEngine engine) {this.engine = engine;}
?public void info() {System.out.println(carName + "使用的是" + engine.getName());}
}
/*** 貨車*/
public class Van {
?private String carName = "貨車";
?private IEngine engine;
?public Van(IEngine engine) {this.engine = engine;}
?public void info() {System.out.println(carName + "使用的是" + engine.getName());}
}
測試
Car car = new Car(new EngineV1());
car.info();
?
Van van = new Van(new EngineV1());
van.info();
?
// 結(jié)果
// 小汽車使用的是1代引擎
// 貨車使用的是1代引擎
Car car = new Car(new EngineV2());
car.info();
?
Van van = new Van(new EngineV2());
van.info();
?
// 結(jié)果
// 小汽車使用的是2代引擎
// 貨車使用的是2代引擎
總結(jié):代碼中不再依賴于具體,而是依賴于抽象容器,即要針對接口編程,不針對實(shí)現(xiàn)編程。過去思維中想要什么依賴,需要自己去 “拉” 改為抽象容器主動(dòng) “推” 給你,你只管使用實(shí)體就可以了。這是依賴倒轉(zhuǎn) (DIP) 的一種表現(xiàn)形式。由原來的汽車依賴引擎轉(zhuǎn)化成了引擎依賴引擎接口了,進(jìn)行了反轉(zhuǎn)?,F(xiàn)在生產(chǎn)汽車的時(shí)候,給汽車什么引擎它就用什么引擎,汽車就脫離了只能用【1代引擎】絕對綁定形式了。
我們還可以接著優(yōu)化,現(xiàn)在是使用引擎的時(shí)候我們手動(dòng)去new的。假如項(xiàng)目中有很多的地方使用了【1代引擎】,要換成【2代引擎】,即把全部的new EngineV1()
改成new EngineV2()
。
-
針對上面的問題進(jìn)行優(yōu)化。我們可以先創(chuàng)建一個(gè)倉庫出來,把引擎先創(chuàng)建好放在容器里面。車輛要用引擎的時(shí)候直接從倉庫里面獲取。
?
/*** 容器*/
public class Container {
?private Map<String, Object> map = new HashMap<String, Object>();
?public Container() {// 此處利用可以利用讀取配置的方式,利用反射動(dòng)態(tài)創(chuàng)建出對象map.put("engine", new EngineV1());}
?public Object getBean(String name) {return map.get(name);}
}
測試
// 創(chuàng)建容器
Container container = new Container();
?
Car car = new Car((IEngine) container.getBean("engine"));
car.info();
?
Van van = new Van((IEngine) container.getBean("engine"));
van.info();
?
// 結(jié)果
// 小汽車使用的是1代引擎
// 貨車使用的是1代引擎
現(xiàn)在要把【1代引擎】化成【2代引擎】,我們只需要修改容器里面的引擎即可,只要修改一處地方。
map.put("engine", new EngineV2());
上面的測試結(jié)果就變成了
// 創(chuàng)建容器
Container container = new Container();
?
Car car = new Car((IEngine) container.getBean("engine"));
car.info();
?
Van van = new Van((IEngine) container.getBean("engine"));
van.info();
?
// 結(jié)果
// 小汽車使用的是2代引擎
// 貨車使用的是2代引擎
總結(jié):這就是IOC要做的事情。把創(chuàng)建對象的過程交給spring管理,我們需要用的時(shí)候直接拿就行。
3、基本使用
3.1 方式一:配置文件
-
創(chuàng)建xml文件 resources/文件名.xml
-
創(chuàng)建類
-
xml配置
<!-- id:spring生成對象的時(shí)候,放到容器里面,會給每一個(gè)對象生成一個(gè)id,用于區(qū)分對象 --> <!-- class:告訴它使用哪個(gè)類 --> <bean id="student" class="com.company.eneity.Student">
-
獲取對象
-
通過id獲取
public static void main(String[] args) {// 生成spring容器// 讀取spring的xml文件,根據(jù)內(nèi)容生成對應(yīng)的對象,放到容器里面ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");// 獲取對象,傳入id,通過id獲取對象Student student = (Student) ctx.getBean("student");System.out.println(student); }
-
通過類獲取
public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");Student student1 = ctx.getBean(Student.class);System.out.println(student1); }
-
3.2 方式二:注解
-
創(chuàng)建類
// spring掃描到這個(gè)注解的時(shí)候,會自動(dòng)生成對象 @Component public class Teacher {}
-
配置xml的component-scan
<!-- 掃描包,會去掃描注解,有對應(yīng)注解的會自動(dòng)生成對象 --> <context:component-scan base-package="com.company"></context:component-scan>
-
獲取對象
@Component public class TeacherDao {private Teacher teacher;public void test(){System.out.println(teacher);} } ? ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); TeacherDao teacherDao = ctx.getBean(TeacherDao.class); teacherDao.test(); 3.3 bean的生命周期
spring默認(rèn)的是單例對象可以通過scope修改
-
單例對象
scope="singleton"
創(chuàng)建bean的時(shí)候使用默認(rèn)的無參構(gòu)造函數(shù)
一個(gè)應(yīng)用只有一個(gè)對象的實(shí)例,它的作用范圍就是整個(gè)應(yīng)用
生命周期
對象出生:當(dāng)應(yīng)用加載,創(chuàng)建容器時(shí),對象就被創(chuàng)建
對象活著:只要容器在,對象一直活著
對象死亡:當(dāng)應(yīng)用卸載,銷毀容器時(shí),對象就被銷毀了
測試
// 創(chuàng)建spring容器 // 創(chuàng)建容器的時(shí)候,對象已經(jīng)生成 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); ? // 獲取對象只是從容器里面拿出來 Student student1 = (Student) ctx.getBean("student"); Student student2 = (Student) ctx.getBean("student"); ? System.out.println(student1 == student2); // 結(jié)果 true
-
多例對象
scope="prototype"
每次訪問對象時(shí),都會被重新創(chuàng)建對象實(shí)例
生命周期
對象出生:當(dāng)使用對象時(shí),創(chuàng)建新的對象實(shí)例
對象活著:對象在使用中,對象就一直活著
對象死亡:當(dāng)對象長時(shí)間不用,被java垃圾回收機(jī)制回收
測試
// 創(chuàng)建spring容器 // 創(chuàng)建容器的時(shí)候?qū)ο筮€沒有生成 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); ? // 獲取對象的時(shí)候才創(chuàng)建對象 Student student1 = (Student) ctx.getBean("student"); Student student2 = (Student) ctx.getBean("student"); ? System.out.println(student1 == student2); ? // 結(jié)果 false
4、依賴注入
IoC的一個(gè)重點(diǎn)是在系統(tǒng)運(yùn)行中,動(dòng)態(tài)的向某個(gè)對象提供它所需要的其他對象。這一點(diǎn)是通過DI(Dependency Injection,依賴注入)來實(shí)現(xiàn)的。
4.1 使用構(gòu)造函數(shù)
前提:類中需要提供一個(gè)對應(yīng)參數(shù)列表的構(gòu)造函數(shù)
標(biāo)簽:constructor-arg
標(biāo)簽屬性:
index:指定參數(shù)在構(gòu)造函數(shù)參數(shù)列表中的索引位置
type:指定參數(shù)在構(gòu)造函數(shù)中的數(shù)據(jù)類型
name:指定參數(shù)在構(gòu)造函數(shù)中的名稱,用這個(gè)找給誰賦值
value:它能賦的值是基本數(shù)據(jù)類型和String類型
ref:它能賦的值是其他bean類型,也就是說,必須得是在配置文件中配置過的bean
-
基本數(shù)據(jù)類型(數(shù)值類和字符串類)
基本數(shù)據(jù)類型直接用value賦值
<bean id="student" class="com.company.eneity.Student"><!-- constructor-arg調(diào)用有參否則函數(shù),必須有寫構(gòu)造函數(shù) --><constructor-arg name="name" value="張三"></constructor-arg><constructor-arg name="age" value="18"></constructor-arg> </bean>
-
引用數(shù)據(jù)類型
引用數(shù)據(jù)類型使用ref賦值
<bean id="student" class="com.company.eneity.Student"><!-- constructor-arg調(diào)用有參否則函數(shù),必須有寫構(gòu)造函數(shù) --><constructor-arg name="name" value="張三"></constructor-arg><constructor-arg name="age" value="18"></constructor-arg><constructor-arg name="studentMarjor" ref="studentMarjor"></constructor-arg> </bean>
4.2 使用set賦值
標(biāo)簽:property
標(biāo)簽屬性:
name:找的是類中set方法后面的部分
value:給屬性賦值的是基本數(shù)據(jù)類型和String類型
ref:給屬性賦值是其他bean類型的
-
基本數(shù)據(jù)類型
<bean id="student2" class="com.company.eneity.Student2"><!-- property調(diào)用set的方法,必須set方法 --><property name="name" value="張三"></property><property name="age" value="18"></property> </bean>
-
引用數(shù)據(jù)類型
<bean id="student" class="com.company.eneity.Student"><property name="name" value="張三"></property><property name="age" value="18"></property><property name="studentMarjor" ref="studentMarjor"></property> </bean> <bean id="studentMarjor" class="com.company.eneity.StudentMarjor"><property name="name" value="軟件工程"></property><property name="studentNum" value="80"></property> </bean>
5、小結(jié)
本章節(jié)中我們學(xué)習(xí)了什么是Spring,spring的優(yōu)勢是什么,為什么要用spring,同時(shí)學(xué)習(xí)了spring的一個(gè)核心功能IOC(控制反轉(zhuǎn)),理解了控制反轉(zhuǎn)的作用以及為什么要用控制反轉(zhuǎn),同時(shí)理解了什么是依賴注入,學(xué)會了通過XML文件配置IOC和不同方式的依賴注入。
下一節(jié)中,我們將會學(xué)習(xí)一些Spring的常用注解,學(xué)會使用注解進(jìn)行開發(fā),去理解注解開發(fā)和配置文件開發(fā)的區(qū)別和優(yōu)勢。