### spring aop嵌套調用問題
在使用 Spring AOP 的時候,我們從 IOC 容器中獲取的 Service Bean 對象其實都是代理對象,而不是那些 Service Bean 對象本身,也就是說獲取的并不是被代理對象或代理目標。當我在自己的 Service 類中使用 this 關鍵字嵌套調用同類中的其他方法時,由于 this 關鍵字引用的并不是該 Service Bean 對象的代理對象,而是其本身,故 Spring AOP 是不能攔截到這些被嵌套調用的方法的
當同一個類中對兩個方法增強后,一個方法調用另外一個方法時,產生了嵌套調用,其中一個切面就會失效
具體代碼:
~~~
public void hello() {
System.out.println("Hello,IOC");
goodbye();
}
public void goodbye() {
System.out.println("Goodbye");
}
@Pointcut("execution(public * io.github.dunwu.spring.core.aop.example.IOCServiceImpl.hello(..))")
public void testAOP1(){
}
@Around("testAOP1()")
public Object around(ProceedingJoinPoint p){
System.out.println("around before testAOP1...");
Object o = null;
try {
o = p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around after testAOP1...");
return o;
}
@Pointcut("execution(public * io.github.dunwu.spring.core.aop.example.IOCServiceImpl.goodbye(..))")
public void testAOP2(){
}
@Around("testAOP2()")
public void around(ProceedingJoinPoint p){
System.out.println("around before testAOP2...");
Object o = null;
try {
p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("around after testAOP2...");
return o;
}
~~~
調用hello方法時,會發生什么呢?
只有hello方法被增強了,goodbye方法直接被調用,并沒有被增強。輸出如下:
~~~undefined
around before testAOP1...
Hello,IOC
Goodbye
around after testAOP1...
~~~
** 為什么goodbye方法沒有被增強 **
這是因為hello方法被增強之后,invoke hello方法時,調用goodbye其實是調用this.goodbye方法,也就是原始方法而并不是被增強的方法
### 原理
```
public interface ICustomerService {
public void doSomething1();
public void doSomething2();
}
public class CustomerServiceImpl implements ICustomerService {
public void doSomething1() {
System.out.println("Inside CustomerServiceImpl.doSomething1()");
doSomething2();
}
public void doSomething2() {
System.out.println("Inside CustomerServiceImpl.doSomething2()");
}
}
public class CustomerServiceProxy implements ICustomerService {
private ICustomerService customerService;
public void setCustomerService(ICustomerService customerService) {
this.customerService = customerService;
}
public void doSomething1() {
doBefore();
customerService.doSomething1();
doAfter();
}
public void doSomething2() {
doBefore();
customerService.doSomething2();
doAfter();
}
private void doBefore() {
// 例如,可以在此處開啟事務
System.out.println("do some important things before...");
}
private void doAfter() {
// 例如,可以在此處提交或回滾事務、釋放資源等等
System.out.println("do some important things after...");
}
}
public class TestProxy {
public static void main(String[] args) {
// 創建代理目標對象。對于Spring來說,這一工作
// 是由Spring DI容器完成的。
ICustomerService serviceProxyTarget = new CustomerServiceImpl();
// 創建代理對象。對于Spring來說,這一工作
// 也是由Spring DI容器完成的。
CustomerServiceProxy serviceProxy = new CustomerServiceProxy();
serviceProxy.setCustomerService(serviceProxyTarget);
ICustomerService serviceBean = (ICustomerService) serviceProxy;
// 調用業務邏輯操作
serviceBean.doSomething1();
}
}
```
現在以調試方式運行這個應用,你會發現在 doSomething1() 中調用 doSomething2() 方法的時候并未去執行CustomerServiceProxy 類的 doBefore()、doAfter() 方法。再來看看這句關鍵代碼:doSomething2(); 把它隱含的意思也表達出來吧:this.doSomething2(); 在CustomerServiceImpl類中this關鍵字表示的是當前這個CustomerServiceImpl類的實例。那程序當然就會去執行 CustomerServiceImpl 類中的 doSomething2() 方法了,而不會去執行 CustomerServiceProxy 類中的 doSomething2() 方法
### 解決方案
1. 將自身注入到自身
```
public class OneBean {
@Resource
private OneBean oneBean;
public OneBean() {
System.out.println("構造器OneBean加載..." + this);
}
public String say(String a) {
System.out.println("say a=" + a);
oneBean.say2(a);
return a + a;
}
public String say2(String a) {
System.out.println("say2 a=" + a);
return a + a;
}
```
2. 使用AopContext.currentProxy()來操作
```
public class OneBean {
public OneBean() {
System.out.println("構造器OneBean加載..." + this);
}
public String say(String a) {
System.out.println("say a=" + a);
((OneBean)AopContext.currentProxy()).say2(a) ;
return a + a;
}
public String say2(String a) {
System.out.println("say2 a=" + a);
return a + a;
}
}
```
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI