Spring
[TOC]
1. 控制反轉(zhuǎn)(Inversion of Control菱皆,IoC)
是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則烤送,降低代碼之間的耦合度樱拴。最常見的實(shí)現(xiàn)方式叫做依賴注入(Dependency Injection寝凌,DI),還有依賴查找(Dependency Lookup)
1.1 依賴注入(dependency injection顽悼,DI)
依賴:a類中的某個(gè)屬性是其他類拱撵,或構(gòu)造方法中傳入了其他類辉川,就叫a依賴了這個(gè)類
作用:DI所帶來的最大收益——松耦合。如果一個(gè)對(duì)象只通過接口(而不是具體實(shí)現(xiàn)或初始化過程)來表明依賴關(guān)系拴测,那么這種依賴就能夠在對(duì)象本身毫不知情的情況下乓旗,用不同的具體實(shí)現(xiàn)進(jìn)行替換。
實(shí)現(xiàn):創(chuàng)建應(yīng)用組件之間協(xié)作的行為通常稱為裝配(wiring)集索。
(1)Spring有三種裝配bean的方式
xml 屿愚,annotation(注解)和javaconfig**
① 配置方式裝配: 配置方式
? 配合setter方法注入<property>
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter方法注入使用更簡(jiǎn)潔的ref屬性 -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
? 配合構(gòu)造方法注入<constructor-arg>
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- 構(gòu)造函數(shù)注入使用更簡(jiǎn)潔的ref屬性 -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
②注解方式裝配:
- @Component:可以用于注冊(cè)所有bean
- @Repository:主要用于注冊(cè)dao層的bean
- @Controller:主要用于注冊(cè)控制層的bean
- @Service:主要用于注冊(cè)服務(wù)層的bean
③JavaConfig裝配:
@Configuration只能標(biāo)記在類上,表示該類為JavaConfig類务荆,使其可以被Spring IOC容器掃描識(shí)別并創(chuàng)建Bean加入到容器中妆距。相當(dāng)于以往的一個(gè) 文件。
@Bean只能標(biāo)記在方法上函匕,表示該方法返回一個(gè)Spring Bean娱据,可以被IOC容器托管,相當(dāng)于以前在 文件中寫的<bean/>元素盅惜。
@Configuration
public class AppConfig{
@Bean
public MyBean myBean(){
// instantiate, configure and return bean ...
return new MyBean();
}
}
通過使用AnnotationConfigApplicationContext取代原來的ClassPath ApplicationContext中剩,可以達(dá)到使用JavaConfig替代 Config的目的。
(2)注入方式:
①基于構(gòu)造方式注入Constructor-based Dependency Injection
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// 用構(gòu)造函數(shù)以便Spring容器可以注入依賴
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// 省略... business logic that actually uses the injected MovieFinder is omitted...
}
②基于setter方法注入Setter-based Dependency Injection
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// 省略... business logic that actually uses the injected MovieFinder is omitted...
}
用構(gòu)造器參數(shù)實(shí)現(xiàn)強(qiáng)制依賴抒寂,setter方法實(shí)現(xiàn)可選依賴结啼。
③注解方式注入
@Autowired
默認(rèn)使用byType,匹配不到bean時(shí)再使用byName屈芜,此時(shí)的name是屬性的名( 中的byName是根據(jù)set方法名)郊愧。可以使用@Qualifier()
指定bean的id井佑。默認(rèn)情況下必須要求依賴對(duì)象必須存在属铁,如果要允許null值,可以設(shè)置它的required屬性為false躬翁,如:@Autowired(required=false) 焦蘑。
@Resource
默認(rèn)使用byName,當(dāng)找不到與名稱匹配的bean時(shí)才按照類型進(jìn)行裝配姆另。但是需要注意的是喇肋,如果name屬性一旦指定坟乾,就只會(huì)按照名稱進(jìn)行裝配迹辐。可以@Resource(name = / type =)
指定name與type甚侣。
(3)自動(dòng)裝配:
定義:
僅僅需要在類的定義中提供依賴明吩,取消在spring配置文件中的描述。
優(yōu)點(diǎn):
①自動(dòng)裝配可以顯著減少指定屬性或構(gòu)造函數(shù)參數(shù)的需要
②自動(dòng)裝配可以隨著對(duì)象的發(fā)展更新配置殷费。
缺點(diǎn):
①屬性和構(gòu)造參數(shù)設(shè)置中的顯式依賴關(guān)系總是覆蓋自動(dòng)裝配印荔。
②自動(dòng)裝配不如顯式布線精確低葫。spring管理對(duì)象之間的關(guān)系不再被明確地記錄。
③如果沒有可用的唯一bean定義仍律,則拋出異常嘿悬。
方式:
使用<bean />
元素的autowire屬性為bean定義指定自動(dòng)裝配模式
例如<bean id="" class="" autowire="byType|byName|constructor|default" />
Mode | Explanation |
---|---|
no |
(Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. (默認(rèn))沒有自動(dòng)裝配。Bean引用必須由ref元素定義水泉。對(duì)于較大的部署善涨,不建議更改默認(rèn)設(shè)置,因?yàn)轱@式地指定collaborator可以提供更好的控制和清晰度草则。在某種程度上钢拧,它記錄了系統(tǒng)的結(jié)構(gòu)。 |
byName |
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property. 按屬性名稱自動(dòng)裝配炕横。Spring尋找與需要自動(dòng)實(shí)現(xiàn)的屬性同名的bean源内。例如,如果一個(gè)bean定義按名稱設(shè)置為autowire份殿,并且它包含一個(gè)master屬性(也就是說膜钓,它有一個(gè)setMaster(..)方法),那么Spring將查找一個(gè)名為master的bean定義伯铣,并使用它來設(shè)置該屬性呻此。 |
byType |
Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set). 如果容器中恰好存在該屬性類型的一個(gè)bean,則允許該屬性自動(dòng)實(shí)現(xiàn)腔寡。如果存在多個(gè)焚鲜,就會(huì)拋出一個(gè)致命異常,這表明您不能對(duì)該bean使用byType自動(dòng)裝配放前。如果沒有匹配的bean忿磅,則什么也不會(huì)發(fā)生(沒有設(shè)置屬性)。 |
constructor |
Analogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. 類似于byType凭语,但適用于構(gòu)造函數(shù)參數(shù)葱她。如果容器中沒有構(gòu)造函數(shù)參數(shù)類型的確切bean,就會(huì)引發(fā)致命錯(cuò)誤似扔。 |
(4)懶加載:
目的:
? 默認(rèn)情況下吨些,作為初始化過程的一部分,ApplicationContext實(shí)現(xiàn)會(huì)急切地創(chuàng)建和配置所有的單例bean炒辉。通常豪墅,這種預(yù)實(shí)例化是可取的,因?yàn)榕渲没蛑車h(huán)境中的錯(cuò)誤是立即發(fā)現(xiàn)的黔寇,而不是幾小時(shí)甚至幾天后發(fā)現(xiàn)的偶器。當(dāng)這種行為不合適時(shí),可以通過將bean定義標(biāo)記為延遲初始化來防止單例bean的預(yù)實(shí)例化。延遲初始化的bean告訴IoC容器在第一次請(qǐng)求bean實(shí)例時(shí)(而不是在啟動(dòng)時(shí))創(chuàng)建bean實(shí)例屏轰。
實(shí)現(xiàn):
? 在 中颊郎,懶加載由
<bean />
元素上的lazy-init屬性控制,如以下示例所示:
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
// 設(shè)置容器級(jí)別的懶加載(所有bean都懶加載)
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
注意:
? 當(dāng)延遲初始化的bean是未延遲初始化的單例bean的依賴項(xiàng)時(shí)霎苗,ApplicationContext在啟動(dòng)時(shí)(而不是第一次請(qǐng)求時(shí))就創(chuàng)建延遲初始化的bean姆吭,因?yàn)樗仨殱M足單例的依賴項(xiàng)。
(5)Bean作用域:
Scope | Description |
---|---|
singleton 單例模式 | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. (默認(rèn)情況下)將每個(gè)Spring IoC容器的單個(gè)bean定義定位到單個(gè)對(duì)象實(shí)例唁盏。 |
prototype 原型模式 | Scopes a single bean definition to any number of object instances. 將單個(gè)bean定義作用于任意數(shù)量的對(duì)象實(shí)例猾编。 |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . 將單個(gè)bean定義定位到單個(gè)HTTP請(qǐng)求的生命周期。也就是說升敲,每個(gè)HTTP請(qǐng)求都有它自己的bean實(shí)例答倡,該實(shí)例是在單個(gè)bean定義的后面創(chuàng)建的。僅在支持web的Spring ApplicationContext上下文中有效驴党。 |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . 將單個(gè)bean定義作用于HTTP會(huì)話的生命周期瘪撇。僅在支持web的Spring ApplicationContext上下文中有效。 |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . 將單個(gè)bean定義作用于ServletContext的生命周期港庄。僅在支持web的Spring ApplicationContext上下文中有效倔既。 |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . 將單個(gè)bean定義作用于WebSocket的生命周期。僅在支持web的Spring ApplicationContext上下文中有效鹏氧。 |
①單例模式
定義:
? 當(dāng)定義一個(gè)bean并且其作用域?yàn)閱卫龝r(shí)渤涌,Spring IoC容器將為該bean所定義的對(duì)象創(chuàng)建一個(gè)且只有一個(gè)實(shí)例。 該單個(gè)實(shí)例存儲(chǔ)在此類單例bean的高速緩存中把还,并且對(duì)該命名bean的所有后續(xù)請(qǐng)求和引用都返回該高速緩存的對(duì)象实蓬。
實(shí)現(xiàn):
<!-- 單例作用域是默認(rèn)的,可不加scope="singleton" -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
@Scope("singleton ")
②原型模式
定義:
? bean部署的非單例原型作用域?qū)е旅看螌?duì)特定bean發(fā)出請(qǐng)求時(shí)都創(chuàng)建一個(gè)新的bean實(shí)例吊履。應(yīng)該對(duì)所有有狀態(tài)bean使用原型作用域安皱,對(duì)無狀態(tài)bean使用單例作用域
實(shí)現(xiàn):
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
注意:
? Spring不管理原型bean的完整生命周期。容器實(shí)例化艇炎、配置和組裝原型對(duì)象并將其交給客戶端酌伊,而不進(jìn)一步記錄該原型實(shí)例。
? 當(dāng)bean的生命周期不同時(shí)就會(huì)出現(xiàn)問題缀踪。假設(shè)單例bean A需要使用非單例(原型)bean B居砖,可能在對(duì)A的每次方法調(diào)用上都是如此:容器只創(chuàng)建一次單例bean A,因此只獲得一次設(shè)置屬性的機(jī)會(huì)驴娃。容器不能在每次需要bean B的時(shí)候都向bean A提供一個(gè)新的實(shí)例奏候。
? 一個(gè)解決辦法是放棄一些控制反轉(zhuǎn)⊥锌可以通過實(shí)現(xiàn)applicationcontexts taware接口鼻由,以及在每次bean A需要bean B實(shí)例時(shí)暇榴,通過對(duì)容器進(jìn)行g(shù)etBean(“B”)調(diào)用來讓bean A接收容器厚棵。
③其他模式
? 請(qǐng)求蕉世、會(huì)話、應(yīng)用程序和websocket作用域只有在使用web感知的Spring ApplicationContext實(shí)現(xiàn)(如 WebApplicationContext)時(shí)才可用婆硬。
(6)Bean生命周期的回調(diào):
三種初始化bean生命周期的方式:
- 使用帶有
@PostConstruct
注解的方法- 實(shí)現(xiàn)
InitializingBean
接口的afterPropertiesSet()
方法- 自定義配置一個(gè)
init()
方法狠轻,通過init-method="init"
屬性三種銷毀bean生命周期的方式:
- 使用帶有
@PreDestroy注解的方法
- 實(shí)現(xiàn)
DisposableBean
接口的destroy()
方法- 自定義配置一個(gè)
destroy()
方法,通過destroy-method="cleanup"
屬性
方式一彬犯、方式二
①初始化回調(diào)函數(shù)
// 方式一
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// 一些初始化工作
}
}
// 方式二 不建議使用
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// 一些初始化工作
}
}
方式一通過
init-method="init"
屬性向楼,方式二通過InitializingBean
接口的afterPropertiesSet
方法,會(huì)使Spring與Java代碼耦合谐区,建議使用方式一湖蜕。建議不要使用InitializingBean接口,因?yàn)樗槐匾貙⒋a與Spring結(jié)合在一起宋列。另外昭抒,建議使用@PostConstruct注解 或指定POJO初始化方法。在基于 的配置元數(shù)據(jù)的情況下炼杖,可以使用init-method屬性指定具有void無參方法的名稱灭返。使用Java配置,可以使用@Bean的initMethod屬性坤邪。
②銷毀回調(diào)函數(shù)
//DisposableBean接口下的 destroy()熙含,不建議使用
void destroy() throws Exception;
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
建議不要使用DisposableBean回調(diào)接口,因?yàn)樗槐匾貙⒋a耦合到Spring艇纺。 另外怎静,建議使用@PreDestroy注釋或指定bean定義支持的通用方法。 使用基于 的配置元數(shù)據(jù)時(shí)黔衡,可以在<bean/>上使用destroy-method屬性消约。 通過Java配置,可以使用@Bean的destroyMethod屬性员帮。
方式三
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// 初始化時(shí)加載緩存
}
@PreDestroy
public void clearMovieCache() {
// 銷毀時(shí)清除緩存
}
}
2.面向切面編程(aspect-oriented programming或粮,AOP)
作用:AOP能夠使系統(tǒng)服務(wù)(例如:日志模塊、安全模塊捞高、事務(wù)管理)模塊化氯材,并以聲明的方式將它們應(yīng)用到它們需要影響的組件中去。所造成的結(jié)果就是業(yè)務(wù)組件會(huì)具有更高的內(nèi)聚性并且會(huì)更加關(guān)注自身的業(yè)務(wù)硝岗,完全不需要了解涉及系統(tǒng)服務(wù)所帶來復(fù)雜性氢哮。
實(shí)現(xiàn):使用了Spring的aop配置命名空間把系統(tǒng)服務(wù) bean聲明為一個(gè)切面。首先型檀,需要把系統(tǒng)服務(wù)聲明為一個(gè)bean冗尤,然后 在 < aop:aspect > 元素中引用該bean。
2.1 AOP概念
Aspect
切面Join point
連接點(diǎn)(指目標(biāo)對(duì)象的方法執(zhí)行)Pointcut
切入點(diǎn)(連接點(diǎn)的集合)Target object
目標(biāo)對(duì)象(原始對(duì)象)AOP proxy
代理對(duì)象(增加了切面邏輯的原始對(duì)象)Weaving
織入(將切面的邏輯加入到目標(biāo)對(duì)象)Advice
通知(切面在特定連接點(diǎn)上采取的操作)
2.2 AOP功能
2.3 AOP代理
Spring AOP默認(rèn)為AOP代理使用標(biāo)準(zhǔn)JDK動(dòng)態(tài)代理。這允許代理任何接口(或一組接口)裂七。
Spring AOP也可以使用CGLIB代理皆看。這對(duì)于代理類而不是接口是必要的。默認(rèn)情況下背零,如果業(yè)務(wù)對(duì)象沒有實(shí)現(xiàn)接口腰吟,則使用CGLIB。
JDK源碼中動(dòng)態(tài)代理已經(jīng)繼承了一個(gè)Proxy徙瓶,Java不能多繼承毛雇,所以不能繼承目標(biāo)對(duì)象,只能實(shí)現(xiàn)目標(biāo)對(duì)象的接口侦镇。CGLIB代理(基于繼承)情況下目標(biāo)對(duì)象等于代理對(duì)象灵疮,JDK動(dòng)態(tài)代理(基于接口)情況下目標(biāo)對(duì)象不等于代理對(duì)象。(但是目標(biāo)對(duì)象和代理對(duì)象等于他倆共同擁有的接口)
2.4 @AspectJ支持
AspectJ是一個(gè)面向切面的框架壳繁,它擴(kuò)展了Java語言始藕。AspectJ定義了AOP語法。Spring Aop只是借助了AspectJ的AOP語法(注解)氮趋。
2.4.1 啟用@AspectJ支持
(1)Java Configuration方式啟用@AspectJ支持
@EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
(2) Configuration方式啟用@AspectJ支持
aop:aspectj-autoproxy
<aop:aspectj-autoproxy/>
2.4.2 聲明一個(gè)Aspect切面
① 定義一個(gè)bean
② 使用@Aspectj注解
import org.aspectj.lang.annotation.Aspect;
@Component
@Aspect
public class AspectDemo {
}
2.4.3 聲明一個(gè)Pointcut切入點(diǎn)
切入點(diǎn)簽名返回值類型必須是void
import org.aspectj.lang.annotation.Aspect;
@Component
@Aspect
public class AspectDemo {
@Pointcut("execution(* transfer(..))") // 切入點(diǎn)表達(dá)式(切入點(diǎn)指示器+聲明需要執(zhí)行的方法)
private void anyOldTransfer() {} // 切入點(diǎn)簽名(訪問修飾符+void+方法名+任何參數(shù))
}
① 切入點(diǎn)指示器pointcut designators (PCD)
-
execution
: 主要的切入點(diǎn)指示器伍派。
// The execution of any public method:
execution(public * *(..))
// The execution of any method with a name that begins with set:
execution(* set*(..))
// The execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
// The execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))
// The execution of any method defined in the service package or one of its sub-packages:
execution(* com.xyz.service..*.*(..))
-
within
: 與execution相比只能限制到類,起輔助作用剩胁。
// Any join point (method execution only in Spring AOP) within the service package:
within(com.xyz.service.*)
// Any join point (method execution only in Spring AOP) within the service package or one of its sub-packages:
within(com.xyz.service..*)
-
this
: 限制代理對(duì)象(目標(biāo)對(duì)象經(jīng)過JDK動(dòng)態(tài)代理或CGLIB代理變?yōu)榇韺?duì)象)
// Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
this(com.xyz.service.AccountService)
-
target
: 限制目標(biāo)對(duì)象
// Any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:
target(com.xyz.service.AccountService)
-
args
: 只限制參數(shù)類型诉植。
// Any join point (method execution only in Spring AOP) that takes a single parameter and where the argument passed at runtime is Serializable:
args(java.io.Serializable)
-
@target
: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.
// Any join point (method execution only in Spring AOP) where the target object has a @Transactional annotation:
@target(org.springframework.transaction.annotation.Transactional)
-
@args
: 限制傳遞的實(shí)際參數(shù)的運(yùn)行時(shí)類型具有給定類型的注解。
// Any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation:
@args(com.xyz.security.Classified)
-
@within
: 限制類是否有給定的注解昵观。
// Any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:
@within(org.springframework.transaction.annotation.Transactional)
-
@annotation
: 將匹配限制為具有給定注解的連接點(diǎn)晾腔。
// Any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:
@annotation(org.springframework.transaction.annotation.Transactional)
② 聲明需要執(zhí)行的方法
切入點(diǎn)指示器的格式:
腌巾?表示可要可不要
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
例子:
* com.xyz.service.AccountService.*(..)
- modifiers-pattern:方法訪問修飾符铝耻,如public可婶,protected
- ret-type-pattern:方法返回值類型 滚停,如void,int
- declaring-type-pattern:方法所在類的全路徑名馋记,如com.xyz.service
- name-pattern:方法名類型 助赞,如AccountService
- param-pattern:方法的參數(shù)類型熬拒,如java.lang.String语御,(..)
- throws-pattern:方法拋出的異常類型峻贮,如java.lang.Exception
③ 切入點(diǎn)表達(dá)式其他方法
使用
&&
||
!
組合表達(dá)式,并且通過名稱引入表達(dá)式应闯。
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
2.4.4 聲明通知Advice
通知與切入點(diǎn)表達(dá)式相關(guān)聯(lián)纤控,并在切入點(diǎn)匹配的方法執(zhí)行之前、之后或周圍運(yùn)行碉纺。
① 前置通知 @Before
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.myapp.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
② 后置通知 @AfterReturning
returning屬性中使用的名稱必須與通知方法中的參數(shù)名稱對(duì)應(yīng)船万。還將匹配限制為只匹配那些返回指定類型值的方法執(zhí)行刻撒,(在本例中是Object,它匹配任何返回值)耿导。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}
③ 拋出異常后通知 @AfterThrowing
throwing屬性中使用的名稱必須與通知方法中的參數(shù)名稱對(duì)應(yīng)声怔。當(dāng)方法執(zhí)行通過拋出異常而退出時(shí),異常將作為相應(yīng)的參數(shù)值傳遞給通知方法碎节。并限制指定類型異常,本例為DataAccessException
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
}
④ 后置最終通知 @After
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}
⑤ 環(huán)繞通知 @Around
ProceedingJoinPoint extends JoinPoint 正在增強(qiáng)(執(zhí)行)的連接點(diǎn)(方法)
JoinPoint 連接點(diǎn)抵卫,有g(shù)etThis()狮荔,getTarget()等方法。
對(duì)ProceedingJoinPoint調(diào)用proceed()會(huì)導(dǎo)致底層方法運(yùn)行介粘。proceed方法還可以傳入一個(gè)對(duì)象[]殖氏。數(shù)組中的值用作方法執(zhí)行時(shí)的參數(shù)。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("com.xyz.myapp.CommonPointcuts.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// 開始
Object retVal = pjp.proceed();
// 結(jié)束
return retVal;
}
}
2.4.5 引入
聲明對(duì)象實(shí)現(xiàn)給定的接口,并提供該接口的一個(gè)實(shí)現(xiàn)代表的對(duì)象姻采。通過
@DeclareParents
實(shí)現(xiàn)雅采。本例中給定一個(gè)名為UsageTracked的接口和該接口的一個(gè)名為DefaultUsageTracked的實(shí)現(xiàn),之后實(shí)現(xiàn)UsageTracked接口的實(shí)現(xiàn)類默認(rèn)調(diào)用DefaultUsageTracked中已實(shí)現(xiàn)的方法慨亲。
@Aspect
public class UsageTracking {
@DeclareParents(value="com.xzy.myapp.service.*+",defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("com.xyz.myapp.CommonPointcuts.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
2.4.6 切面實(shí)例化模型
perthis
pertarget
@Aspect("perthis(com.xyz.myapp.CommonPointcuts.businessService())")
public class MyAspect {
private int someState;
@Before("com.xyz.myapp.CommonPointcuts.businessService()")
public void recordServiceUsage() {
// ...
}
}