我有类似的需求。简而言之,当您在Spring中注入组件时,像A依赖于B,B依赖于A这样的循环依赖情况是完全可以的,但您需要将这些组件作为字段或设置器进行注入。构造函数注入会导致堆栈溢出。因此,我不得不为这些组件引入一个init()
方法,与构造函数不同,它可能会被错误地调用多次。不用说,像这样的样板代码:
private volatile boolean wasInit = false;
public void init() {
if (wasInit) {
throw new IllegalStateException("Method has already been called");
}
wasInit = true;
logger.fine("ENTRY");
...
}
随处可见开始出现。由于这并不是应用程序的关键点,我决定引入一种优雅的线程安全的一行解决方案,注重简洁而非速度:
public class Guard {
private static final Map<String, Object> callersByMethods = new ConcurrentHashMap<String, Object>();
public static void requireCalledOnce(Object source) {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
String fullClassName = stackTrace[1].getClassName();
String methodName = stackTrace[1].getMethodName();
int lineNumber = stackTrace[1].getLineNumber();
int hashCode = source.hashCode();
String key = new StringBuilder().append(fullClassName).append(' ').append(methodName).append(' ').append(lineNumber).toString();
System.out.println(key);
if (callersByMethods.put(key, source) != null) {
throw new IllegalStateException(String.format("%s@%d.%s() was called the second time.", fullClassName, hashCode, methodName));
}
}
}
现在,由于我更喜欢在 DI 框架内构建应用程序,因此将 Guard
声明为组件,然后注入它,并调用实例方法 requireCalledOnce
可能听起来很自然。但是由于其通用性,静态引用更有意义。现在我的代码看起来像这样:
private void init() {
Guard.requireCalledOnce(this);
...
}
当同一个对象的init
被第二次调用时,这里会出现异常:
Exception in thread "main" java.lang.IllegalStateException: my.package.MyComponent@4121506.init() was called the second time.
at my.package.Guard.requireCalledOnce(Guard.java:20)
at my.package.MyComponent.init(MyComponent.java:232)
at my.package.MyComponent.launch(MyComponent.java:238)
at my.package.MyComponent.main(MyComponent.java:48)