一. Spring概述
1.1 什么是Spring
Spring是一個開源框架,Spring是于2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/EE (一棧式) 輕量級開源框架。
- JEE分層
- 表現層(頁面數據顯示、頁面跳轉調度)jsp/servlet
- 業務層(業務處理和功能邏輯、事務控制)-service
- 持久層(數據存取和封裝、和數據庫打交道)dao
- 一站式
Spring提供了JavaEE各層的解決方案:
表現層:Spring MVC,持久層:JdbcTemplate、ORM框架整合,業務層:IoC、AOP、事務控制。
- 輕量級:Spring的出現取代了EJB的臃腫、低效、繁瑣復雜、脫離現實。
1.2 Spring的核心
IoC(Inverse of Control 反轉控制): 將對象創建權利交給Spring工廠進行管理。
AOP(Aspect Oriented Programming 面向切面編程),基于動態代理的功能增強方式。
1.3 Spring的優點
- 方便解耦,簡化開發
- Spring就是一個大工廠,可以將所有對象創建和依賴關系維護,交給Spring管理
- AOP編程的支持
- Spring提供面向切面編程,可以方便的實現對程序進行權限攔截、運行監控等功能
- 聲明式事務的支持
- 只需要通過配置就可以完成對事務的管理,而無需手動編程
- 方便程序的測試
- Spring對Junit4支持,可以通過注解方便的測試Spring程序
- 方便集成各種優秀框架
- Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持
- 降低JavaEE API的使用難度
- Spring 對JavaEE開發中非常難用的一些API(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大降低
關于框架的特性,我們也會俗稱Spring為開發架構的粘合劑。
二. Spring框架的快速入門
2.1 搭建環境
- 下載開發包,導jar包
下載網址:http://repo.spring.io/libs-release-local/org/springframework/spring/
開發包目錄結構:
docs : api文檔和開發規范
libs : 開發需要的jar包(源碼)
schema : 開發需要的約束schema
-----------------------------------------------------------------------------
1. 新建web項目
2. Spring項目的核心容器最基本的jar包(4個):
1. Beans 2. Core 3. Context 4.Expression Language
3. Spring框架需要的日志包(2個,依賴庫中找)
1. apache commons-logging(JCL)日志框架
2. log4j的日志實現
3. log4j的配置文件 log4j.properties
2.2 業務代碼
模擬用戶保存。
傳統寫法 :
UserServiceImpl :
// 模擬用戶注冊
public void save() {
// 傳統方式
System.out.println("業務層........UserServiceImpl用戶注冊...");
UserDAO dao = new UserDAOImpl();
userDAO.save();
}
UserDAO :
//模擬用戶注冊
public void save() {
System.out.println("持久層.......UserDAOImpl用戶注冊...");
}
web層(這里是測試test):
@Test
public void test() {
UserService service = new UserServiceImpl();
service.save();
}
傳統的方式代碼過于耦合,上層代碼過于依賴下層代碼,如果業務有改動,要改變DAO的實現類時,需要改動代碼:
UserDAO userDAO = new UserDaoImpl();因此需要采取方式進行解耦合。
解決方案:采用IoC(Inverse of Control)控制反轉的思想進行解耦合.
簡單的說就是引入工廠(第三者),將原來在程序中手動創建管理的依賴的UserDAO對象,交給工廠來創建管理。
在Spring框架中,這個工廠就是Spring中的工廠,因此,也可以說,將創建管理UserDAO對象的控制權被反轉給了Spring框架了。
-----------------------------------------------------------------------------
概念:IoC中文翻譯為控制反轉,指以前程序自己創建對象,現在將創建對象的控制權交給了第三方(Spring)了。
IoC底層實現:工廠(設計模式)+反射(機制) + 配置文件(xml)。
IoC是一種思想,控制反轉的思想、解耦合的思想。
Spring的IoC是該思想的一種實現。因此Spring容器也通常稱之為IoC容器。
2.3 IoC控制反轉的實現
傳統是自己創建對象,現在將創建對象交給Spring容器,我們獲取就行了.
這種方式,即使更換實現類,也只需要修改配置文件中的實現類的路徑。
2.3.1 Spring的核心配置文件編寫applicationContext.xml
1. 習慣上: 在src建立applicationContext.xml (位置:src目錄或者 WEB-INF目錄)
2. 引入xml的頭部信息bean schema約束,可以參考規范文檔中的的xsd-config.html
<?xml version="1.0" encoding="UTF-8"?>
<!-- 導入Spring的DTD約束 -->
<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="userDAO" class="com.itdream.spring.dao.UserDAOImpl"/>
<bean id="userService" class="com.itdream.spring.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
</beans>
3. 配置實現類的映射bean
<bean id="userDAO" class="com.itdream.spring.dao.UserDAOImpl"/>
2.3.2 通過Spring的工廠獲取Bean完成相關操作
讀取配置文件,獲取
Spring的Bean
工廠-
通過
Spring的Bean
工廠獲取對象// Spring解耦合,使用配置文件:創建工廠+反射創建UserDAO對象 // 加載配置文件,獲取工廠對象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 實例化對象 UserDAO dao = (UserDAO) applicationContext.getBean("userDAO"); userDAO.save();
2.4 DI依賴注入的實現
DI:Dependency Injection 依賴注入,在Spring框架負責創建Bean對象時,動態的將依賴對象注入到Bean組件(簡單的說,可以將另外一個bean對象動態的注入到另外一個bean中。)
耦合代碼變成依賴注入代碼的方法:
即:Spring創建了Service、DAO對象,在配置中將DAO傳入Servcie,那么Service對象就包含了DAO對象的引用。
在Service對象創建調用時,也會產生一個DAO對象,并通過Service內提供的setter方法將該對象的引用注入進去。
applicationContext.xml核心配置文件:
<!-- 配置userDAO的創建映射,Spring框架通過這個映射來new對象 -->
<bean id="userDAO" class="com.itdream.spring.dao.UserDAOImpl"/>
<!-- 配置userService的創建映射,Spring通過該映射創建Service對象 -->
<bean id="userService" class="com.itdream.spring.service.UserServiceImpl">
<!-- property:屬性注入
name : setter方法的名字,例如setUserDAO-userDAO。
//注入時,Spring會自動調用setter方法,將創建出來的對象放入到該方法中完成注入
ref : 創建該對象的引用,寫入被Spring管理的bean的name屬性
-->
<property name="userDAO" ref="userDAO"/>
</bean>
Service層:
聲明注入的對象,提供setter方法
private UserDAO userDAO;
//提供setter方法Spring方法進行動態注入userDAO
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
@Override
// 模擬用戶注冊
public void save() {
//Spring框架創建了userDAO對象并注入進來,因此不會是空
userDAO.save();
}
web層:
//加載配置,獲取Spring工廠(容器)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//獲取bean對象
UserService userService = (UserService) applicationContext.getBean("userService");
//調用業務邏輯
userService.save();
小結:
IoC:是一個大的思想,將某件事情(對象創建權利等)的控制前交給第三方管理。
DI:還是IoC的思想,將對象依賴注入權利交給第三方管理。
2.5 Spring的工廠
ApplicationContext用來加載Spring框架的配置文件,來構建Spring對象的工廠對象,它被稱為Spring框架的上下文,
也被稱為Spring的容器。
ApplicationContext是BeanFactory(Bean工廠,Bean就是一個java對象)的一個子接口。
為什么不直接使用頂層接口對象來操作呢?
因為ApplicationContext是對BeanFactory的擴展,它的功能更強。
* 國際化處理
* 事件傳遞
* Bean的自動裝配
* 不同應用層的Context的實現
2.5.1 獲取Spring工廠的兩種方式
src:開發的時候,工程里的一個目錄,存放的文件,會在編譯發布后,放入classes下
- 如果
applicationContext.xml 在 src下, ClassPathXmlApplication
讀取 -
如果applicationContext.xml 在WEB-INF下,FileSystemXmlApplicationContext
讀取
三. IoC容器裝配Bean_基于XML配置方式
3.1 實例化Bean的四種方式(了解)
- 無參構造/帶參構造方式
- 靜態工廠方式
- 實例工廠方式
- FactoryBean方式
1. 無參構造/帶參構造方式:
public class Bean1 {
//無參構造
public Bean1() {
System.out.println("Bean1被創建了............無參構造");
}
}
public class Bean2 {
private Integer id;
private String name;
//提供帶參構造
public Bean2(Integer id, String name) {
System.out.println("Bean2被創建了..........帶參構造");
this.id = id;
this.name = name;
}
}
配置文件applicationContext.xml:
<!-- 無參構造實例化Bean -->
<bean id="bean1" class="com.itdream.spring.newBean.Bean1"/>
<!-- 帶參構造實例化Bean -->
<bean id="bean2" class="com.itdream.spring.newBean.Bean2">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="唐嫣"/>
</bean>
2. 靜態工廠方式 : 通過靜態工廠的靜態方法創建bean對象
要被反轉控制的Bean類:
public class Bean3 {
public Bean3() {
System.out.println("Bean3被創建了.....靜態工廠方式");
}
}
靜態工廠類提供靜態方法:
public class StaticBean3Factory {
public static Bean3 getBean3() {
//在實例化時,可以進行其它操作,例如邏輯判斷等
return new Bean3();
}
}
配置文件:
<!-- 靜態工廠方式實例化Bean -->
<bean id="bean3" class="com.itdream.spring.newBean.StaticBean3Factory" factory-method="getBean3"/>
3. 實例化工廠方式:
要被反轉控制的Bean類:
public class Bean4 {
public Bean4() {
System.out.println("Bean4被創建了.......實例化工廠方式");
}
}
實例化工廠類提供創建Bean的方法:
public class Bean4Factory {
// 實例化工廠方式創建Bean對象
public Bean4 initBean() {
// 可以在new Bean4之前進行很多的邏輯判斷
// 例如判斷new哪一個對象
return new Bean4();
}
}
配置文件:
<!-- 實例化工廠方式實例化Bean -->
<!-- 先實例化工廠,再通過實例化的工廠調用方法創建對象 -->
<bean id="bean4Factory" class="com.itdream.spring.newBean.Bean4Factory"/>
<bean id="bean4" factory-bean="bean4Factory" factory-method="initBean"/>
4. FactoryBean方式:
實現接口,實現getObject方法,返回要創建的Bean類.Spring檢查到實現了FactoryBean接口時,會在實例化
FactoryBean時自動調用getObject方法獲取Bean對象。
public class FactoryBean5 implements FactoryBean<Bean5> {
@Override
public Bean5 getObject() throws Exception {
return new Bean5();
}
<!-- FactoryBean方式實例化Bean -->
<bean id="beanFactory5" class="com.itdream.spring.newBean.FactoryBean5"/>
小結:
四種方式:
第一種最常用,第二、三、一些框架初始化的時候用的多、第四種spring底層用的多。
<!-- 實例化Bean的四種方式 -->
<!-- 無參構造實例化Bean -->
<bean id="bean1" class="com.itdream.spring.newBean.Bean1"/>
<!-- 帶參構造實例化Bean -->
<bean id="bean2" class="com.itdream.spring.newBean.Bean2">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="唐嫣"/>
</bean>
<!-- 靜態工廠方式實例化Bean -->
<bean id="bean3" class="com.itdream.spring.newBean.StaticBean3Factory" factory-method="getBean3"/>
<!-- 實例化工廠方式實例化Bean -->
<!-- 先實例化工廠,再通過實例化的工廠調用方法創建對象 -->
<bean id="bean4Factory" class="com.itdream.spring.newBean.Bean4Factory"/>
<bean id="bean4" factory-bean="bean4Factory" factory-method="initBean"/>
<!-- FactoryBean方式實例化Bean -->
<bean id="beanFactory5" class="com.itdream.spring.newBean.FactoryBean5"/>
---------------------------------------------------------------------------
BeanFactory和FactoryBean的區別?
BeanFactory(ApplicationContext):
是一個工廠(其實是構建了一個spring上下文的環境,容器),用來管理和獲取很多Bean對象.
FactoryBean:
是一個Bean生成工具,是用來獲取一種類型對象的Bean,它是構造Bean實例的一種方式。
3.2 Bean的作用域
項目開發中通常會使用:singleton 單例、 prototype多例 。
Singleton: 在一個spring容器中,對象只有一個實例。(默認值)
Prototype: 在一個spring容器中,存在多個實例,每次getBean 返回一個新的實例。
單例是默認值,如果需要單例對象,則不需要配置scope。
3.3 在xml配置Bean的初始化和銷毀方法(了解)
- 說明:Spring初始化bean或銷毀bean時,有時需要作一些處理工作,因此spring可以在創建和拆卸bean的時候調用bean的兩個生命周期方法
- init-method -- 當bean被載入到容器的時候調用init-method屬性指定的方法
- destroy-method -- 當bean從容器中刪除的時候調用destroy-method屬性指定的方法
想查看destroy-method的效果,有如下條件
1. 單例(singleton)的bean才可以手動銷毀。
2. web容器中會自動調用,但是main函數或測試用例需要手動調用(需要使用ClassPathXmlApplicationContext的close()方法)
public class LifeCycleBean {
public LifeCycleBean() {
System.out.println("實例化................");
}
public void init() {
System.out.println("初始化...............");
}
public void destroy() {
System.out.println("銷毀................");
}
}
配置文件:
<!-- 生命周期測試 -->
<bean id="lifeCycle" class="com.itdream.spring.lifecycle.LifeCycleBean" init-method="init" destroy-method="destroy"/>
測試:
3.4 Bean的屬性依賴注入
3.4.1 屬性依賴注入的三種方式
- 構造器參數注入
- setter方法屬性注入
- 接口注入(了解)
Spring 框架規范中通過配置文件配置的方式,只支持構造器參數注入和setter方法屬性注入,不支持接口注入 !
3.4.2 構造器參數注入constructor-arg
當Spring初始化Car時,如果發現有constructor-arg
標簽,會自動調用帶參構造,而不會使用無參構造。
constructor-arg
的屬性:
- name : 根據屬性名稱定位屬性
- index : 根據索引定位屬性
- type : 根據屬性的類型定位屬性
- ================================
- value : 簡單值,數字,字符串,其他對象等等
- ref : 復雜的對象(就是指bean),值:bean的引用名字
Bean類:
public class Car {
private Integer id;
private String name;
private Double price;
// 必須提供帶參構造,用于屬性注入
public Car(Integer id, String name, Double price) {
super();
this.id = id;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Car [id=" + id + ", name=" + name + ", price=" + price + "]";
}
}
配置文件applicationContext.xml:
<!-- 帶參構造屬性注入 -->
<bean id="car" class="com.itdream.spring.di.Car">
<constructor-arg index="0" value="1"/>
<constructor-arg name="name" value="寶馬"/>
<constructor-arg type="java.lang.Double" value="999999D"/>
</bean>
測試:
@Test
public void test() {
// 加載配置文件,獲取Spring工廠
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//獲取Bean對象
Car car = (Car) applicationContext.getBean("car");
System.out.println(car);
}
結果:
Car [id=1, name=寶馬, price=999999.0]
補充:
1. 定位屬性的標簽,可以混用
即:<constructor-arg index="0" name="name" type="java.lang.Double"/>
2. 自標簽的屬性賦值問題,可以使用子標簽的value,效果和value屬性一樣
如:
<constructor-arg name="name">
<value>寶馬</value>
</constructor-arg>
3.4.3.setter方法屬性注入 property【推薦】
使用的默認的構造器(new Bean()),但必須提供屬性的setter方法。
Spring創建出Bean對象,再通過setter方法屬性注入值。
兩步:在類中加入setter方法,在配置文件中使用property
Bean類:
public class Person {
private Integer pid;
private String name;
private Car car;
//必須提供setter方法,用于屬性注入
public void setPid(Integer pid) {
this.pid = pid;
}
public void setName(String name) {
this.name = name;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [pid=" + pid + ", name=" + name + ", car=" + car + "]";
}
}
配置文件applicationContext.xml:
<!-- 帶參構造屬性注入 -->
<bean id="car" class="com.itdream.spring.di.Car">
<constructor-arg index="0" value="1" />
<constructor-arg name="name" value="寶馬" />
<constructor-arg type="java.lang.Double" value="999999" />
</bean>
<!-- setter方法屬性注入 -->
<bean id="person" class="com.itdream.spring.di.Person ">
<property name="pid" value="1" />
<property name="name" value="jack" />
<property name="car" ref="car"/>
</bean>
測試:
@Test
public void test() {
// 加載配置文件,獲取Spring工廠
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//獲取Bean對象
//將創建對象與屬性注入的權利交給Spring,我們直接從容器拿到這個對象就可以了(已經包含了屬性的值)
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
結果:
Person [pid=1, name=jack, car=Car [id=1, name=寶馬, price=999999.0]]
setter方法屬性注入的屬性:
property : 用于setter方式進行屬性注入的標簽
name : 與Bean類中的setter方法對應.例如setCar --- name就是car.
value : 簡單值.
ref : 復雜值。要注入的Bean對象的id/name名字
3.4.4.p名稱空間的使用(了解)
什么是名稱空間?
作用:Schema區分同名元素。(有點類似于java的包)
xmlns="http://www.springframework.org/schema/beans"
Xmlns沒有前綴是默認的名稱空間.
p名稱空間的作用是為了簡化setter方法屬性依賴注入配置的,它不是真正的名稱空間。
它的使用方法:
p:<屬性名>="" 引入常量值
p:<屬性名>-ref =""引入其他Bean對象
具體使用步驟:
1. 引入p名稱空間
<beans xmlns="http://www.springframework.org/schema/beans"
//在這里引入p名稱空間(默認名稱空間后,在xmlns后添加:p , 將最后的beans改成p。)
xmlns:p="http://www.springframework.org/schema/p"
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">
2. 將<property> 子元素 簡化為 元素的屬性 (以上面的person為例)
<!-- P名稱空間簡化setter方法屬性注入 -->
<bean id="person" class="com.itdream.spring.di.Person" p:pid="2" p:name="coco" p:car-ref="car"/>
結果:
Person [pid=2, name=coco, car=Car [id=1, name=寶馬, price=999999.0]]
配置時不需要<property > 子元素,簡化了配置 .
3.4.5 spEL表達式的使用 –會使用
spEL(Spring Expression Language)
是一種表達式語言,它是spring3.x版本的新特性。
作用:支持在運行時操作和查詢對象,其語法類似統一的EL語言,但是SpEL提供了額外的功能,功能更強大。
語法: #{…} , 引用另一個Bean 、屬性、 方法
-
#{bean_id}
引用Bean(具體對象) -
#{bean_id.屬性}
引用Bean的屬性 -
#{bean_id.方法(參數)}
引用Bean的方法
例1:
//修改了p:pid的值為#{2*3},修改p:name為#{car.name},car是創建car對象的id。
car.name相當于調用了它的getName()方法,因此Car的Bean類中必須提供getName方法。
<bean id="person" class="com.itdream.spring.di.Person" p:pid="#{2*3}" p:name="#{car.name}" p:car-ref="car"/>
測試結果:
Person [pid=6, name=寶馬, car=Car [id=1, name=寶馬, price=999999.0]]
例2:
//修改了p:pid的值為#{car.id},修改p:name為#{car.name},car是創建car對象的id。
car.id相當于調用了它的getId()方法,因此Car的Bean類中必須提供getId方法。
<bean id="person" class="com.itdream.spring.di.Person" p:pid="#{car.id}" p:name="#{'bmw'.toUpperCase()}" p:car-ref="car"/>
測試結果:
Person [pid=1, name=BMW, car=Car [id=1, name=寶馬, price=999999.0]]
3.4.6.集合類型屬性注入 (了解-使用時查看即可)
作用:主要用于框架整合配置。
Spring為集合提供了對應的標簽:
<list> 注入 list元素
<set> 注入 set元素
<map> 注入 map元素
<props> 注入 properties 元素 (hashtable類的子類,是特殊的map,key和value都是String )
Bean類:提供四種集合,List,Map,Set,Properties,使用setter方式進行屬性注入:
public class CollectionBean {
private List<Integer> list;
private Set<String> set;
private Map<String, String> map;
private Properties properties;
// 提供setter方式,供Spring框架屬性注入
public void setList(List<Integer> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean [list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + "]";
}
}
======================================================================================
applicationContext.xml :
value注入簡單類型。 ref注入復雜類型。
<bean id="collectionBean" class="com.itdream.spring.collection.CollectionBean">
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</list>
</property>
<property name="set">
<set>
<value>aa</value>
<value>bb</value>
<value>cc</value>
<value>dd</value>
</set>
</property>
<property name="map">
<map>
<entry key="羅貫中" value="三國演義"/>
<entry key="施耐庵" value="水滸傳"/>
<entry key="曹雪芹" value="紅樓夢"/>
<entry key="吳承恩" value="西游記"/>
</map>
</property>
<property name="properties">
<props>
<prop key="霍建華">林心如</prop>
<prop key="吳奇隆">李詩詩</prop>
</props>
</property>
</bean>
測試:
// 加載配置文件,獲取Spring工廠
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//獲取Bean對象
CollectionBean collectionBean = (CollectionBean) applicationContext.getBean("collectionBean");
System.out.println(collectionBean);
結果:
CollectionBean [list=[1, 2, 3, 4], set=[aa, bb, cc, dd], map={羅貫中=三國演義, 施耐庵=水滸傳, 曹雪芹=紅樓夢, 吳承恩=西游記}, properties={霍建華=林心如, 吳奇隆=李詩詩}]
3.5.配置文件分開管理(了解)
在開發中,所有的bean不可能只寫在一個配置文件中,如果在src的目錄下又多創建了一個配置文件,現在是兩個核心的配置文件,那么加載這兩個配置文件的方式有兩種:
-
主配置文件中包含其他的配置文件:【推薦】
在applicationContext.xml中: <import resource="applicationContext2.xml"/>
工廠創建的時候直接加載多個配置文件:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml","applicationContext2.xml");