我有类似的要求。长话短说,当您在 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();
// Builds a key using full class name, method name and line number
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));
}
}
}
Guard
现在,由于我更喜欢在 DI 框架中构建应用程序,因此将其声明为组件,然后注入它,然后调用实例方法可能听起来很自然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)