一鸠踪、AOP概念
AOP(Aspect Oriented Programming)是一種編程范式港柜。請看大佬的總結(jié) AOP的簡介
常用術(shù)語
切面(Aspect):
一個關(guān)注點的模塊化,這個關(guān)注點可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式來實現(xiàn)饼灿。
連接點(Joinpoint):
程序執(zhí)行過程中某個特定的點,比如某方法調(diào)用或者處理異常
通知(Advice):
切面連接點的處理邏輯帝美,也就是向連接點注入的代碼碍彭。許多AOP框架(包括Spring)都是以攔截器做通知模型,并維護一個以連接點為中心的攔截器鏈悼潭。
@Before:?標識一個前置增強方法硕旗,相當于BeforeAdvice的功能.
@After:?final增強,不管是拋出異撑穑或者正常退出都會執(zhí)行.
@AfterReturning:? 后置增強漆枚,似于AfterReturningAdvice, 方法正常退出時執(zhí)行.
@AfterThrowing:? 異常拋出增強,相當于ThrowsAdvice.
@Around: 環(huán)繞增強抵知,相當于MethodInterceptor.
切入點(Pointcut):
JoinPoint的集合墙基,是程序中需要注入Advice的位置的集合,指明Advice要在什么樣的條件下才能被觸發(fā)刷喜,在程序中主要體現(xiàn)為書寫切入點表達式残制。
引入(Introduction):
用來給一個類型聲明額外的方法或?qū)傩裕ㄒ脖环Q為連接類型聲明(inter-type declaration))。Spring允許引入新的接口(以及一個對應(yīng)的實現(xiàn))到任何被代理的對象掖疮。例如初茶,你可以使用引入來使一個bean實現(xiàn)IsModified接口,以便簡化緩存機制浊闪。
目標對象(Target Object):
被一個或者多個切面所通知的對象恼布。也被稱做被通知(advised)對象。既然Spring AOP是通過運行時代理實現(xiàn)的搁宾,這個對象永遠是一個被代理(proxied)對象折汞。
AOP代理(AOP Proxy):
AOP框架創(chuàng)建的對象,用來實現(xiàn)切面契約(例如通知方法執(zhí)行等等)盖腿。在Spring中爽待,AOP代理可以是JDK動態(tài)代理或者CGLIB代理损同。
織入(Weaving):
把切面連接到其它的應(yīng)用程序類型或者對象上,并創(chuàng)建一個被通知的對象鸟款。這些可以在編譯時(例如使用AspectJ編譯器)膏燃,類加載時和運行時完成。Spring和其他純Java AOP框架一樣何什,在運行時完成織入组哩。
二、AOP的實現(xiàn)
1富俄、定義切面類
① 在類上使用 @Component 注解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 注解 使之成為切面類
@Component
@Aspect
public class WebLogAspect {
}
2、定義切入點
定義方式:
execution:用于匹配方法執(zhí)行的連接點而咆;
within:用于匹配指定類型內(nèi)的方法執(zhí)行霍比;
this:用于匹配當前AOP代理對象類型的執(zhí)行方法;注意是AOP代理對象的類型匹配暴备,這樣就可能包括引入接口也類型匹配悠瞬;????????
target:用于匹配當前目標對象類型的執(zhí)行方法;注意是目標對象的類型匹配涯捻,這樣就不包括引入接口也類型匹配浅妆;
args:用于匹配當前執(zhí)行的方法傳入的參數(shù)為指定類型的執(zhí)行方法;
@within:用于匹配所以持有指定注解類型內(nèi)的方法障癌;
@target:用于匹配當前目標對象類型的執(zhí)行方法凌外,其中目標對象持有指定的注解;
@args:用于匹配當前執(zhí)行的方法傳入的參數(shù)持有指定注解的執(zhí)行涛浙;
@annotation:用于匹配當前執(zhí)行方法持有指定注解的方法康辑;
建議使用@annotation,配合自定義注解轿亮,實現(xiàn)攔截
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WebLogAspect {
}
@Pointcut("@annotation(com.zgn.blog.annotation.WebLogAspect)")
public void webLog(){}
三疮薇、AOP的通知
1、前置通知@Before
除非拋出異常我注,否則這個通知不能阻止連接點之前的執(zhí)行流程
1)通過JoinPoint可以獲得通知的簽名信息按咒,如目標方法名、目標方法參數(shù)信息等
2)通過RequestContextHolder來獲取請求信息但骨,Session信息
@Before("webLog()")
public void doBefore(JoinPoint joinPoint)? throws Throwable{
? ? //獲取目標方法的參數(shù)信息?
? ? Object[] obj = joinPoint.getArgs();?
? ? //AOP代理類的信息?
? ? joinPoint.getThis();?
? ? //代理的目標對象?
? ? joinPoint.getTarget();?
? ? //用的最多 通知的簽名?
? ? Signature signature = joinPoint.getSignature();?
? ? //代理的是哪一個方法?
? ? logger.info("代理的是哪一個方法"+signature.getName());?
? ? //AOP代理類的名字?
? ? logger.info("AOP代理類的名字"+signature.getDeclaringTypeName());?
? ? //AOP代理類的類(class)信息?
? ? signature.getDeclaringType();?
? ? //獲取RequestAttributes?
? ? RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();?
? ? //從獲取RequestAttributes中獲取HttpServletRequest的信息?
? ? HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);?
? ? //如果要獲取Session信息的話励七,可以這樣寫:?
? ? //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);?
? ? //獲取請求參數(shù)
? ? Enumeration<String> enumeration = request.getParameterNames();?
? ? Map<String,String> parameterMap = Maps.newHashMap();?
? ? while (enumeration.hasMoreElements()){?
? ? ? ? String parameter = enumeration.nextElement();?
? ? ? ? parameterMap.put(parameter,request.getParameter(parameter));?
? ? }?
? ? String str = JSON.toJSONString(parameterMap);?
? ? if(obj.length > 0) {?
? ? ? ? logger.info("請求的參數(shù)信息為:"+str);
? ? }?
}
2、后置返回通知
1)如果參數(shù)中的第一個參數(shù)為JoinPoint奔缠,則第二個參數(shù)為返回值的信息
2)? 如果參數(shù)中的第一個參數(shù)不為JoinPoint呀伙,則第一個參數(shù)為returning中對應(yīng)的參數(shù)
3)? returning:限定了只有目標方法返回值與通知方法相應(yīng)參數(shù)類型時才能執(zhí)行后置返回通知,否則不執(zhí)行添坊,
4)? 對于returning對應(yīng)的通知方法參數(shù)為Object類型將匹配任何目標返回值
@AfterReturning(value = "webLog()",returning = "keys")?
public void doAfterReturning(JoinPoint joinPoint,Object keys){?
? ? log.info("第一個后置返回通知的返回值:"+keys);?
}?
@AfterReturning(value = "webLog()",returning = "keys",argNames = "keys")?
public void doAfterReturning(String keys){?
? ? log.info("第二個后置返回通知的返回值:"+keys);?
}
3剿另、后置異常通知
定義一個名字,該名字用于匹配通知實現(xiàn)方法的一個參數(shù)名,當目標方法拋出異常返回后雨女,將把目標方法拋出的異常傳給通知方法谚攒;
throwing:限定了只有目標方法拋出的異常與通知方法相應(yīng)參數(shù)異常類型時才能執(zhí)行后置異常通知,否則不執(zhí)行氛堕,
對于throwing對應(yīng)的通知方法參數(shù)為Throwable類型將匹配任何異常馏臭。
@AfterThrowing(value = "webLog()",throwing = "exception")?
public void doAfterThrowing(JoinPoint joinPoint,Throwable exception){?
? ? if(exception instanceof NullPointerException){?
? ? ? ? logger.info("空指針異常");?
? ? }?
}?
4、后置最終通知
后置最終通知(目標方法只要執(zhí)行完了就會執(zhí)行后置通知方法)
@After(value = "webLog()")?
public void doAfter(JoinPoint joinPoint){
? ? log.info("doAfter success");?
}?
5讼稚、環(huán)繞通知
1)第一個參數(shù)必須是ProceedingJoinPoint類型括儒。
2)在通知體內(nèi)調(diào)用ProceedingJoinPoint的proceed()方法會導致后臺的連接點方法執(zhí)行
3)proceed()方法也可能會被調(diào)用并且傳入一個Object[]對象,該數(shù)組中的值將被作為方法執(zhí)行時的入?yún)ⅰ?/p>
@Around(value = "webLog()")?
public Object doAround(ProceedingJoinPoint joinPoint){?
? ? try {?
? ? ? ? Object obj = joinPoint.proceed();?
? ? ? ? return obj;?
? ? } catch (Throwable e) {?
? ? ? ? e.printStackTrace();?
? ? }?
? ? return null;?
}?
參考文章:
https://blog.csdn.net/zhaoyanjun6/article/details/80669022