AOP理解
Aspect Oriented Program面向切面編程,通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。
但是,這種說法有些片面,因為在軟件工程中,AOP的價值體現(xiàn)的并不是代碼方面,更多的是為了項目的模塊化,而不僅僅是為了減少重復(fù)代碼。
AOP是一種編程思想,為的是讓模塊本身變得更加內(nèi)聚,讓開發(fā)者更多的關(guān)注到業(yè)務(wù)邏輯的開發(fā)。
在面向切面編程里,把功能分為核心業(yè)務(wù)功能和周邊業(yè)務(wù)功能:
核心業(yè)務(wù),比如登陸,日常增,刪數(shù)據(jù)
周邊功能,統(tǒng)計,日志,事務(wù)管理。在Spring的面向切面編程AOP思想里,即被定義為切面
在面向切面的思想里,核心業(yè)務(wù)功能和切面功能單獨開發(fā),然后把兩個整合在一起,就是AOP。
AOP實現(xiàn)原理
AOP底層將采用代理機制進(jìn)行實現(xiàn)
接口+實現(xiàn)類:spring采用jdk的動態(tài)代理Proxy
實現(xiàn)類:spring采用cglib字節(jié)碼增強
Spring默認(rèn)使用JDK動態(tài)代理,在需要代理類而不是接口的時候,Spring會自動切換為使用cglib代理,不過現(xiàn)在的項目都是面向接口開發(fā),所以JDK動態(tài)代理相對來說用的還是多一些。
Spring AOP引入了幾個概念
切面(Aspect),它是跨不同Java類層面的橫切性邏輯??梢酝ㄟ^XML文件中配置的普通類,也可以在類代碼中使用“@Aspect” 注解去聲明,在運行時,Spring會創(chuàng)建類似Advisor來代替它。其內(nèi)部包括切入和通知。通俗的講,是要告訴Spring什么時候,什么地方,做什么。
連接點(JoinPoint),程序運行過程中明確的點,一般是方法的調(diào)用。
通知(Advice),AOP在特定的切入點上知性的增強處理,告訴Spring什么時候,做什么。一般會講Advice模擬成一個攔截起,并且在JoinPoint上維護(hù)多個Advice,進(jìn)行層攔截。比如Http鑒權(quán)的實現(xiàn)。
切入點(PointCut),就是帶有通知的連接點。要切入的對象,可以是類,或方法。在Spring中,所有的方法都可以認(rèn)為是JoinPoint,都可以添加Advice,但是這并不是所有都是我們真正想要的,而PointCut提供了一組規(guī)則,來匹配JointPoint,給滿足規(guī)則的JointPoint添加Advice。其主要用來修飾JointPoint。
Advice的五種通知類型
前置通知(@Before):在目標(biāo)方法被調(diào)用之前調(diào)用通知功能
后置通知(@After):在目標(biāo)方法完成之后調(diào)用通知,此時不會關(guān)心方法的輸出
返回通知(@After-Returning):在目標(biāo)方法成功執(zhí)行之后調(diào)用通知
異常通知(@AfterThrowing):在目標(biāo)方法拋出異常后調(diào)用通知
環(huán)繞通知(@Around):通知包裹了被通知的方法,在被通知的方法調(diào)用之前和之后執(zhí)行自定義的行為
做個小demo
1. 在src下新建bean包,新建Operator類
package bean;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect public class Operator {
// @Pointcut("execution(* service.UserService.add()")
// public void service() { //
}
@Pointcut("execution(* service.UserService.add())") public void IService(){
}
@Before("IService()") public void doBefore(JoinPoint joinPoint) {
System.out.println(" before Advice");
}
@After("IService()") public void doAfter(JoinPoint joinPoint) {
System.out.println(" after Advice");
}
@AfterReturning(pointcut = "IService()", returning = "returnVal") public void afterReturn(JoinPoint joinPoint, Object returnVal) {
System.out.println(" after Advice return " + returnVal);
}
@AfterThrowing(pointcut = "IService()", throwing = "errors") public void afterThrowing(JoinPoint joinPoint, Throwable errors) {
System.out.println("afterThrowing Advice ..." + errors);
System.out.println(" afterThrowing..");
}
@Around("IService()") public void doAround(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println(" around Advice before"); try {
proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(" around Advice before");
}
}
- src下新建service包,新建UserService類
package service;
import org.springframework.stereotype.Service;
@Service public class UserService { public void add() {
System.out.println("UserService add.");
} public boolean delete() {
System.out.println("UserService delete .."); return true;
} public void edit() {
System.out.println("UserService edit ");
}
}
3.配置XML文件,在src下新建applicationContext.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" 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">
<bean name="userService" class="service.UserService"></bean>
<bean name="operator" class="bean.Operator"></bean>
<aop:config>
<aop:aspect id="operatorAspect" ref="operator">
<aop:pointcut id="servicePointCut" expression="execution(* service.UserService.add())"></aop:pointcut>
<aop:before pointcut-ref="servicePointCut" method="doBefore"></aop:before>
<aop:after pointcut-ref="servicePointCut" method="doAfter"></aop:after>
<aop:around pointcut-ref="servicePointCut" method="doAround"></aop:around>
<aop:after-returning pointcut-ref="servicePointCut" method="afterReturn" returning="returnVal"></aop:after-returning>
<aop:after-throwing pointcut-ref="servicePointCut" method="afterThrowing" throwing="errors"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
4.測試AOP,在src下新建test.java
import bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class test {
@Test public void demo() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService", UserService.class);
userService.add();
}
}
5.運行結(jié)果
