Spring簡介
Spring是開源的控制反轉(Ioc)和麵向切面編程(AOP)的容器框架,Spring的主要功能用於默認單例模式管理Bean對象、生產Bean、聲明式事務、以及AOP開發。
Spring的Ioc類圖如下所示:
在Spring中主要的核心類和接口層,也是下面文章重點講解的核心知識,如下幾個:
- BeanFactory:Bean工廠頂層接口,生產任意的Bean。
- ApplicationContext:配置對象Bean的接口。
- ClassPathXmlApplicationContext:加載類路徑下的配置文件中的Bean。
- FileSystemXmlApplicationContext:用於加載系統文件中的配置文件中的Bean。
控制反轉(Ioc)
控制反轉由之前需要類的對象,程序員主動new(實例化)類的對象,現在將對象交給spring的工廠進行管理,現在可以讓spring生產對象,程序員只需要獲取對象使用。
要將Bean對象交給Spring工廠進行管理,首先需要在類路徑下,新建一個文件為applicationContext.xml,如下所示:
<code><
beans
xmlns
="http://www.springframework.org/schema/beans"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
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-4.0.xsd"
><
bean
id
="userService"
class
="com.ldc.org.service.impl.UserServiceImpl"
>bean
>beans
>/<code>
Spring中默認名稱為applicationContext.xml,並且配置文件的默認位置為/WEB-INF/applicationContext.xml。要想獲得上面配置的Bean實例,通過以下代碼就可以獲得:
<code> BeanFactory bf =new
ClassPathXmlApplicationContext("applicationContext.xml"
) UserService userService = bf.getBean("userService"
);/<code>
Spring的IOC容器的工作模式看做是工廠模式的昇華,可以把IOC容器看作是一個工廠,這個工廠裡要生產的對象都在配置文件中給出定義,然後利用編程語言的的反射編程,根據配置文件中給出的類名生成相應的對象。
從實現來看,IOC是把以前在工廠方法裡寫死的對象生成代碼,改變為由配置文件來定義,也就是把工廠和對象生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。
IOC中核心的技術就是反射(Reflection)編程,簡而言之就是根據給出的全類名(字符串方式)來動態地生成對象。這種編程方式可以讓對象在運行時才決定到底是哪一種對象。
Spring的配置文件的內容沒有人會去記住它,很多時候,都是在原來的已有的項目中直接粘貼配置文件過來複用,若是第一個項目,這些配置文件都是在自己收藏的基礎上進行使用。
依賴注入(DI)
依賴注入:Dependency Injection(DI)與控制反轉(IoC),不同角度但是同一個概念。首先我們理解一點在傳統方式中我們使用new的方式來創建一個對象,這會造成對象與被實例化的對象之間的耦合性增加以致不利於維護代碼。
在spring框架中對象實例改由spring框架創建,spring容器負責控制程序之間的關係,這就是spring的控制反轉。在spring容器的角度看來,spring容器負責將被依賴對象賦值給成員變量,這相當於為實例對象注入了它所依賴的實例,這是spring的依賴注入。
依賴注入配置文件的方式:
<code>public
class
UserServiceImpl
implements
UserService
{private
UserDao userDao;public
void
setUserDao(UserDao userDao){this
.UserDao = userDao; } }/<code>
配置文件中的配置:
<code>"userService"class
="com.ldc.org.service.impl.UserServiceImpl"
><
property
name
="userDao"
ref
="userDao"
>property
>bean
><
bean
id
="userDao"
class
="com.ldc.org.dao.UserDao"
>bean
>/<code>
實際中成員屬性的依賴注入很少使用配置文件的方式,直接使用註解的方式進行注入,如下所示:
<code>public
class
UserServiceImpl
implements
UserService
{private
UserDao userDao; }/<code>
屬性的依賴注入主要分為:手動注入和自動注入。
- 屬性手動注入:基於xml配置注入
- 構造方法注入
- set方法注入
- 集合注入
- 對象手動注入:基於xml配置注入
- 默認構造注入
- 靜態工廠注入
- 對象工廠注入
(1)構造方法注入
<code><
bean
id
="helloBeanByIndex"
class
="com.lyc.cn.day04.HelloImpl"
><
constructor-arg
index
="0"
value
="小張"
/><
constructor-arg
index
="1"
value
="3"
/>bean
><
bean
id
="helloBeanByName"
class
="com.lyc.cn.day04.HelloImpl"
><
constructor-arg
name
="name"
value
="小王"
/><
constructor-arg
name
="age"
value
="5"
/>bean
>/<code>
(2)set方法注入
<code><
bean
id
="user"
class
="com.ldc.org.domain.User"
><
property
name
="name"
value
="小明"
/><
property
name
="age"
value
="3"
/>bean
>/<code>
(3)集合注入
<code><
bean
id
="user"
class
="com.ldc.org.domain.User"
><
property
name
="listNames"
><
list
value-type
="java.lang.String"
merge
="false"
><
value
>張三value
><
value
>李四value
><
value
>王五value
>list
>property
><
property
name
="setNames"
><
set
value-type
="java.lang.String"
merge
="true"
><
value
>張三value
><
value
>李四value
><
value
>王五value
>set
>property
><
property
name
="mapNames"
><
map
key-type
="java.lang.String"
value-type
="java.lang.String"
><
entry
key
="name"
value
="小明"
/><
entry
key
="age"
value
="3"
/>map
>property
><
property
name
="arrayNames"
><
array
value-type
="java.lang.String"
><
value
>張三value
><
value
>李四value
><
value
>王五value
>array
>property
><
property
name
="propertiesNames"
><
props
value-type
="java.lang.String"
><
prop
key
="name"
>小明prop
><
prop
key
="age"
>3prop
>props
>property
>bean
>/<code>
(4)默認構造注入
<code><
bean
id
="userController"
class
="com.ldc.org.controller.UserController"
>bean
>/<code>
(5)靜態工廠注入
<code><
bean
id
="myFactory"
class
="com.ldc.org.bean.MyFactory"
factory-method
="getBean"
>bean
>/<code>
(6)對象工廠注入
<code>"myFactory"class
="com.ldc.org.bean.MyFactory"
>bean
><
bean
id
="beanFactory"
class
="com.ldc.org.bean.BeanFactory"
factory-bean
="myFactory"
factory-method
="getBean"
>bean
>/<code>
- 自動注入:基於註解方式注入
- @Value
- @Autowired
- @Autowired和@Qualifier("名稱")結合使用
- @Resource(name="名稱")
- 按名稱注入
- 按類型注入
- 普通屬性的注入
相關注解可以給私有字段設置,也可以給setter方法設置。
bean的種類
- 基本bean:
- FactoryBean:是一個bean,是創建特定bean對象的工廠bean。
- BeanFactory:是一個factory,可以創建任意bean對象的工廠。
bean作用域
- singleton 單例 (默認)
- prototype 多例
- request一次請求
- session一次會話
- globalSession 全局會話
bean生命週期
- init-method:初始化的時候執行方法
- destroy-method:指定銷燬的時候執行的方法
注意:要關閉ApplicationContext對象,才會調用destory方法,只有ClassPathXmlApplicationContext才有close方法。
面向切面(AOP)
AOP(面向切面編程),可以說是OOP(面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構,用以模擬公共行為的一個集合。
當我們需要為分散的對象引入公共行為的時候,OOP則顯得無能為力。也就是說,OOP允許你定義從上到下的關係,但並不適合定義從左到右 的關係。
例如日誌功能。日誌代碼往往水平地散佈在所有對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其他類型的代碼,如安全性、異常處理和透明的持續性也是如此。
這種散佈在各處的無關的代碼被稱為橫切(cross-cutting)代碼,在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。
而AOP技術則恰恰相反,它利用一種稱為橫切技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行為封裝到一個可重用模塊,並將其名為Aspect,即切面。
所謂切面,簡單地說,就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊間的耦合度,並有利於未來的可操作性和
可維護性。AOP代表的是一個橫向的關係,如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行為;那麼面向方面編程的方法,就彷彿一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息,然後它又以巧奪天工的妙手將這些剖開的切面復原,不留痕跡。
AOP的基本概念
- Aspect(切面):Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含著一些 Pointcut 以及相應的Advice。
- Joint point(連接點):表示在程序中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程序塊的執行等等,它自身還可以嵌套其它joint point。
- Pointcut(切點):表示一組 joint point,這些 joint point或是通過邏輯關係組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
- Advice(增強):Advice 定義了在 Pointcut 裡面定義的程序點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之後還是代替執行的代碼。
- Target(目標對象):織入 Advice 的目標對象.。
- Weaving(織入):將 Aspect 和其他對象連接起來, 並創建Adviced object 的過程
通知方法:
- 前置通知:在我們執行目標方法之前運行(@Before)
- 後置通知:在我們目標方法運行結束之後 ,不管有沒有異常(@After)
- 返回通知:在我們的目標方法正常返回之後運行(@AfterReturning)
- 異常通知:在我們的目標方法出現異常後運行(@AfterThrowing)
- 環繞通知:動態代理, 需要手動執行joinPoint.procced()(其實就是執行我們的目標方法執行之前相當於前置通知,執行之後就相當於我們後置通知(@Around)
AOP代碼示範
下面以一個AOP日誌功能的例子進行代碼的演示,具體的代碼如下所示:
<code>public
class
LogAspect
{public
void pointCut(){};public
void logStart(){ System.out
.println("除法運行....參數列表是:{}"
); }public
void logEnd(){ System.out
.println("除法結束......"
); }public
void logReturn(){ System.out
.println("除法正常返回......運行結果是:{}"
); }public
void logException(){ System.out
.println("異常通知"
); }public
Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out
.println("執行目標方法之前"
); Object obj = proceedingJoinPoint.proceed(); System.out
.println("@Arount:執行目標方法之後..."
);return
obj; } }/<code>
@Pointcut("execution(* com.savage.aop.Calculator .*(..))"),括號中各個pattern分別表示:
- 第一個*表示返回值匹配,可以為*表示任何返回值, 全路徑的類名等
- com.savage.aop表示類路徑匹配
- 第二個*表示方法名匹配,可以指定方法名 或者*代表所有, set* 代表以set開頭的所有方法
- (..)表示參數匹配:可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用""來表示匹配任意類型的參數,".."表示零個或多個任意參數。如(String)表示匹配一個String參數的方法;(,String)表示匹配有兩個參數的方法,第一個參數 可以是任意類型,而第二個參數是String類型。異常類型匹配(throws-pattern?)
目標方法:
<code>public
class
Calculator
{public
int
div
(int
i,int
j){ System.out
.println("--------"
);return
i/j; } }/<code>
配置類:
<code>public
class
MyAspectConfig
{public
Calculatorcalculator
()
{return
new
Calculator(); }public
LogAspectlogAspects
()
{return
new
LogAspect (); } }/<code>
測試類:
<code>public
class
TestLogAop
{public
void
test
()
{ AnnotationConfigApplicationContext app =new
AnnotationConfigApplicationContext(MyAspectConfig.
class
); Calculator c = app.getBean(Calculator.
class
);int
result = c.div(4
,3
); System.out.println(result); app.close(); } }/<code>