http://www.itdecent.cn/p/b114eebcd2e9
重要概念:
Aspect(切面):
切面由切點與通知組成,可以看出是具有相同類型功能的一個類,比如日志輸出與事務管理,就可以看成是兩個切面。
Joint point(連接點):
連接點就是一些需要增強方法,spring也只支持方法類型的連接點,spring也只能對方法進行增強。
Pointcut(切點):
表示一組 joint point(因為按照功能分,可以分為很多切面,而每個切面并不是對所有的連接點都進行服務,所以按功能將一些連接點組合起來,這些就是某個切面的切點,不同切面的功能不一樣,所以連接點組合不一樣,切點也就不一樣),這些 joint point 或是通過邏輯關系組合起來,或是通過通配、正則表達式等方式集中起來,切面切入的地方。
Advice(通知):
Advice 定義了在 Pointcut 里面定義的連接點具體要做的操作,它通過 before、after 和 around 來區(qū)別是在每個 joint point 之前、之后還是環(huán)繞執(zhí)行。
Target(目標對象):
織入 Advice 的目標對象.。可以理解為一個bean,一個被代理對象。
Weaving(織入):
將切面和目標對象連接起來, 并創(chuàng)建代理對象的過程。spring的織入可以在編譯的時候,也可以在運行的時候,編譯的時候織入就是靜態(tài)代理,運行的時候織入就是動態(tài)代理。
Advisor(顧問):
Advisor是切面的另外一種實現(xiàn),能夠將通知以更為復雜的方式織入到目標對象中,是將通知包裝為更復雜切面的裝配器。Advisor由切點和Advice組成。與Aspect不同的就是切面可以有很多通知與切點,而顧問只有一個切點與通知。
基于注解的方式實現(xiàn)aop
@Component
@Aspect
public class MyTestAspect {
//需要注意切點必須配置在切面里面(用@Aspect修飾的類里面)
@Pointcut("execution(* com.dahuici.zyb.service.impl.UserServiceImpl.save(..))")
public void pointcut(){}
//配置前置通知
@Before("pointcut()")
public void before(){
System.out.println("前置通知");
}
//配置后置通知
@After("pointcut()")
public void after(){
System.out.println("后置通知");
}
}
測試與輸出
@Test
public void testLamda(){
//該userService是一個bean
userService.save();
}

spring的aop原理(個人查看博客后的理解):spring在加載bean的時候,會判斷該bean是否是切面(用了@Aspect),如果是切面,它會將切面分解成一個一個的(顧問)Advisor,然后在創(chuàng)建號所有的bean后會用這些advisor對bean進行判斷是否滿足切點條件,如果滿足,就將advice通知注入進去(實際就是創(chuàng)建一個代理對象來替換掉原來的bean,然后就實現(xiàn)了對bean的aop增強)。需要注意的是,默認情況下,自動注入切面創(chuàng)建代理bean這個功能是關閉的,需要手動打開。
自定義Advisor
首先必須知道幾個對象:Advice,PointCut,MethodMatcher,Advisor的關系
Advisor(顧問)由Advice(通知)與PointCut(切點)組成,Advice(通知)用于執(zhí)行具體需要增強的邏輯,切點用于設置那些方法需要增強,而PointCut(切點)是通過MethodMatcher對象篩選出那些類與方法需要增強。
Advisor
package com.dahuici.zyb.advisor;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.stereotype.Component;
@Component
public class MyAdvisor implements PointcutAdvisor {
@Override
//使用自定義的切點
public Pointcut getPointcut() {
return new MyPointCut();
}
@Override
//使用自定義的前置增強
public Advice getAdvice() {
return new MyBeforeAdvice();
}
//目前該方法作用是啥不知道
@Override
public boolean isPerInstance() {
return true;
}
}
切點
package com.dahuici.zyb.advisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
public class MyPointCut implements Pointcut {
//用于過濾類,先過濾類,如果類不滿足,
// 那么就不會判斷方法是否匹配
@Override
public ClassFilter getClassFilter() {
//這里返回ClassFilter.TRUE表示所有類都匹配
return ClassFilter.TRUE;
}
@Override
//用于過濾方法,需要一個MyMethodMatcher用于方法邏輯判斷
public MethodMatcher getMethodMatcher() {
return new MyMethodMatcher();
}
}
切點的方法匹配器MethodMatcher
package com.dahuici.zyb.advisor;
import org.springframework.aop.MethodMatcher;
import java.lang.reflect.Method;
public class MyMethodMatcher implements MethodMatcher {
@Override
//用得最多的是這個方法,另外兩個方法不知干啥用的,暫時就都返回false
//判斷該方法上有@MyAnnotation
public boolean matches(Method method, Class<?> aClass) {
//注意自定義注解必須使用內(nèi)置注解@Retention(RetentionPolicy.RUNTIME)
//不然獲取不了
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
//判斷方法是否使用了MyAnnotation注解。
if (annotation != null) return true;
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> aClass, Object... objects) {
return false;
}
}
自定義前置通知
package com.dahuici.zyb.advisor;
import com.dahuici.zyb.controller.MyTestController;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyBeforeAdvice implements MethodBeforeAdvice {
/*
* 參數(shù):
* method-當前執(zhí)行的方法
* objects-當前執(zhí)行方法的參數(shù)數(shù)組(這里可以對參數(shù)進行修改)
* o-當前執(zhí)行的方法的實例對象
*
* */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("我的前置通知");
}
}
自定義注解
package com.dahuici.zyb.advisor;
import java.lang.annotation.*;
//@Target(ElementType.METHOD)
//使用該注解設置為RetentionPolicy.RUNTIME
// 才能通過反射訪問到
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
String value() default "";
}
配置spring自動掃描Advisor并創(chuàng)建代理
這一步非常重要,如果不添加該bean,那么spring掃描到Advisor后也不會創(chuàng)建代理對象替換掉原來的bean。
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
給測試方法添加注解并測試
@GetMapping("/isLogin")
@MyAnnotation
public String isLogin(){
System.out.println("已經(jīng)登錄");
return "isLogin";
}
測試結果:

shiro的啟用注解授權大概就是使用的這種方式,也是通過Advisor代理來實現(xiàn)注解授權和角色驗證的。除了以上的前置通知以外,還有后置通知于環(huán)繞通知,實現(xiàn)方式如下。
后置通知
package com.dahuici.zyb.advisor;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAfterAdvice implements AfterReturningAdvice {
/*
* 參數(shù):
* method-當前執(zhí)行的方法
* objects-當前執(zhí)行方法的參數(shù)數(shù)組(這里可以對參數(shù)進行修改)
* o-代理方法的返回值對象
* o1-代理的目標對象,注意是目標對象,不是代理對象
*
* */
@Override
public void afterReturning(Object o, Method method,
Object[] objects, Object o1) throws Throwable {
System.out.println("后置通知"+o1);
}
}
環(huán)繞通知
package com.dahuici.zyb.advisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("前");
//執(zhí)行目標方法,proceed為目標方法的返回值
Object proceed = methodInvocation.proceed();
System.out.println(proceed);
System.out.println("后");
return proceed;
}
}