首先,你的方面類應該有一個@Aspect
註釋。其次,如果你想使用Spring AOP而不是完整的AspectJ,你的方面和所有的目標類都應該是Spring @Component
s。
話雖如此,這裏是一個小樣本。我用普通的AspectJ創建了它,但Spring AOP中的方面代碼應該是相同的。
Helper類使代碼編譯並運行:
package de.scrum_master.app;
import java.util.Random;
public class Host {
private static final Random RANDOM = new Random();
private String name;
public Host(String name) {
this.name = name;
}
public void doSomething() {
if (RANDOM.nextBoolean())
throw new RuntimeException("oops!");
}
@Override
public String toString() {
return "Host(name=" + name + ")";
}
}
package de.scrum_master.app;
import java.util.HashMap;
import java.util.Map;
public class ConfigReader {
private Map<Integer, Host> hostMap = new HashMap<>();
public ConfigReader() {
hostMap.put(1, new Host("mercury"));
hostMap.put(2, new Host("venus"));
hostMap.put(3, new Host("earth"));
hostMap.put(4, new Host("mars"));
}
public Map<Integer, Host> getHostMap() {
return hostMap;
}
}
驅動程序:
我不喜歡Iterator
這是從老版本的JDK的遺物,所以我用更現代的Java風格for
循環取而代之。
package de.scrum_master.app;
class MyClass {
private ConfigReader configReader = new ConfigReader();
void check() throws Exception {
for (Host host : configReader.getHostMap().values()) {
System.out.println(host);
host.doSomething();
}
}
public static void main(String[] args) throws Exception {
new MyClass().check();
}
}
看點與切入點/建議做記錄和異常在同一時間處理:
另外請注意,我的意見在代碼的最後一刻。
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AopLogger {
private static final InheritableThreadLocal<String> indent = new InheritableThreadLocal<String>() {
@Override
protected String initialValue() {
return "";
}
};
@Around("execution(* de.scrum_master.app..*(..)) && !execution(* toString())")
public Object logAround(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Object result = null;
System.out.println(indent.get() + ">> " + thisJoinPoint);
try {
indent.set(indent.get() + " ");
result = thisJoinPoint.proceed();
indent.set(indent.get().substring(2));
} catch (Exception e) {
System.out.println(indent.get() + "Caught exception: " + e);
indent.set(indent.get().substring(2));
}
System.out.println(indent.get() + "<< " + thisJoinPoint);
// Attention: If a method with a caught exception does not have 'void'
// return type, we return a (probably unexpected) result of 'null' here.
// So maybe we should not catch all execptions but rather pick more
// specific joinpoints where we are sure we can cleanly handle the
// corresponding exceptions.
return result;
}
}
控制檯日誌:
>> execution(void de.scrum_master.app.MyClass.main(String[]))
>> execution(void de.scrum_master.app.MyClass.check())
>> execution(Map de.scrum_master.app.ConfigReader.getHostMap())
<< execution(Map de.scrum_master.app.ConfigReader.getHostMap())
Host(name=mercury)
>> execution(void de.scrum_master.app.Host.doSomething())
Caught exception: java.lang.RuntimeException: oops!
<< execution(void de.scrum_master.app.Host.doSomething())
Host(name=venus)
>> execution(void de.scrum_master.app.Host.doSomething())
<< execution(void de.scrum_master.app.Host.doSomething())
Host(name=earth)
>> execution(void de.scrum_master.app.Host.doSomething())
Caught exception: java.lang.RuntimeException: oops!
<< execution(void de.scrum_master.app.Host.doSomething())
Host(name=mars)
>> execution(void de.scrum_master.app.Host.doSomething())
Caught exception: java.lang.RuntimeException: oops!
<< execution(void de.scrum_master.app.Host.doSomething())
<< execution(void de.scrum_master.app.MyClass.check())
<< execution(void de.scrum_master.app.MyClass.main(String[]))
第二個方面的變體分離異常處理記錄:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AopLogger {
private static final InheritableThreadLocal<String> indent = new InheritableThreadLocal<String>() {
@Override
protected String initialValue() {
return "";
}
};
@Around("execution(* de.scrum_master.app..*(..)) && !execution(* toString())")
public Object logAround(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(indent.get() + ">> " + thisJoinPoint);
try {
indent.set(indent.get() + " ");
Object result = thisJoinPoint.proceed();
indent.set(indent.get().substring(2));
System.out.println(indent.get() + "<< " + thisJoinPoint);
return result;
} catch (Exception e) {
indent.set(indent.get().substring(2));
System.out.println(indent.get() + "<< " + thisJoinPoint);
throw e;
}
}
@Around("execution(void de.scrum_master.app.Host.doSomething())")
public void handleException(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
thisJoinPoint.proceed();
} catch (Exception e) {
System.out.println(indent.get() + "Caught exception: " + e);
}
}
}
日誌輸出保持不變,但這次的異常處理是在一個更精確的切入點單獨建議。日誌建議只關注日誌記錄(如果不是正確的縮進,甚至不需要嘗試捕獲)。異常處理建議只做自己的工作。
隨意問後續問題。
你沒有提供足夠的信息;如果連接點在doSomething方法中(假設它是公共的),那麼它應該足夠讓try catch塊返回proceedingJoinPoint.proceed()。 「什麼對你有好處......」當你進入doSomething時,以及方法何時返回,你可能會記錄下來;其實很奇怪,在代碼中缺少這個。 –
當我把try catch塊返回proceedingJoinPoint.proceed()迭代結束,應用程序退出。當我把try catch塊放在host.doSomething()周圍時,迭代繼續,但是我不能在logAround – user2683906
中記錄任何東西,首先找出連接點是什麼:在你的IDE中放置一個斷點,返回proceedingJoinPoint.proceed()和檢查ProceedingJoinPoint的各個領域 - 你應該看到哪個方法被攔截。嘗試從那裏推理。 –