一、AOP技術概念
面向切面編程[底層就是動態代理]指程序在運行期間動態的將某段代碼切入到指定方法位置進行運行的編程方式。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
二、Spring AOP 相關術語
1、切面(Aspect):在Spring AOP中,切面可以使用通用類或者在普通類中以@Aspect 註解(@AspectJ風格)來實現;
2、連接點(Joinpoint):在Spring AOP中一個連接點代表一個方法的執行;
3、通知(Advice):在切面的某個特定的連接點(Joinpoint)上執行的動作通知有各種類型,其中包括"around"、 "before”
和"after"等通知。許多AOP框架,包括Spring,都是以攔截器做通知模型,並維護一個以連接點爲中心 的攔截器鏈;
4、切入點(Pointcut):定義出一個或一組方法,當執行這些方法時可產生通知,Spring缺省使用AspectJ切入點語法。
三、通知Advice類型
1、前置通知(@Before):在某連接點(join point)之前執行的通知,但這個通知不能阻止連接點前的執行(除非它拋出一個異常)
2、返回後通知(@AfterReturning):在某連接點(join point)正常完成後執行的通知:例如,一個方法沒有拋出任何異常,正常返回
3、拋出異常後通知(@AfterThrowing):方法拋出異常退出時執行的通知
4、後通知(@After):當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)
5、環繞通知(@Around):包圍一個連接點(join point)的通知,如方法調用。這是最強大的一種通知類型,環繞通知可
以在方法調用 * 前後完成自定義的行爲,它也會選擇是否繼續執行連接點或直接返回它們自己的返回值或拋出異常來結束執行
四、代碼示例
1、定義切面織入點註解
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CollectEnable {
boolean isCollect() default false;
}
2、定義切面
@Aspect
@Component
public class CollectAspect {
private final static Logger LOGGER= LoggerFactory.getLogger(CollectAspect.class);
@Value("${collect.canable}")
private boolean isConfig;
/**
* 判斷是否有註解
* @param joinPoint 連接點
*/
private CollectEnable getCollectAnnotation(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature= (MethodSignature) signature;
Method method=methodSignature.getMethod();
if(method!=null){
return method.getAnnotation(CollectEnable.class);
}
return null;
}
/**
*配置切面的織入點
*/
@Pointcut("@annotation(com.fce.mycat.submeter.anotation.CollectEnable)")
public void collectPointCut(){}
/**
* 在切點方法之前執行
* @param joinPoint
*/
@Before("collectPointCut()")
public void doBefore(JoinPoint joinPoint){
CollectEnable collectAnnotation = getCollectAnnotation(joinPoint);
if(collectAnnotation!=null){
LOGGER.info("CollectAspect-->doBefore-------前置織入");
}
}
@After("collectPointCut()")
public void doAfter(JoinPoint joinPoint){
CollectEnable collectAnnotation = getCollectAnnotation(joinPoint);
if(collectAnnotation!=null){
LOGGER.info("CollectAspect-->doAfter-------後置織入");
}
}
/**
* 環繞增強,控制包裹代碼是否執行
* @param joinPoint
*/
@Around("collectPointCut()")
public void doAround(ProceedingJoinPoint joinPoint){
CollectEnable collectAnnotation = getCollectAnnotation(joinPoint);
boolean collect = collectAnnotation.isCollect();
if(collect&&isConfig){
LOGGER.info("CollectAspect-->doAround----開啓採集任務");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}else{
LOGGER.info("CollectAspect-->doAround----禁止執行採集");
}
}
}
3、在切點上使用增強
@Service
public class CollectService {
private final static Logger LOGGER= LoggerFactory.getLogger(CollectService.class);
@CollectEnable(isCollect = true)
public void collect360(){
LOGGER.info("CollectService---->collect360()-------開始採集了");
}
@CollectEnable
public void collectHuaWei(){
LOGGER.info("CollectService---->collectHuaWei()----開始採集了");
}
}