AOP(面向切面編程)
AOP是OOP(面向對象編程)的延續,可是它和麪向對象的縱向編程不一樣,它是一個橫向的切面式的編程。能夠理解爲oop就是一根柱子,若是須要就繼續往上加長,而aop則是在須要的地方把柱子切開,在中間加上一層,再把柱子完美的粘合起來。
用物理上的話來講,aop就是給這個編程世界加上了一個維度,二維到三維的差異。很明顯aop要靈活得多
AOP主要實現的目的是針對業務處理過程當中的切面進行提取,它所面對的是處理過程當中的某個步驟或階段,以得到邏輯過程當中各部分之間低耦合性的隔離效果。
AOP核心概念
Joinpoint(鏈接點):指那些被攔截到的點(spring中這些點指的是方法)
Pointcut(切入點):指咱們要對哪些joinpoint進行攔截的定義
Advice(通知/加強):攔截到joinpoint以後多要作的事情就是通知(通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知)
Introduction(引介):引介是一種特殊的通知。在不改變代碼的前提下,introduction能夠在運行期間爲類動態日案件一些方法或Field
Target(目標對象):代理的目標對象(須要加強的類)
Weaving(織入):把加強應用到目標的過程(advice應用到target的過程)
Proxy(代理):一個類被AOP織入加強後,就會產生一個結果代理類
Aspect(切面):是切入點和通知(引介)的結合web
看不懂吧<( ̄︶ ̄)>,由於我以前也沒看懂,沒事,咱們寫幾個例子看看就能懂了。若是你沒學過還能看懂,那我也只能膜拜大佬了
概念說完,下面開始進入正式環節
第一步 添加依賴
想使用AOP光憑以前的那些仍是不夠的,因此咱們還須要添加一些依賴
compile "org.springframework:spring-aspects:4.3.9.RELEASE"
compile "org.springframework:spring-aop:4.3.9.RELEASE"
compile "aspectj:aspectjweaver:1.5.4"
compile "aopalliance:aopalliance:1.0"
這裏面的aspectj能夠看到,並非spring的一部分,可是自從spring2以後,官方就添加了對aspectj的支持,並且如今spring官方是推薦使用aspectj來開發aop,咱們天然要跟隨官方的大旗走了
aspectj實現aop有兩種方式,並且仍是老兩種
xml配置
註解實現
咱們來看這兩種,和以前同樣,咱們經常使用的確定仍是能少寫代碼的註解方式,xml配置的方式看看就能夠了,可是至少也要能看懂,否則別人寫了你看不懂就尷尬了
1. xml配置
首先在咱們的xml文件中加入約束
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
能夠看到咱們又加上了aop的約束
咱們仍是繼續用咱們的User類不過此次一個類不夠用,咱們須要再來一個類就叫Advice吧,並且咱們還須要在類中加點方法
@Bean
data class User(var name: String, var age: Int)
{
fun add()
{
println("user add")
}
}spring
@Bean
data class Advice(var content: String)
{
fun before()
{
println("前置通知")
}
}
而後就是配置xml了,咱們經過xml來配置切入點(pointcut),這裏咱們須要用到一個函數
execution(<訪問修飾符>?<返回類型><方法名>(<參數>)<異常>)
來點例子看一下:
匹配全部public的方法:execution(public * *(..))
匹配包下全部類的方法:execution(* com.kotlin.*(..)) (一個點表示不包含子包)
execution(* com.kotlin.. *(..)) (兩個點表示包含子包)
匹配實現特定接口的全部類的方法:execution(* com.kotlin.xxinterface+.*(..))
匹配全部add開頭的方法:execution(* add*(..))
匹配全部方法:execution(* *.*(..))
這樣大概清楚了吧,下面咱們咱們來寫一個前置通知加強User中的add方法
<!--1.配置對象-->
<bean id="user" class="com.kotlin.Bean.User"></bean>
<bean id="advice" class="com.kotlin.Bean.Advice"></bean>express
<!--2.配置aop操做-->
<aop:config>
<!--2.1配置切入點 由於User中只有一個方法,就直接加強User中的全部方法了-->
<aop:pointcut id="pointcut" expression="execution(* com.kotlin.Bean.User.*(..))"/>編程
<!--2.2配置切面 將加強用到方法上-->
<aop:aspect ref="advice">
<!--選擇用來加強的方法和須要加強的切入點-->
<aop:before method="before" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
而後來到咱們的測試類,運行下看看
class main
{
@Test
fun test()
{
//加載Spring配置文件,建立對象
val context = FileSystemXmlApplicationContext("src/main/webapp/WEB-INF/applicationContext.xml")app
val user = context.getBean("user") as User
user.add()
}
}
webapp
結果能夠看到,before方法已經添加到add方法中了
下面我就直接演示下其餘幾種的用法了
@Bean
data class User(var name: String, var age: Int)
{
fun add(): String
{
println("user add")
return "你好"
}
}函數
@Bean
data class Advice(var content: String)
{
fun before()
{
println("前置通知")
}oop
//後置通知須要傳入一個參數,這個參數就是須要加強方法的返回值,沒有能夠不寫
fun afterResult(result: Any)
{
println("後置通知 "+result)
}測試
//最終通知不管該方法有沒有出異常有沒有返回值,最終都會被執行
fun after()
{
println("最終通知")
}.net
/* 環繞通知須要一個ProceedingJoinPoint參數,這至關於須要加強的函數的方法體,須要的調用它的proceed方法執行,若是該函數有返回值,那麼環繞通知也須要返回一個proceed方法的返回值 */
fun around(pro: ProceedingJoinPoint): Any
{
//方法以前
println("環繞通知 方法以前")
//被加強的方法
val any = pro.proceed()
//方法以後
println("環繞通知 方法以後")
return any
}
//異常通知須要一個異常參數,當出現異常時該方法將會被調用
fun exception(ex : Exception)
{
println("異常通知 "+ex)
}
}
<!--1.配置對象-->
<bean id="user" class="com.kotlin.Bean.User"></bean>
<bean id="advice" class="com.kotlin.Bean.Advice"></bean>
<!--2.配置aop操做-->
<aop:config>
<!--2.1配置切入點-->
<aop:pointcut id="pointcut" expression="execution(* com.kotlin.Bean.User.*(..))"/>
<!--2.2配置切面 將加強用到方法上-->
<aop:aspect ref="advice">
<!--選擇用來加強的方法和須要加強的切入點-->
<aop:before method="before" pointcut-ref="pointcut"/>
<!--後置通知須要配置它的參數-->
<aop:after-returning method="afterResult" pointcut-ref="pointcut" returning="result"/>
<aop:after method="after" pointcut-ref="pointcut" />
<aop:around method="around" pointcut-ref="pointcut" />
<!--異常通知也要配置它的異常參數-->
<aop:after-throwing method="exception" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
而後咱們來看下結果
接着,咱們手動給他製造一個異常,就用4/0吧
能夠看到,後置通知和後環繞通知沒有了,取而代之的是除零異常,這時異常通知出現了,這也說明了後置通知只有在沒有異常時候纔會執行,異常通知只會在有異常時候執行
這也得出了這樣的結論
try
{
// 前置通知
// 環繞通知(前)
// 目標方法
// 環繞通知(後)
// 後置通知(也有人稱爲返回通知)
}
catche(Exception e)
{
// 異常通知
}
finally
{
// 最終通知
}
2.註解配置
註解配置很簡單,直接把內容寫在方法的頭上就能夠了,我把代碼給出,你們一看就知道了
首先在xml中開啓自動掃描
<!--開啓aop自動掃描代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
1
2
而後在各方法上寫上註解,別忘了類上面的註解
@Bean
@Component(value = "user")
data class User(var name: String, var age: Int)
{
fun add(): String
{
println("user add")
// var s = 4 / 0
return "你好"
}
}
@Aspect
@Bean
@Component(value = "advice")
data class Advice(var content: String)
{
@Before(value = "execution(* com.kotlin.Bean.User.*(..))")
fun before()
{
println("前置通知")
}
@AfterReturning(value = "execution(* com.kotlin.Bean.User.*(..))", returning = "result")
fun afterResult(result: Any)
{
println("後置通知 " + result)
}
@After(value = "execution(* com.kotlin.Bean.User.*(..))")
fun after()
{
println("最終通知")
}
@Around(value = "execution(* com.kotlin.Bean.User.*(..))")
fun around(pro: ProceedingJoinPoint): Any
{
//方法以前
println("環繞通知 方法以前")
//被加強的方法
val any = pro.proceed()
//方法以後
println("環繞通知 方法以後")
return any
}
@AfterThrowing(value = "execution(* com.kotlin.Bean.User.*(..))", throwing = "ex")
fun exception(ex: Exception)
{
println("異常通知 " + ex)
}
}
別忘了開啓IOC的註解掃描
結果天然毫無疑問