aop 終於提上日程來寫一寫了。java
本系列分爲 上、中、下三篇。上篇主要是介紹若是使用 AOP ,提供了demo和配置方式說明;中篇來對實現 AOP 的技術原理進行分析;下篇主要針對Spring中對於AOP的實現進行源碼分析。git
項目地址:glmapper-ssm-parentgithub
這個項目裏面包含了下面幾種 AOP 實現方式的全部代碼,有興趣的同窗能夠fork跑一下。這個demo中列舉了4中方式的實現:web
目前咱們常常用到的是基於Aspect註解的方式的方式。下面來一個個瞭解下不一樣方式的表現形式。正則表達式
這種方式看起來很好理解,可是配置起來至關麻煩;小夥伴們能夠參考項目來看,這裏只貼出比較關鍵的流程代碼。spring
public interface GoodsService {
/** * 查詢全部商品信息 * * @param offset 查詢起始位置 * @param limit 查詢條數 * @return */
List<Goods> queryAll(int offset,int limit);
}
複製代碼
@Service
@Qualifier("goodsService")
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsDao goodsDao;
public List<Goods> queryAll(int offset, int limit) {
System.out.println("執行了queryAll方法");
List<Goods> list = new ArrayList<Goods>();
return list;
}
}
複製代碼
//通知類 LoggerHelper
public class LoggerHelper implements MethodBeforeAdvice, AfterReturningAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerHelper.class);
//MethodBeforeAdvice的before方法實現
public void before(Method method, Object[] objects, Object o) throws Throwable {
LOGGER.info("before current time:"+System.currentTimeMillis());
}
//AfterReturningAdvice的afterReturning方法實現
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
複製代碼
<!-- 定義被代理者 -->
<bean id="goodsServiceImpl" class="com.glmapper.framerwork.service.impl.GoodsServiceImpl"></bean>
<!-- 定義通知內容,也就是切入點執行先後須要作的事情 -->
<bean id="loggerHelper" class="com.glmapper.framerwork.aspect.LoggerHelper"></bean>
<!-- 定義切入點位置 -->
<bean id="loggerPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*query.*"></property>
</bean>
<!-- 使切入點與通知相關聯,完成切面配置 -->
<!-- 從這裏能夠幫助咱們理解Advisor,advice和pointcut之間的關係-->
<!--adivce和pointcut是Advisor的兩個屬性-->
<bean id="loggerHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="loggerHelper"></property>
<property name="pointcut" ref="loggerPointcut"></property>
</bean>
<!-- 設置代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理的對象 ,也就是目標類-->
<property name="target" ref="goodsServiceImpl"></property>
<!-- 使用切面 -->
<property name="interceptorNames" value="loggerHelperAdvisor"></property>
<!-- 代理接口,商品接口 -->
<property name="proxyInterfaces" value="com.glmapper.framerwork.service.GoodsService"></property>
</bean>
複製代碼
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
//由於咱們已經在配置文件中配置了proxy,
//因此這裏能夠直接注入拿到咱們的代理類
@Autowired
private GoodsService proxy;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
//這裏使用proxy執行了*query*,
List<Goods> goods = proxy.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
複製代碼
這個方式是經過一個SpringContextUtil工具類來獲取代理對象的。express
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request,
HttpServletResponse response, ModelAndView view) {
//這裏經過工具類來拿,效果同樣的。
GoodsService proxy= (GoodsService) SpringContextUtil.getBean("proxy");
List<Goods> goods = proxy.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
複製代碼
這個仍是有點坑的,首先SpringContextUtil是繼承ApplicationContextAware這個接口,咱們但願可以SpringContextUtil能夠被Spring容器直接管理,因此,須要使用 @Component 標註。標註了以後最關鍵的是它得可以被咱們配置的注入掃描掃到(親自踩的坑,我把它放在一個掃不到的包下面,一直debug都是null;差點砸電腦...)編程
@Component
public class SpringContextUtil implements ApplicationContextAware {
// Spring應用上下文環境
private static ApplicationContext applicationContext;
/** * 實現ApplicationContextAware接口的回調方法,設置上下文環境 * * @param applicationContext */
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
/** * @return ApplicationContext */
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/** * 獲取對象 * 這裏重寫了bean方法,起主要做用 * @param name * @return Object 一個以所給名字註冊的bean的實例 * @throws BeansException */
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
複製代碼
21:04:47.940 [http-nio-8080-exec-7] INFO
c.g.framerwork.aspect.LoggerHelper - before current
time:1529413487940
執行了queryAll方法
21:04:47.940 [http-nio-8080-exec-7] INFO
c.g.framerwork.aspect.LoggerHelper - afterReturning current
time:1529413487940
複製代碼
上面就是最最經典的方式,就是經過代理的方式來實現AOP的過程。bash
注意這裏和LoggerHelper的區別,這裏的LoggerAspect並無繼承任何接口或者抽象類。網絡
/** * @description: [描述文本] * @email: <a href="guolei.sgl@antfin.com"></a> * @author: guolei.sgl * @date: 18/6/20 */
public class LoggerAspect {
private static final Logger LOGGER =
LoggerFactory.getLogger(LoggerHelper.class);
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
public void afterReturning() {
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
複製代碼
<!-- 定義通知內容,也就是切入點執行先後須要作的事情 -->
<bean id="loggerAspect" class="com.glmapper.framerwork.aspect.LoggerAspect">
</bean>
<aop:config>
<!--定義切面-->
<aop:aspect ref="loggerAspect">
<aop:pointcut id="loggerPointCut" expression= "execution(* com.glmapper.framerwork.service.impl.*.*(..)) " />
<!-- 定義 Advice -->
<!-- 前置通知 -->
<aop:before pointcut-ref="loggerPointCut" method="before" />
<!-- 後置通知 -->
<aop:after-returning pointcut-ref="loggerPointCut" method="afterReturning"/>
</aop:aspect>
</aop:config>
複製代碼
注意這裏LoggerAspect中的before和afterReturning若是有參數,這裏須要處理下,不然會報 0 formal unbound in pointcut 異常。
這種方式是最簡單的一種實現,直接使用 @Aspect 註解標註咱們的切面類便可。
/** * @description: 使用Aspect註解驅動的方式 * @email: <a href="guolei.sgl@antfin.com"></a> * @author: guolei.sgl * @date: 18/6/20 */
@Aspect
public class LoggerAspectInject {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..))")
public void cutIn(){}
@Before("cutIn()")
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
@AfterReturning("cutIn()")
public void AfterReturning(){
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
複製代碼
<aop:aspectj-autoproxy />
<!-- 定義通知內容,也就是切入點執行先後須要作的事情 -->
<bean id="loggerAspectInject" class="com.glmapper.framerwork.aspect.LoggerAspectInject">
</bean>
<!-- 定義被代理者 -->
<bean id="goodsServiceImpl" class="com.glmapper.framerwork.service.impl.GoodsServiceImpl">
</bean>
複製代碼
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
//經過SpringContextUtil手動獲取 代理bean
GoodsService goodsService=(GoodsService)
SpringContextUtil.getBean("goodsServiceImpl");
List<Goods> goods = goodsService.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
複製代碼
@Aspect
@Component //這裏加上了Component註解,就不須要在xml中配置了
public class LoggerAspectInject {
private static final Logger LOGGER =
LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.framerwork.service.impl.*.*(..))")
public void cutIn(){}
@Before("cutIn()")
public void before(){
LOGGER.info("before current time:"+System.currentTimeMillis());
}
@AfterReturning("cutIn()")
public void AfterReturning(){
LOGGER.info("afterReturning current time:"+System.currentTimeMillis());
}
}
複製代碼
@Controller
@RequestMapping("/buy")
public class BuyController {
@Autowired
private OrderService orderService;
//直接注入
@Autowired
private GoodsService goodsService;
@RequestMapping("/initPage")
public ModelAndView initPage(HttpServletRequest request, HttpServletResponse response, ModelAndView view) {
List<Goods> goods = goodsService.queryAll(10,10);
view.addObject("goodsList", goods);
view.setViewName("goodslist");
return view;
}
}
複製代碼
/** * @description: aop * @email: <a href="henugl@1992.163.com"></a> * @author: glmapper@磊叔 * @date: 18/6/4 */
@Aspect
@Component
public class LoggerAspectInject {
private static final Logger LOGGER= LoggerFactory.getLogger(LoggerAspectInject.class);
@Pointcut("execution(* com.glmapper.book.web.controller.*.*(..))")
public void cutIn(){
}
@Around("cutIn()") // 定義Pointcut,名稱即下面的標識"aroundAdvice
public Object aroundAdvice(ProceedingJoinPoint poin){
System.out.println("環繞通知");
Object object = null;
try{
object = poin.proceed();
}catch (Throwable e){
e.printStackTrace();
}
return object;
}
// 定義 advise
//這個方法只是一個標識,至關於在配置文件中定義了pointcut的id,此方法沒有返回值和參數
@Before("cutIn()")
public void beforeAdvice(){
System.out.println("前置通知");
}
@After("cutIn()")
public void afterAdvice(){
System.out.println("後置通知");
}
@AfterReturning("cutIn()")
public void afterReturning(){
System.out.println("後置返回 ");
}
@AfterThrowing("cutIn()")
public void afterThrowing(){
System.out.println("後置異常");
}
}
複製代碼
關於命名切入點:上面的例子中cutIn方法能夠被稱之爲命名切入點,命名切入點能夠被其餘切入點引用,而匿名切入點是不能夠的。只有@AspectJ支持命名切入點,而Schema風格不支持命名切入點。 以下所示,@AspectJ使用以下方式引用命名切入點:
@Pointcut("execution(* com.glmapper.book.web.controller.*.*(..))")
public void cutIn(){
}
//引入命名切入點
@Before("cutIn()")
public void beforeAdvice(){
System.out.println("前置通知");
}
複製代碼
這種方式我感受是第二種和第三種的結合的一種方式。
/** * @description: 注入式 也是一種經過XML方式配置的方式 * @email: <a href="guolei.sgl@antfin.com"></a> * @author: guolei.sgl * @date: 18/6/20 */
public class LoggerAspectHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggerAspectHelper.class);
/** * 調動方法前執行 * @param point * @throws Throwable */
public void doBefore(JoinPoint point) throws Throwable {
LOGGER.info("before current time:"+System.currentTimeMillis());
}
/** * 在調用方法先後執行 * @param point * @return * @throws Throwable */
public Object doAround(ProceedingJoinPoint point) throws Throwable {
LOGGER.info("around current time:"+System.currentTimeMillis());
if(point.getArgs().length>0) {
return point.proceed(point.getArgs());
}else{
return point.proceed();
}
}
/** * 在調用方法以後執行 * @param point * @throws Throwable */
public void doAfter(JoinPoint point) throws Throwable {
LOGGER.info("after current time:"+System.currentTimeMillis());
}
/** * 異常通知 * @param point * @param ex */
public void doThrowing(JoinPoint point, Throwable ex) {
LOGGER.info("throwing current time:"+System.currentTimeMillis());
}
}
複製代碼
<bean id="loggerAspectHelper" class="com.glmapper.framerwork.aspect.LoggerAspectHelper">
</bean>
<aop:config>
<aop:aspect id="configAspect" ref="loggerAspectHelper">
<!--配置com.glmapper.framerwork.service.imp 包下全部類或接口的全部方法 -->
<aop:pointcut id="cutIn" expression= "execution(* com.glmapper.framerwork.service.impl.*.*(..))" />
<aop:before pointcut-ref="cutIn" method="doBefore" />
<aop:after pointcut-ref="cutIn" method="doAfter" />
<aop:around pointcut-ref="cutIn" method="doAround" />
<aop:after-throwing pointcut-ref="cutIn" method="doThrowing" throwing="ex" />
</aop:aspect>
</aop:config>
複製代碼
23:39:48.756 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- before current time:1529509188756
23:39:48.757 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- around current time:1529509188757
excute queryAll method...
23:39:48.757 [http-nio-8080-exec-4] INFO c.g.f.aspect.LoggerAspectHelper
- after current time:1529509188757
複製代碼
從上面的例子中咱們都是使用一些正則表達式來指定咱們的切入點的。在實際的使用中,不只僅是execution,還有其餘不少種類型的表達式。下面就列舉一些:
用於匹配方法執行的鏈接點;
execution(* com.glmapper.book.web.controller.*.*(..))
複製代碼
用於匹配指定類型內的方法執行;
//若是在com.glmapper.book.web.controller包或其下的任何子包中
//定義了該類型,則在Web層中有一個鏈接點。
within(com.glmapper.book.web.controller..*)
@Pointcut("within(com.glmapper.book.web.controller..*)")
public void cutIn(){}
複製代碼
@within:用於匹配因此持有指定註解類型內的方法;
/** * @description: 註解定義 * @email: <a href="henugl@1992.163.com"></a> * @author: glmapper@磊叔 * @date: 18/6/4 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD})
public @interface AuthAnnotation {
}
複製代碼
任何目標對象對應的類型持有AuthAnnotation註解的類方法;必須是在目標對象上聲明這個註解,在接口上聲明的對它不起做用。
@within(com.glmapper.book.common.annotaion.AuthAnnotation)
//全部被@AdviceAnnotation標註的類都將匹配
@Pointcut("@within(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
複製代碼
用於匹配當前AOP代理對象類型的執行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入接口也類型匹配;this中使用的表達式必須是類型全限定名,不支持通配符;
//當前目標對象(非AOP對象)實現了 UserService 接口的任何方法
this(com.glmapper.book.web.service.UserService)
//用於向通知方法中傳入代理對象的引用。
@Before("cutIn() && this(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
System.out.println("前置通知");
}
複製代碼
用於匹配當前目標對象類型的執行方法;注意是目標對象的類型匹配,這樣就不包括引入接口也類型匹配;target中使用的表達式必須是類型全限定名,不支持通配符;
//當前目標對象(非AOP對象)實現了 UserService 接口的任何方法
target(com.glmapper.book.web.service.UserService)
//用於向通知方法中傳入代理對象的引用。
@Before("cutIn() && target(proxy)")
public void beforeAdvice(ProceedingJoinPoint poin,Object proxy){
System.out.println("前置通知");
}
複製代碼
@target:用於匹配當前目標對象類型的執行方法,其中目標對象持有指定的註解;任何目標對象持有Secure註解的類方法;這個和@within同樣必須是在目標對象上聲明這個註解,在接口上聲明的對它一樣不起做用。
@target(com.glmapper.book.common.annotaion.AuthAnnotation)
@Pointcut("@target(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
複製代碼
用於匹配當前執行的方法傳入的參數爲指定類型的執行方法;參數類型列表中的參數必須是類型全限定名,通配符不支持;args屬於動態切入點,這種切入點開銷很是大,非特殊狀況最好不要使用;
//任何一個以接受「傳入參數類型爲java.io.Serializable」開頭,
//且其後可跟任意個任意類型的參數的方法執行,
//args指定的參數類型是在運行時動態匹配的
args (java.io.Serializable,..)
//用於將參數傳入到通知方法中。
@Before("cutIn() && args(age,username)")
public void beforeAdvide(JoinPoint point, int age, String username){
//...
}
複製代碼
@args:用於匹配當前執行的方法傳入的參數持有指定註解的執行;任何一個只接受一個參數的方法,且方法運行時傳入的參數持有註解AuthAnnotation;動態切入點,相似於arg指示符;
@args (com.glmapper.book.common.annotaion.AuthAnnotation)
@Before("@args(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void beforeAdvide(JoinPoint point){
//...
}
複製代碼
使用「@annotation(註解類型)」匹配當前執行方法持有指定註解的方法;註解類型也必須是全限定類型名;
//當前執行方法上持有註解 AuthAnnotation將被匹配
@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)
//匹配鏈接點被它參數指定的AuthAnnotation註解的方法。
//也就是說,全部被指定註解標註的方法都將匹配。
@Pointcut("@annotation(com.glmapper.book.common.annotaion.AuthAnnotation)")
public void cutIn(){}
複製代碼
還有一種是bean的方式,沒用過。有興趣能夠看看。
例子在下面說到的基礎概念部分對應給出。
基礎概念部分主要將 AOP 中的一些概念點捋一捋,這部分主要參考了官網上的一些解釋。
AOP(Aspect-Oriented Programming)
, 即 面向切面編程, 它與 OOP
( Object-Oriented Programming
, 面向對象編程) 相輔相成, 提供了與 OOP
不一樣的抽象軟件結構的視角。在 OOP
中,咱們以類(class)做爲咱們的基本單元, 而 AOP
中的基本單元是 Aspect(切面)。
橫切關注點(Cross Cutting Concern
):獨立服務,如系統日誌。若是不是獨立服務(就是與業務耦合比較強的服務)就不能橫切了。一般這種獨立服務須要遍及系統各個角落,遍及在業務流程之中。
目標對象。織入 advice 的目標對象。 目標對象也被稱爲 advised object
。 由於 Spring AOP 使用運行時代理的方式來實現 aspect, 所以 adviced object 老是一個代理對象(proxied object);注意, adviced object 指的不是原來的類, 而是織入 advice 後所產生的代理類。
即Advice
應用在JoinPoint
的過程,這個過程叫織入。從另一個角度老說就是將 aspect
和其餘對象鏈接起來, 並建立 adviced object
的過程。
根據不一樣的實現技術, AOP
織入有三種方式:
Java
編譯器Advice
)生成子類的方式。Spring 採用動態代理織入, 而AspectJ採用編譯器織入和類裝載期
Spring AOP默認使用代理的是標準的JDK動態代理。這使得任何接口(或一組接口)均可以代理。
Spring AOP也可使用CGLIB代理。若是業務對象不實現接口,則默認使用CGLIB。對接口編程而不是對類編程是一種很好的作法;業務類一般會實現一個或多個業務接口。在一些特殊的狀況下,即須要通知的接口上沒有聲明的方法,或者須要將代理對象傳遞給具體類型的方法,有可能強制使用CGLIB。
咱們知道Java語言自己並不是是動態的,就是咱們的類一旦編譯完成,就很難再爲他添加新的功能。可是在一開始給出的例子中,雖然咱們沒有向對象中添加新的方法,可是已經向其中添加了新的功能。這種屬於向現有的方法添加新的功能,那能不能爲一個對象添加新的方法呢?答案確定是能夠的,使用introduction就可以實現。
introduction:動態爲某個類增長或減小方法。爲一個類型添加額外的方法或字段。Spring AOP 容許咱們爲 目標對象
引入新的接口(和對應的實現)。
切面:通知和切入點的結合。
切面實現了cross-cutting(橫切)功能。最多見的是logging模塊、方法執行耗時模塊,這樣,程序按功能被分爲好幾層,若是按傳統的繼承的話,商業模型繼承日誌模塊的話須要插入修改的地方太多,而經過建立一個切面就可使用AOP來實現相同的功能了,咱們能夠針對不一樣的需求作出不一樣的切面。
而將散落於各個業務對象之中的Cross-cutting concerns 收集起來,設計各個獨立可重用的對象,這些對象稱之爲Aspect;在上面的例子中咱們根據不一樣的配置方式,定義了四種不一樣形式的切面。
Aspect 在應用程序執行時加入業務流程的點或時機稱之爲 Joinpoint ,具體來講,就是 Advice 在應用程序中被呼叫執行的時機,這個時機多是某個方法被呼叫以前或以後(或二者都有),或是某個異常發生的時候。
環繞通知 = 前置+目標方法執行+後置通知,proceed方法就是用於啓動目標方法執行的。
環繞通知 ProceedingJoinPoint 執行 proceed 方法 的做用是讓目標方法執行 ,這 也是環繞通知和前置、後置通知方法的一個最大區別。
Proceedingjoinpoint 繼承了 JoinPoint 。是在JoinPoint的基礎上暴露出 proceed 這個方法。proceed很重要,這個是aop代理鏈執行的方法;暴露出這個方法,就能支持aop:around 這種切面(其餘的幾種切面只須要用到JoinPoint,這跟切面類型有關), 能決定是否走代理鏈仍是走本身攔截的其餘邏輯。
在環繞通知的方法中是須要返回一個Object類型對象的,若是把環繞通知的方法返回類型是void,將會致使一些沒法預估的狀況,好比:404。
匹配 join points
的謂詞。Advice
與切入點表達式相關聯, 並在切入點匹配的任何鏈接點上運行。(例如,具備特定名稱的方法的執行)。由切入點表達式匹配的鏈接點的概念是AOP
的核心,Spring
默認使用AspectJ
切入點表達式語言。
在 Spring
中, 全部的方法均可以認爲是Joinpoint
, 可是咱們並不但願在全部的方法上都添加 Advice
, 而 Pointcut
的做用就是提供一組規則(使用 AspectJ pointcut expression language
來描述) 來匹配Joinpoint
, 給知足規則的Joinpoint
添加 Advice
。
在Spring AOP
中, 全部的方法執行都是 join point
。 而 point cut
是一個描述信息,它修飾的是 join point
, 經過 point cut
,咱們就能夠肯定哪些 join point
能夠被織入Advice
。 所以join point
和 point cut
本質上就是兩個不一樣維度上的東西。
advice
是在 join point
上執行的, 而 point cut
規定了哪些 join point
能夠執行哪些 advice
。
Advice 是咱們切面功能的實現,它是切點的真正執行的地方。好比像前面例子中打印時間的幾個方法(被@Before等註解標註的方法都是一個通知);Advice 在 Jointpoint 處插入代碼到應用程序中。
BeforeAdvice,AfterAdvice,區別在於Advice在目標方法以前調用仍是以後調用,Throw Advice 表示當目標發生異常時調用Advice。
下面這張圖是在網上一位大佬的博客裏發現的,能夠幫助咱們更好的理解這些概念之間的關係。
上面是對於AOP中涉及到的一些基本概念及它們之間的關係作了簡單的梳理。
在調試程序過程當中出現的一些問題記錄
@Around("cutIn()")
public void aroundAdvice(ProceedingJoinPoint poin) {
System.out.println("環繞通知");
}
複製代碼
這裏須要注意的是再使用環繞通知時,須要給方法一個返回值。
@Around("cutIn()")
public Object aroundAdvice(ProceedingJoinPoint poin) throws Throwable {
System.out.println("環繞通知");
return poin.proceed();
}
複製代碼
在spring 4.x中 提供了aop註解方式 帶參數的方式。看下面例子:
@Pointcut(value = "execution(* com.glmapper.framerwork.service.impl.*(int,int)) && args(i,j)")
public void cutIn(int i, int j) {}
@Before(value="cutIn(i, j)",argNames = "i,j")
public void beforeMethod( int i, int j) {
System.out.println("---------begins with " + i + "-" +j);
}
複製代碼
好比說這裏,Before中有兩個int類型的參數,若是此時咱們在使用時沒有給其指定參數,那麼就會拋出:Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 異常信息。
原本是想放在一篇裏面的,可是實在太長了,就分開吧;週末更新下