博客遷移至:https://blog.csdn.net/wangshihuidev
前言
本文主要內(nèi)容包含如下:
內(nèi)容來自于《Java EE 企業(yè)級應(yīng)用開發(fā)教程》這本書的翻讀筆記疆瑰,內(nèi)容相對簡單眉反,用于基礎(chǔ)知識的復(fù)習(xí)鞏固。
Spring的核心容器
Spring框架提供了兩種核心容器穆役,分別為BeanFactory和ApplicationContext寸五。
知識點(diǎn):BeanFactory和ApplicationContext的區(qū)別是什么?
BeanFactory
BeanFactoryBeanFactory由org.springframework.beans.facytory.BeanFactory接口定義耿币,是基礎(chǔ)類型的IoC容器梳杏,它提供了完整的IoC服務(wù)支持。簡單來說,BeanFactory就是一個管理Bean的工廠十性,它主要負(fù)責(zé)初始化各種Bean叛溢,并調(diào)用它們的生命周期方法。BeanFactory接口提供了幾個實(shí)現(xiàn)類劲适,其中最常用的是org.springframework.beans. factory.xml.XmlBeanFactory楷掉,該類會根據(jù)XML配置文件中的定義來裝配Bean。創(chuàng)建BeanFactory實(shí)例時霞势,需要提供Spring所管理容器的詳細(xì)配置信息烹植,這些信息通常采用XML文件形式來管理,其加載配置信息的語法如下愕贡。
BeanFactory beanFactory =new XmlBeanFactory(new FileSystemResource("F: /applicationContext.xml"));
ApplicationContext
ApplicationContext是BeanFactory的子接口草雕,也被稱為應(yīng)用上下文,是另一種常用的Spring核心容器固以。它由org.springframework.context. ApplicationContext接口定義墩虹,不僅包含了BeanFactory的所有功能,還添加了對國際化憨琳、資源訪問败晴、事件傳播等方面的支持。創(chuàng)建ApplicationContext接口實(shí)例栽渴,通常采用兩種方法尖坤,具體如下。
1.通過ClassPathXmlApplicationContext創(chuàng)建
2.通過FileSystemXmlApplicationContext創(chuàng)建
在使用Spring框架時闲擦,可以通過實(shí)例化其中任何一個類來創(chuàng)建ApplicationContext容器慢味。通常在Java項(xiàng)目中,會采用通過ClassPathXmlApplicationContext類來實(shí)例化ApplicationContext容器的方式墅冷,而在Web項(xiàng)目中纯路,ApplicationContext容器的實(shí)例化工作會交由Web服務(wù)器來完成。Web服務(wù)器實(shí)例化ApplicationContext容器時寞忿,通常會使用基于ContextLoaderListener實(shí)現(xiàn)的方式驰唬,此種方式只需要在web.xml中添加如下代碼。
<! -- 指定Spring配置文件的位置腔彰,多個配置文件時叫编,以逗號分隔-->
<context-param>
<param-name>contextConfigLocation</param-name>
<! -- Spring將加載spring目錄下的applicationContext.xml文件 -->
<param-value>classpath:spring/applicationContext.xml
</param-value>
</context-param>
BeanFactory和ApplicationContext兩種容器都是通過XML配置文件加載Bean的。二者的主要區(qū)別在于霹抛,如果Bean的某一個屬性沒有注入搓逾,使用BeanFacotry加載后,在第一次調(diào)用getBean()方法時會拋出異常杯拐,而ApplicationContext則在初始化時自檢霞篡,這樣有利于檢查所依賴屬性是否注入世蔗。因此,在實(shí)際開發(fā)中朗兵,通常都優(yōu)先選擇使用ApplicationContext污淋,而只有在系統(tǒng)資源較少時,才考慮使用BeanFactory余掖。
控制翻轉(zhuǎn)和依賴注入
控制翻轉(zhuǎn)概念
在使用Spring框架之后芙沥,對象的實(shí)例不再由調(diào)用者來創(chuàng)建,而是由Spring容器來創(chuàng)建浊吏,Spring容器會負(fù)責(zé)控制程序之間的關(guān)系,而不是由調(diào)用者的程序代碼直接控制救氯。這樣找田,控制權(quán)由應(yīng)用代碼轉(zhuǎn)移到了Spring容器,控制權(quán)發(fā)生了反轉(zhuǎn)着憨,這就是Spring的控制反轉(zhuǎn)墩衙。
依賴注入方式
依賴注入的實(shí)現(xiàn)方式依賴注入的作用就是在使用Spring框架創(chuàng)建對象時,動態(tài)地將其所依賴的對象注入Bean組件中甲抖,其實(shí)現(xiàn)方式通常有兩種漆改,一種是屬性setter方法注入,另一種是構(gòu)造方法注入准谚,具體介紹如下挫剑。
- 屬性setter方法注入:指IoC容器使用setter方法注入被依賴的實(shí)例。通過調(diào)用無參構(gòu)造器或無參靜態(tài)工廠方法實(shí)例化Bean后柱衔,調(diào)用該Bean的setter方法樊破,即可實(shí)現(xiàn)基于setter方法的依賴注入。
- 構(gòu)造方法注入:指IoC容器使用構(gòu)造方法注入被依賴的實(shí)例唆铐≌芷荩基于構(gòu)造方法的依賴注入通過調(diào)用帶參數(shù)的構(gòu)造方法來實(shí)現(xiàn),每個參數(shù)代表著一個依賴艾岂。
Bean的實(shí)例化
Bean的實(shí)例化在面向?qū)ο蟮某绦蛑兴成伲胍褂媚硞€對象,就需要先實(shí)例化這個對象王浴。同樣脆炎,在Spring中,要想使用容器中的Bean氓辣,也需要實(shí)例化Bean腕窥。實(shí)例化Bean有三種方式,分別為:
- 構(gòu)造器實(shí)例化(最常用)
- 靜態(tài)工廠方式實(shí)例化
- 實(shí)例工廠方式實(shí)例化筛婉。
構(gòu)造器實(shí)例化
<bean id="bean1" class="com.itheima.instance.constructor.Bean1" />
靜態(tài)工廠方式實(shí)例化
靜態(tài)工廠方式實(shí)例化使用靜態(tài)工廠是實(shí)例化Bean的另一種方式簇爆。該方式要求開發(fā)者創(chuàng)建一個靜態(tài)工廠的方法來創(chuàng)建Bean的實(shí)例癞松,其Bean配置中的class屬性所指定的不再是Bean實(shí)例的實(shí)現(xiàn)類,而是靜態(tài)工廠類入蛆,同時還需要使用factory-method屬性來指定所創(chuàng)建的靜態(tài)工廠方法响蓉。
<bean id="bean2"
class="com.itheima.instance.static_factory.MyBean2Factory"
factory-method="createBean" />
</beans>
在上述配置文件中,首先通過<bean>元素的id屬性定義了一個名稱為bean2的Bean哨毁,然后由于使用的是靜態(tài)工廠方法枫甲,所以需要通過class屬性指定其對應(yīng)的工廠實(shí)現(xiàn)類為MyBean2Factory。由于這種方式配置Bean后扼褪,Spring容器不知道哪個是所需要的工廠方法想幻,所以增加了factory-method屬性來告訴Spring容器,其方法名稱為createBean话浇。
實(shí)例工廠方式實(shí)例化
實(shí)例工廠方式實(shí)例化還有一種實(shí)例化Bean的方式就是采用實(shí)例工廠脏毯。此種方式的工廠類中,不再使用靜態(tài)方法創(chuàng)建Bean實(shí)例幔崖,而是采用直接創(chuàng)建Bean實(shí)例的方式食店。同時,在配置文件中赏寇,需要實(shí)例化的Bean也不是通過class屬性直接指向的實(shí)例化類吉嫩,而是通過factory-bean屬性指向配置的實(shí)例工廠,然后使用factory-method屬性確定使用工廠中的哪個方法嗅定。
<! -- 配置工廠 -->
<bean id="myBean3Factory"8
class="com.itheima.instance.factory.MyBean3Factory" />
<! -- 使用factory-bean屬性指向配置的實(shí)例工廠自娩,
使用factory-method屬性確定使用工廠中的哪個方法-->
<bean id="bean3" factory-bean="myBean3Factory"
factory-method="createBean" />
在上述配置文件中,首先配置了一個工廠Bean渠退,然后配置了需要實(shí)例化的Bean椒功。在id為bean3的Bean中,使用factory-bean屬性指向配置的實(shí)例工廠智什,該屬性值就是工廠Bean的id动漾。使用factory-method屬性來確定使用工廠中的createBean()方法。
Bean的生命周期
Spring容器可以管理singleton作用域的Bean的生命周期荠锭,在此作用域下旱眯,Spring能夠精確地知道該Bean何時被創(chuàng)建,何時初始化完成以及何時被銷毀证九。對于prototype作用域的Bean, Spring只負(fù)責(zé)創(chuàng)建删豺,當(dāng)容器創(chuàng)建了Bean實(shí)例后,Bean的實(shí)例就交給客戶端代碼來管理愧怜,Spring容器將不再跟蹤其生命周期呀页。每次客戶端請求prototype作用域的Bean時,Spring容器都會創(chuàng)建一個新的實(shí)例拥坛,并且不會管那些被配置成prototype作用域的Bean的生命周期蓬蝶。
了解Bean的生命周期的意義就在于尘分,可以在某個Bean生命周期的某些指定時刻完成一些相關(guān)操作。這種時刻可能有很多丸氛,但在一般情況下培愁,常會在Bean的postinitiation(初始化后)和predestruction(銷毀前)執(zhí)行一些相關(guān)操作。
Bean的生命周期的整個執(zhí)行過程描述如下缓窜。
(1)根據(jù)配置情況調(diào)用Bean構(gòu)造方法或工廠方法實(shí)例化Bean定续。
(2)利用依賴注入完成Bean中所有屬性值的配置注入。
(3)如果Bean實(shí)現(xiàn)了BeanNameAware接口禾锤,則Spring調(diào)用Bean的setBeanName()方法傳入當(dāng)前Bean的id值私股。
(4)如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,則Spring調(diào)用setBeanFactory()方法傳入當(dāng)前工廠實(shí)例的引用恩掷。
(5)如果Bean實(shí)現(xiàn)了ApplicationContextAware接口倡鲸,則Spring調(diào)用setApplicationContext()方法傳入當(dāng)前ApplicationContext實(shí)例的引用。
(6)如果BeanPostProcessor和Bean關(guān)聯(lián)螃成,則Spring將調(diào)用該接口的預(yù)初始化方法postProcessBeforeInitialzation()對Bean進(jìn)行加工操作,這個非常重要查坪,Spring的AOP就是用它實(shí)現(xiàn)的寸宏。
(7)如果Bean實(shí)現(xiàn)了InitializingBean接口,則Spring將調(diào)用afterPropertiesSet()方法偿曙。
(8)如果在配置文件中通過init-method屬性指定了初始化方法氮凝,則調(diào)用該初始化方法。
(9)如果有BeanPostProcessor和Bean關(guān)聯(lián)望忆,則Spring將調(diào)用該接口的初始化方法post ProcessAfterInitialization()罩阵。此時,Bean已經(jīng)可以被應(yīng)用系統(tǒng)使用了启摄。
(10)如果在<bean> 中指定了該Bean的作用范圍為scope="singleton"稿壁,則將該Bean放入Spring IoC的緩存池中,將觸發(fā)Spring對該Bean的生命周期管理歉备;如果在<bean>中指定了該Bean的作用范圍為scope="prototype"傅是,則將該Bean交給調(diào)用者,調(diào)用者管理該Bean的生命周期蕾羊,Spring不再管理該Bean喧笔。
(11)如果Bean實(shí)現(xiàn)了DisposableBean接口,則Spring會調(diào)用destory()方法將Spring中的Bean銷毀龟再;如果在配置文件中通過destory-method屬性指定了Bean的銷毀方法书闸,則Spring將調(diào)用該方法進(jìn)行銷毀。Spring為Bean提供了細(xì)致全面的生命周期過程利凑,通過實(shí)現(xiàn)特定的接口或通過<bean>的屬性設(shè)置浆劲,都可以對Bean的生命周期過程產(chǎn)生影響嫌术。我們可以隨意地配置<bean>的屬性,但是在這里建議不要過多地使用Bean實(shí)現(xiàn)接口梳侨,因?yàn)檫@樣會使代碼和Spring聚合比較緊密蛉威。
Bean的裝配方式
Bean的裝配方式Bean的裝配可以理解為依賴關(guān)系注入,Bean的裝配方式即Bean依賴注入的方式走哺。Spring容器支持多種形式的Bean的裝配方式蚯嫌,如
- 基于XML的裝配
- 基于注解(Annotation)的裝配
- 自動裝配
基于XML的裝配
基于XML的裝配Spring提供了兩種基于XML的裝配方式:
- 設(shè)值注入(Setter Injection)
- 構(gòu)造注入(Constructor Injection)
在Spring實(shí)例化Bean的過程中,Spring首先會調(diào)用Bean的默認(rèn)構(gòu)造方法來實(shí)例化Bean對象丙躏,然后通過反射的方式調(diào)用setter方法來注入屬性值择示。因此,設(shè)值注入要求一個Bean必須滿足以下兩點(diǎn)要求晒旅。
- Bean類必須提供一個默認(rèn)的無參構(gòu)造方法栅盲。
- Bean類必須為需要注入的屬性提供對應(yīng)的setter方法。
使用設(shè)值注入時废恋,在Spring配置文件中谈秫,需要使用<bean>元素的子元素<property>來為每個屬性注入值;而使用構(gòu)造注入時鱼鼓,在配置文件里拟烫,需要使用<bean>元素的子元素<constructor-arg>來定義構(gòu)造方法的參數(shù),可以使用其value屬性(或子元素)來設(shè)置該參數(shù)的值迄本。
基于注解(Annotation)的裝配
@Autowired:用于對Bean的屬性變量硕淑、屬性的setter方法及構(gòu)造方法進(jìn)行標(biāo)注,配合對應(yīng)的注解處理器完成Bean的自動配置工作嘉赎。默認(rèn)按照Bean的類型進(jìn)行裝配置媳。
@Resource:其作用與Autowired一樣。其區(qū)別在于@Autowired默認(rèn)按照Bean類型裝配公条,而@Resource默認(rèn)按照Bean實(shí)例名稱進(jìn)行裝配拇囊。
@Resource中有兩個重要屬性:name和type。Spring將name屬性解析為Bean實(shí)例名稱靶橱,type屬性解析為Bean實(shí)例類型寂拆。如果指定name屬性,則按實(shí)例名稱進(jìn)行裝配抓韩;如果指定type屬性纠永,則按Bean類型進(jìn)行裝配;如果都不指定谒拴,則先按Bean實(shí)例名稱裝配尝江,如果不能匹配笔链,再按照Bean類型進(jìn)行裝配岳枷;如果都無法匹配窃页,則拋出NoSuchBeanDefinitionException異常徙鱼。· @Qualifier:與@Autowired注解配合使用惭聂,會將默認(rèn)的按Bean類型裝配修改為按Bean的實(shí)例名稱裝配窗声,Bean的實(shí)例名稱由@Qualifier注解的參數(shù)指定。
自動裝配
自動裝配雖然使用注解的方式裝配Bean辜纲,在一定程度上減少了配置文件中的代碼量笨觅,但是也有企業(yè)項(xiàng)目中,是沒有使用注解方式開發(fā)的耕腾,那么有沒有什么辦法既可以減少代碼量见剩,又能夠?qū)崿F(xiàn)Bean的裝配呢?答案是肯定的扫俺,Spring的<bean>元素中包含一個autowire屬性苍苞,我們可以通過設(shè)置autowire的屬性值來自動裝配Bean。所謂自動裝配狼纬,就是將一個Bean自動地注入到其他Bean的Property中羹呵。
在默認(rèn)情況下,配置文件中需要通過ref來裝配Bean疗琉,但設(shè)置了autowire="byName"后冈欢,Spring會自動尋找userService Bean中的屬性,并將其屬性名稱與配置文件中定義的Bean做匹配没炒。由于UserServiceImpl中定義了userDao屬性及其setter方法涛癌,這與配置文件中id為userDao的Bean相匹配犯戏,所以Spring會自動地將id為userDao的Bean裝配到id為userService的Bean中送火。
Spring AOP
目前最流行的AOP框架有兩個,分別為Spring AOP和AspectJ先匪。Spring AOP使用純Java實(shí)現(xiàn)种吸,不需要專門的編譯過程和類加載器,在運(yùn)行期間通過代理方式向目標(biāo)類織入增強(qiáng)的代碼坚俗。AspectJ是一個基于Java語言的AOP框架,從Spring 2.0開始岸裙,Spring AOP引入了對AspectJ的支持猖败,AspectJ擴(kuò)展了Java語言,提供了一個專門的編譯器降允,在編譯時提供橫向代碼的織入恩闻。
動態(tài)代理
Spring中的AOP代理,可以是JDK動態(tài)代理剧董,也可以是CGLIB動態(tài)代理幢尚。
知識點(diǎn): JDK動態(tài)代理和CGLIB動態(tài)代理的區(qū)別是什么破停?
JDK動態(tài)代理
JDK動態(tài)代理JDK動態(tài)代理是通過java.lang.reflect.Proxy類來實(shí)現(xiàn)的,我們可以調(diào)用Proxy類的newProxyInstance()方法來創(chuàng)建代理對象尉剩。對于使用業(yè)務(wù)接口的類真慢,Spring默認(rèn)會使用JDK動態(tài)代理來實(shí)現(xiàn)AOP。
基本流程:
JdkProxy類實(shí)現(xiàn)了InvocationHandler接口理茎,并實(shí)現(xiàn)了接口中的invoke()方法黑界,所有動態(tài)代理類所調(diào)用的方法都會交由該方法處理。在創(chuàng)建的代理方法createProxy()中功蜓,使用了Proxy類的newProxyInstance()方法來創(chuàng)建代理對象园爷。newProxyInstance()方法中包含3個參數(shù),其中第1個參數(shù)是當(dāng)前類的類加載器式撼,第2個參數(shù)表示的是被代理對象實(shí)現(xiàn)的所有接口童社,第3個參數(shù)this代表的就是代理類JdkProxy本身。在invoke()方法中著隆,目標(biāo)類方法執(zhí)行的前后扰楼,會分別執(zhí)行切面類中的check_Permissions()方法和log()方法。
CGLIB代理
CGLIB代理JDK動態(tài)代理的使用非常簡單美浦,但它還有一定的局限性——使用動態(tài)代理的對象必須實(shí)現(xiàn)一個或多個接口弦赖。如果要對沒有實(shí)現(xiàn)接口的類進(jìn)行代理,那么可以使用CGLIB代理浦辨。CGLIB(Code Generation Library)是一個高性能開源的代碼生成包蹬竖,它采用非常底層的字節(jié)碼技術(shù),對指定的目標(biāo)類生成一個子類流酬,并對子類進(jìn)行增強(qiáng)币厕。
基本流程:
首先創(chuàng)建了一個動態(tài)類對象Enhancer,它是CGLIB的核心類芽腾;然后調(diào)用了Enhancer類的setSuperclass()方法來確定目標(biāo)對象旦装;接下來調(diào)用了setCallback()方法添加回調(diào)函數(shù),其中的this代表的就是代理類CglibProxy本身摊滔;最后通過return語句將創(chuàng)建的代理類對象返回阴绢。intercept()方法會在程序執(zhí)行目標(biāo)方法時被調(diào)用,方法運(yùn)行時將會執(zhí)行切面類中的增強(qiáng)方法艰躺。
基于代理類的AOP實(shí)現(xiàn)
在Spring中呻袭,使用Proxy FactoryBean是創(chuàng)建AOP代理的最基本方式。
Spring中的通知按照在目標(biāo)類方法的連接點(diǎn)位置腺兴,可以分為以下5種類型左电。
- MethodInterceptor(環(huán)繞通知)在目標(biāo)方法執(zhí)行前后實(shí)施增強(qiáng),可以應(yīng)用于日志、事務(wù)管理等功能券腔。
- MethodBeforeAdvice(前置通知)在目標(biāo)方法執(zhí)行前實(shí)施增強(qiáng)伏穆,可以應(yīng)用于權(quán)限管理等功能。
- AfterReturningAdvice(后置通知)在目標(biāo)方法執(zhí)行后實(shí)施增強(qiáng)纷纫,可以應(yīng)用于關(guān)閉流枕扫、上傳文件、刪除臨時文件等功能辱魁。
- ThrowsAdvice(異常通知)在方法拋出異常后實(shí)施增強(qiáng)烟瞧,可以應(yīng)用于處理異常記錄日志等功能。
- IntroductionInterceptor(引介通知)在目標(biāo)類中添加一些新的方法和屬性染簇,可以應(yīng)用于修改老版本程序(增強(qiáng)類)参滴。
ProxyFactoryBeanProxyFactoryBean是FactoryBean接口的實(shí)現(xiàn)類,F(xiàn)actoryBean負(fù)責(zé)實(shí)例化一個Bean锻弓,而ProxyFactoryBean負(fù)責(zé)為其他Bean創(chuàng)建代理實(shí)例砾赔。
首先通過<bean>元素定義了目標(biāo)類和切面,然后使用ProxyFactoryBean類定義了代理對象青灼。在定義的代理對象中暴心,分別通過<property>子元素指定了代理實(shí)現(xiàn)的接口、代理的目標(biāo)對象杂拨、需要織入目標(biāo)類的通知以及代理方式专普。
AspectJ開發(fā)
AspectJ開發(fā)AspectJ是一個基于Java語言的AOP框架,它提供了強(qiáng)大的AOP功能弹沽。Spring 2.0以后檀夹,Spring AOP引入了對AspectJ的支持,并允許直接使用AspectJ進(jìn)行編程策橘,而Spring自身的AOP API也盡量與AspectJ保持一致。新版本的Spring框架役纹,也建議使用AspectJ來開發(fā)AOP偶摔。使用AspectJ實(shí)現(xiàn)AOP有兩種方式:
- 基于XML的聲明式AspectJ
- 基于注解的聲明式AspectJ
基于XML的聲明式AspectJ
基于XML的聲明式AspectJ基于XML的聲明式AspectJ是指通過XML文件來定義切面暇唾、切入點(diǎn)及通知策州,所有的切面、切入點(diǎn)和通知都必須定義在<aop:config>元素內(nèi)毅贮。<aop:config>元素及其子元素如圖所示。
Spring配置文件中的<beans>元素下可以包含多個<aop:config>元素尘奏,一個<aop:config>元素中又可以包含屬性和子元素滩褥,其子元素包括<aop:pointcut>炫加、<aop:advisor>和<aop:aspect>瑰煎。在配置時,這3個子元素必須按照此順序來定義俗孝。在<aop:aspect>元素下酒甸,同樣包含了屬性和多個子元素,通過使用<aop:aspect>元素及其子元素就可以在XML文件中配置切面赋铝、切入點(diǎn)和通知插勤。圖中灰色部分標(biāo)注的元素即為常用的配置元素,這些常用元素的配置代碼如下所示革骨。
>> <! -- 定義切面Bean --><bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
<aop:config>
<! --1.配置切面 -->
<aop:aspect id="aspect" ref="myAspect">
<! -- 2.配置切入點(diǎn) -->
<aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))" id="myPointCut" />
<! --3.配置通知 -->
<! -- 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<! -- 后置通知 -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<! -- 環(huán)繞通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<! -- 異常通知 -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<! -- 最終通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
配置切面在Spring的配置文件中饮六,配置切面使用的是<aop:aspect>元素,該元素會將一個已定義好的Spring Bean轉(zhuǎn)換成切面Bean苛蒲,所以要在配置文件中先定義一個普通的Spring Bean(如上述代碼中定義的myAspect)卤橄。定義完成后,通過<aop:aspect>元素的ref屬性即可引用該Bean臂外。
在上述配置代碼片段中窟扑,execution(* com.itheima.jdk..(..))就是定義的切入點(diǎn)表達(dá)式,該切入點(diǎn)表達(dá)式的意思是匹配com.itheima.jdk包中任意類的任意方法的執(zhí)行漏健。其中execution()是表達(dá)式的主體嚎货,第1個表示的是返回類型,使用代表所有類型蔫浆;com.itheima.jdk表示的是需要攔截的包名殖属,后面第2個表示的是類名,使用代表所有的類瓦盛;第3個表示的是方法名洗显,使用表示所有方法;后面(..)表示方法的參數(shù)原环,其中的“..”表示任意參數(shù)挠唆。需要注意的是,第1個*與包名之間有一個空格嘱吗。
在AOP的配置信息中玄组,使用<aop:after-returning>配置的后置通知和使用<aop:after>配置的最終通知雖然都是在目標(biāo)方法執(zhí)行之后執(zhí)行,但它們也是有所區(qū)別的。后置通知只有在目標(biāo)方法成功執(zhí)行后才會被織入俄讹,而最終通知不論目標(biāo)方法如何結(jié)束(包括成功執(zhí)行和異常中止兩種情況)哆致,它都會被織入。
基于注解的聲明式AspectJ
基于注解的聲明式AspectJ與基于代理類的AOP實(shí)現(xiàn)相比患膛,基于XML的聲明式ApectJ要便捷得多沽瞭,但是它也存在著一些缺點(diǎn),那就是要在Spring文件中配置大量的代碼信息剩瓶。為了解決這個問題驹溃,AspectJ框架為AOP的實(shí)現(xiàn)提供了一套注解,用以取代Spring配置文件中為實(shí)現(xiàn)AOP功能所配置的臃腫代碼延曙。
使用<aop:aspectj-autoproxy />來啟動Spring對基于注解的聲明式AspectJ的支持豌鹤。
Spring事務(wù)管理概述
事務(wù)管理的方式Spring中的事務(wù)管理分為兩種方式:
- 編程式事務(wù)管理:是通過編寫代碼實(shí)現(xiàn)的事務(wù)管理,包括定義事務(wù)的開始枝缔、正常執(zhí)行后的事務(wù)提交和異常時的事務(wù)回滾布疙。
- 聲明式事務(wù)管理:是通過AOP技術(shù)實(shí)現(xiàn)的事務(wù)管理,其主要思想是將事務(wù)管理作為一個“切面”代碼單獨(dú)編寫愿卸,然后通過AOP技術(shù)將事務(wù)管理的“切面”代碼織入到業(yè)務(wù)目標(biāo)類中灵临。
聲明式事務(wù)管理
聲明式事務(wù)管理Spring的聲明式事務(wù)管理可以通過兩種方式來實(shí)現(xiàn):
- 基于XML的方式
- 基于Annotation的方式。
基于XML的方式
基于XML方式的聲明式事務(wù)基于XML方式的聲明式事務(wù)管理是通過在配置文件中配置事務(wù)規(guī)則的相關(guān)聲明來實(shí)現(xiàn)的趴荸。Spring 2.0以后儒溉,提供了tx命名空間來配置事務(wù),tx命名空間下提供了<tx:advice>元素來配置事務(wù)的通知(增強(qiáng)處理)发钝。當(dāng)使用<tx:advice>元素配置了事務(wù)的增強(qiáng)處理后顿涣,就可以通過編寫的AOP配置,讓Spring自動對目標(biāo)生成代理酝豪。配置<tx:advice>元素時涛碑,通常需要指定id和transaction-manager屬性,其中id屬性是配置文件中的唯一標(biāo)識孵淘,transaction-manager屬性用于指定事務(wù)管理器蒲障。除此之外,還需要配置一個<tx:attributes>子元素瘫证,該子元素可通過配置多個<tx:method>子元素來配置執(zhí)行事務(wù)的細(xì)節(jié)揉阎。<tx:advice>元素及其子元素如圖所示。
基于Annotation的方式
基于Annotation方式的聲明式事務(wù)Spring的聲明式事務(wù)管理還可以通過Annotation(注解)的方式來實(shí)現(xiàn)痛悯。這種方式的使用非常簡單余黎,開發(fā)者只需做兩件事情:
① 在Spring容器中注冊事務(wù)注解驅(qū)動重窟,其代碼如下载萌。
<tx:annotation-driven transaction-manager="transactionManager"/>
② 在需要使用事務(wù)的Spring Bean類或者Bean類的方法上添加注解@Transactional。如果將注解添加在Bean類上,則表示事務(wù)的設(shè)置對整個Bean類的所有方法都起作用扭仁;如果將注解添加在Bean類中的某個方法上垮衷,則表示事務(wù)的設(shè)置只對該方法有效。使用@Transactional注解時乖坠,可以通過其參數(shù)配置事務(wù)詳情搀突。@Transactional注解可配置的參數(shù)信息如表。
Spring MVC的工作流程
Spring MVC程序的完整執(zhí)行流程如下熊泵。
(1)用戶通過瀏覽器向服務(wù)器發(fā)送請求仰迁,請求會被Spring MVC的前端控制器DispatcherServlet所攔截。
(2)DispatcherServlet攔截到請求后顽分,會調(diào)用HandlerMapping處理器映射器徐许。
(3)處理器映射器根據(jù)請求URL找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet卒蘸。
(4)DispatcherServlet會通過返回信息選擇合適的HandlerAdapter(處理器適配器)雌隅。
(5)HandlerAdapter會調(diào)用并執(zhí)行Handler(處理器),這里的處理器指的就是程序中編寫的Controller類缸沃,也被稱之為后端控制器恰起。
(6)Controller執(zhí)行完成后,會返回一個ModelAndView對象趾牧,該對象中會包含視圖名或包含模型和視圖名检盼。
(7)HandlerAdapter將ModelAndView對象返回給DispatcherServlet。
(8)DispatcherServlet會根據(jù)ModelAndView對象選擇一個合適的ViewReslover(視圖解析器)翘单。
(9)ViewReslover解析后梯皿,會向DispatcherServlet中返回具體的View(視圖)。
(10)DispatcherServlet對View進(jìn)行渲染(即將模型數(shù)據(jù)填充至視圖中)县恕。
(11)視圖渲染結(jié)果會返回給客戶端瀏覽器顯示东羹。
Spring數(shù)據(jù)綁定流程
數(shù)據(jù)綁定介紹在執(zhí)行程序時,Spring MVC會根據(jù)客戶端請求參數(shù)的不同忠烛,將請求消息中的信息以一定的方式轉(zhuǎn)換并綁定到控制器類的方法參數(shù)中属提。這種將請求消息數(shù)據(jù)與后臺方法參數(shù)建立連接的過程就是Spring MVC中的數(shù)據(jù)綁定。在數(shù)據(jù)綁定過程中美尸,Spring MVC框架會通過數(shù)據(jù)綁定組件(DataBinder)將請求參數(shù)串的內(nèi)容進(jìn)行類型轉(zhuǎn)換冤议,然后將轉(zhuǎn)換后的值賦給控制器類中方法的形參,這樣后臺方法就可以正確綁定并獲取客戶端請求攜帶的參數(shù)了师坎。
(1)Spring MVC將ServletRequest對象傳遞給DataBinder恕酸。
(2)將處理方法的入?yún)ο髠鬟f給DataBinder。
(3)DataBinder調(diào)用ConversionService組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換胯陋、數(shù)據(jù)格式化等工作蕊温,并將ServletRequest對象中的消息填充到參數(shù)對象中袱箱。
(4)調(diào)用Validator組件對已經(jīng)綁定了請求消息數(shù)據(jù)的參數(shù)對象進(jìn)行數(shù)據(jù)合法性校驗(yàn)。
(5)校驗(yàn)完成后會生成數(shù)據(jù)綁定結(jié)果BindingResult對象义矛,Spring MVC會將BindingResult對象中的內(nèi)容賦給處理方法的相應(yīng)參數(shù)发笔。
Spring JSON數(shù)據(jù)交互
JSON數(shù)據(jù)轉(zhuǎn)換為了實(shí)現(xiàn)瀏覽器與控制器類(Controller)之間的數(shù)據(jù)交互,Spring提供了一個HttpMessageConverter<T>接口來完成此項(xiàng)工作凉翻。該接口主要用于將請求信息中的數(shù)據(jù)轉(zhuǎn)換為一個類型為T的對象了讨,并將類型為T的對象綁定到請求方法的參數(shù)中,或者將對象轉(zhuǎn)換為響應(yīng)信息傳遞給瀏覽器顯示制轰。Spring為HttpMessageConverter<T>接口提供了很多實(shí)現(xiàn)類前计,這些實(shí)現(xiàn)類可以對不同類型的數(shù)據(jù)進(jìn)行信息轉(zhuǎn)換。其中MappingJackson2HttpMessageConverter是Spring MVC默認(rèn)處理JSON格式請求響應(yīng)的實(shí)現(xiàn)類垃杖。該實(shí)現(xiàn)類利用Jackson開源包讀寫JSON數(shù)據(jù)残炮,將Java對象轉(zhuǎn)換為JSON對象和XML文檔,同時也可以將JSON對象和XML文檔轉(zhuǎn)換為Java對象缩滨。
在使用注解式開發(fā)時势就,需要用到兩個重要的JSON格式轉(zhuǎn)換注解,分別為@RequestBody和@ResponseBody脉漏,關(guān)于這兩個注解的說明如表所示苞冯。
Spring RESTful支持
什么是RESTful?RESTful也稱之為REST(Representational State Transfer)侧巨,可以將它理解為一種軟件架構(gòu)風(fēng)格或設(shè)計(jì)風(fēng)格舅锄,而不是一個標(biāo)準(zhǔn)。簡單來說司忱,RESTful風(fēng)格就是把請求參數(shù)變成請求路徑的一種風(fēng)格皇忿。例如,傳統(tǒng)的URL請求格式為:http://.../queryItems? id=1而采用RESTful風(fēng)格后坦仍,其URL請求為:http://.../items/1從上述兩個請求中可以看出鳍烁,RESTful風(fēng)格中的URL將請求參數(shù)id=1變成了請求路徑的一部分,并且URL中的queryItems也變成了items(RESTful風(fēng)格中的URL不存在動詞形式的路徑繁扎,如queryItems表示查詢訂單幔荒,是一個動詞,而items表示訂單梳玫,為名詞)爹梁。
/** *接收RESTful風(fēng)格的請求,其接收方式為GET */
@RequestMapping(value="/user/{id}",
method=RequestMethod.GET)
@ResponseBodypublic User selectUser(@PathVariable("id") String id){
User user=new User();
...
//返回JSON格式的數(shù)據(jù)
return user;
}
在上述代碼中提澎,@RequestMapping(value="/user/{id}", method=RequestMethod.GET)注解用于匹配請求路徑(包括參數(shù))和方式姚垃。其中value="/user/{id}"表示可以匹配以“/user/{id}”結(jié)尾的請求,id為請求中的動態(tài)參數(shù)盼忌;method=RequestMethod.GET表示只接收GET方式的請求积糯。方法中的@PathVariable("id")注解則用于接收并綁定請求參數(shù)掂墓,它可以將請求URL中的變量映射到方法的形參上,如果請求路徑為“/user/{id}”絮宁,即請求參數(shù)中的id和方法形參名稱id一樣梆暮,則@PathVariable后面的“("id")”可以省略服协。
Spring 攔截器
攔截器概述Spring MVC中的攔截器(Interceptor)類似于Servlet中的過濾器(Filter)绍昂,它主要用于攔截用戶請求并做相應(yīng)的處理。例如通過攔截器可以進(jìn)行權(quán)限驗(yàn)證偿荷、記錄請求信息的日志窘游、判斷用戶是否登錄等。
攔截器的定義要使用Spring MVC中的攔截器跳纳,就需要對攔截器類進(jìn)行定義和配置忍饰。通常攔截器類可以通過兩種方式來定義。一種是通過實(shí)現(xiàn)HandlerInterceptor接口寺庄,或繼承HandlerInterceptor接口的實(shí)現(xiàn)類(如HandlerInterceptorAdapter)來定義艾蓝;另一種是通過實(shí)現(xiàn)WebRequestInterceptor接口,或繼承WebRequestInterceptor接口的實(shí)現(xiàn)類來定義斗塘。
HandlerInterceptor三個方法的具體描述如下:
- preHandler()方法:該方法會在控制器方法前執(zhí)行赢织,其返回值表示是否中斷后續(xù)操作。當(dāng)其返回值為true時馍盟,表示繼續(xù)向下執(zhí)行于置;當(dāng)其返回值為false時,會中斷后續(xù)的所有操作(包括調(diào)用下一個攔截器和控制器類中的方法執(zhí)行等)贞岭。
- postHandle()方法:該方法會在控制器方法調(diào)用之后八毯,且解析視圖之前執(zhí)行∶榻埃可以通過此方法對請求域中的模型和視圖做出進(jìn)一步的修改话速。
- afterCompletion()方法:該方法會在整個請求完成,即視圖渲染結(jié)束之后執(zhí)行芯侥∧蚩祝可以通過此方法實(shí)現(xiàn)一些資源清理、記錄日志信息等工作筹麸。
單個攔截器的執(zhí)行流程
單個攔截器的執(zhí)行流程在運(yùn)行程序時活合,攔截器的執(zhí)行是有一定順序的,該順序與配置文件中所定義的攔截器的順序相關(guān)物赶。如果在項(xiàng)目中只定義了一個攔截器白指,那么該攔截器在程序中的執(zhí)行流程如圖所示。
程序首先會執(zhí)行攔截器類中的preHandle()方法酵紫,如果該方法的返回值為true告嘲,則程序會繼續(xù)向下執(zhí)行處理器中的方法错维,否則將不再向下執(zhí)行;在業(yè)務(wù)處理器(即控制器Controller類)處理完請求后橄唬,會執(zhí)行postHandle()方法赋焕,然后會通過DispatcherServlet向客戶端返回響應(yīng);在DispatcherServlet處理完請求后仰楚,才會執(zhí)行afterCompletion()方法隆判。
多個攔截器的執(zhí)行流程
在大型的企業(yè)級項(xiàng)目中,通常不會只有一個攔截器僧界,開發(fā)人員可能會定義很多攔截器來實(shí)現(xiàn)不同的功能侨嘀。那么多個攔截器的執(zhí)行順序又是怎樣的呢?下面通過一張圖來描述多個攔截器的執(zhí)行流程(假設(shè)有兩個攔截器Interceptor1和Interceptor2捂襟,并且在配置文件中咬腕,Interceptor1攔截器配置在前)。
當(dāng)有多個攔截器同時工作時葬荷,它們的preHandle()方法會按照配置文件中攔截器的配置順序執(zhí)行涨共,而它們的postHandle()方法和afterCompletion()方法則會按照配置順序的反序執(zhí)行。