學習AOP已經(jīng)很多年了,但是想起來總有點模糊,現(xiàn)在做下梳理,用最簡單直接的方式做個總結(jié)。
一定要動手!?。。。。。。。。。。。。?!
使用SpringBoot項目,首先添加maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
先認識有幾個核心概念:
AOP:Aspect Oriented Programming,面向切面編程。
連接點(Joinpoint):被切入的方法。
增強(Advice):一共有5種
- @Before:前置增強,就是在目標方法執(zhí)行之前執(zhí)行;
- @AfterReturning:后置增強,方法退出時執(zhí)行;
- @AfterThrowing:有異常時該方法執(zhí)行;
- @After:最終增強,無論什么情況都會執(zhí)行;
- @Afround:環(huán)繞增強
切點(Pointcut):標記被切入的方法,連接了連接點和增強。
@Component
@Aspect
public class WorkAspect {
// 第一種方式:直接使用切點表達式
@Before("execution(* com.example.aop.Worker.work())")
@Order(1)
public void before1(JoinPoint joinPoint) {
System.out.println("before1: " + joinPoint.getSignature());
}
// 第二種方式: 切點(表達式)
@Pointcut("execution(* com.example.aop.Worker.work())")
public void point() {
}
@Before("point()")
@Order(2)
public void before2(JoinPoint joinPoint) {
System.out.println("before2: " + joinPoint.getSignature());
}
// 第三種方式: 切點(注解)
@Pointcut("@annotation(com.example.aop.Cache)")
public void point2() {
}
@Order(3)
@Before("point2()")
public void before3(JoinPoint joinPoint) {
System.out.println("before3: " + joinPoint.getSignature());
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Cache {
}
@Component
public class Worker {
@Cache
public void work() {
System.out.println("cnt: " + (++cnt));
}
private int cnt;
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
@Override
public String toString() {
return "Work{" +
"cnt=" + cnt +
'}';
}
}
再看下測試代碼:
@ComponentScan("com.example.aop")
@Configuration
@EnableAspectJAutoProxy
public class Config {
}
public class AopMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContex =
new AnnotationConfigApplicationContext(Config.class);
Worker worker = applicationContex.getBean(Worker.class);
worker.work();
}
}
測試結(jié)果:
before1: void com.example.aop.Worker.work()
before2: void com.example.aop.Worker.work()
before3: void com.example.aop.Worker.work()
cnt: 1
2019-03-13深入:
代理類是一個空殼(或外觀),它背后才是真正的類,通常稱為目標類。由此得出代理類要包含目標類。
當進入目標對象的方法后,執(zhí)行的上下文已經(jīng)變成目標對象本身了,因為目標對象的代碼是我們自己寫的,和事務沒有半毛錢關系,此時你再調(diào)用帶注解的方法,照樣沒有事務,只是一個普通的方法調(diào)用而已。
@Transactional:Spring選擇讓protected方法和package方法不支持事務,所以只有public方法支持事務。
只要是以代理方式實現(xiàn)的聲明式事務,無論是JDK動態(tài)代理,還是CGLIB直接寫字節(jié)碼生成代理,都只有public方法上的事務注解才起作用。
而且必須在代理類外部調(diào)用才行,如果直接在目標類里面調(diào)用,事務照樣不起作用。
//是否是JDK動態(tài)代理
System.out.println("isJdkDynamicProxy => " + AopUtils.isJdkDynamicProxy(exampleService));
//是否是CGLIB代理
System.out.println("isCglibProxy => " + AopUtils.isCglibProxy(exampleService));
//代理類的類型
System.out.println("proxyClass => " + exampleService.getClass());
//代理類的父類的類型
System.out.println("parentClass => " + exampleService.getClass().getSuperclass());
//代理類的父類實現(xiàn)的接口
System.out.println("parentClass's interfaces => " + Arrays.asList(exampleService.getClass().getSuperclass().getInterfaces()));
//代理類實現(xiàn)的接口
System.out.println("proxyClass's interfaces => " + Arrays.asList(exampleService.getClass().getInterfaces()));
//代理對象
System.out.println("proxy => " + exampleService);
//目標對象
System.out.println("target => " + AopProxyUtils.getSingletonTarget(exampleService));
//代理對象和目標對象是不是同一個
System.out.println("proxy == target => " + (exampleService == AopProxyUtils.getSingletonTarget(exampleService)));
//目標類的類型
System.out.println("targetClass => " + AopProxyUtils.getSingletonTarget(exampleService).getClass());
//目標類實現(xiàn)的接口
System.out.println("targetClass's interfaces => " + Arrays.asList(AopProxyUtils.getSingletonTarget(exampleService).getClass().getInterfaces()));