首先,让我们看一下实用程序类(大多数javadoc已经移除到简单的示例中):
public class ApplicationContextUtils {
/**
* The application context; care should be taken to ensure that 1) this
* variable is assigned exactly once (in the
* {@link #setContext(ApplicationContext)} method, 2) the context is never
* reassigned to {@code null}, 3) access to the field is thread-safe (no race
* conditions can occur)
*/
private static ApplicationContext context = null;
public static ApplicationContext getContext() {
if (!isInitialized()) {
throw new IllegalStateException(
"Context not initialized yet! (Has the "
+ "ApplicationContextProviderBean definition been configured "
+ "properly and has the web application finished "
+ "loading before you invoked this method?)");
}
return context;
}
public static boolean isInitialized() {
return context == null;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(final String name, final Class<T> requiredType) {
if (requiredType == null) {
throw new IllegalArgumentException("requiredType is null");
}
return (T) getContext().getBean(name, requiredType);
}
static synchronized void setContext(final ApplicationContext theContext) {
if (theContext == null) {
throw new IllegalArgumentException("theContext is null");
}
if (context != null) {
throw new IllegalStateException(
"ApplicationContext already initialized: it cannot be done twice!");
}
context = theContext;
}
private ApplicationContextUtils() {
throw new AssertionError(); // NON-INSTANTIABLE UTILITY CLASS
}
}最后,下面的实际上调用了'setContext‘方法:
public final class ApplicationContextProviderBean implements
ApplicationContextAware {
public void setApplicationContext(
final ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtils.setContext(applicationContext);
}
}在应用程序启动后,Spring将调用一次setApplicationContext方法。假设nincompoop以前没有调用ApplicationContextUtils.setContext(),那么应该锁定对实用程序类中上下文的引用,从而允许对getContext()的调用成功(这意味着isInitialized()返回true)。
我只想知道这个类是否违反了任何良好编码实践的原则,特别是在线程安全方面(但其他发现的愚蠢之处是受欢迎的)。
谢谢你帮助我成为一个更好的程序员,StackOverflow!
你好,莱斯
P.S.我没有深入说明为什么我需要这个实用程序类--让我确实有一个合法的需求,从应用程序中的任何地方(当然,在spring上下文加载之后)访问它。
发布于 2009-11-11 19:56:44
不是的。这不是线的安全。
写入context类变量对于通过getContext()读取该变量的线程不一定是可见的。
至少,声明context为volatile。理想情况下,通过这样的调用将context重新定义为AtomicReference:
if(!context.compareAndSet(null, theContext))
throw new IllegalStateException("The context is already set.");下面是一个更完整的例子:
public class ApplicationContextUtils {
private static final AtomicReference<ApplicationContext> context =
new AtomicReference<ApplicationContext>();
public static ApplicationContext getContext() {
ApplicationContext ctx = context.get();
if (ctx == null)
throw new IllegalStateException();
return ctx;
}
public static boolean isInitialized() {
return context.get() == null;
}
static void setContext(final ApplicationContext ctx) {
if (ctx == null)
throw new IllegalArgumentException();
if (!context.compareAndSet(null, ctx))
throw new IllegalStateException();
}
public static <T> T getBean(final String name, final Class<T> type) {
if (type == null)
throw new IllegalArgumentException();
return type.cast(getContext().getBean(name, type));
}
private ApplicationContextUtils() {
throw new AssertionError();
}
}注意,除了线程安全之外,这还提供了类型安全性,利用了传递到getBean()方法的getBean()实例。
我不确定您打算如何使用isInitialized()方法;对我来说,它似乎不太有用,因为一旦您调用它,条件就会改变,而且您也没有一个很好的方法来通知它。
发布于 2009-11-29 20:00:49
Spring已经有一个名为ContextSingletonBeanFactoryLocator的类,它将静态访问连接到ApplicationContext。至少,使用这个类可能会为您省去担心自定义方法是否线程安全的麻烦。
不过,一开始使用这个类有点让人费解,因为存在一些间接的情况。有关此调用如何工作的更多信息,您可以查看这篇博客文章。
https://stackoverflow.com/questions/1717625
复制相似问题