如何确保 Spring 构造一个 bean 一次
How can I ensure Spring constructs a bean once
我有一个复杂的企业环境,其中有多个项目相互依赖。配置由 spring 处理,包括疯狂的数字导入等等。
看起来默认情况下 Spring 可以多次构造同名 bean。特别是,在多个 Spring 上下文的情况下。每个上下文都有自己的同一个单例 bean 实例。在Spring架构师的心目中,单例并不是真正的单例...
不幸的是,在我的例子中,有一个 bean 永远不能创建多次。
有没有办法强制 Spring 检查 bean 是否已经创建并且不再尝试再次调用其构造函数?
特别是bean在里面创建ehcache的CacheManager失败,因为同名的CacheManager不能创建两次
<bean id="cacheService" class="somepackage.CacheServiceImpl">
<constructor-arg index="0" value="/somepackage/ehcache.xml" />
</bean>
我无法控制 CacheServiceImpl 代码。我只能更改它周围的配置。
不同的 Spring 应用程序上下文相互不了解。就每个上下文而言,您的对象 是 一个单例。但是,听起来您不希望存在多个上下文。
您如何创建上下文?您的应用程序应该创建 ApplicationContext
的单个实例(可能在 main
或附近的某个地方)。任何其他需要应用程序上下文的东西都应该注入或制作 ApplicationContextAware
.
你提到一个"complex enterprise environment"。在我工作的地方,对我们来说最有效的是让一个项目管理 Spring。假设您的所有项目都在同一个应用程序中 运行(否则 Spring 无法真正帮助您),可能有一些项目启动了一切。对我们来说,这是构建到 war 并部署到我们服务器的项目。
我终于明白了。加载上下文两次的原因是在第一次加载上下文时发生了隐藏错误。下面是冗长乏味的解释。
上下文应该在第一次调用 spring 方法 DefaultTestContext.getApplicationContext() 时加载。
有一个 class DefaultCacheAwareContextLoaderDelegate,它负责实际加载一次上下文并缓存它以备将来使用。方法代码如下:
@Override
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {
synchronized (this.contextCache) {
ApplicationContext context = this.contextCache.get(mergedContextConfiguration);
if (context == null) {
try {
context = loadContextInternal(mergedContextConfiguration);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Storing ApplicationContext in cache under key [%s]",
mergedContextConfiguration));
}
this.contextCache.put(mergedContextConfiguration, context);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load ApplicationContext", ex);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved ApplicationContext from cache with key [%s]",
mergedContextConfiguration));
}
}
this.contextCache.logStatistics();
return context;
}
}
显然,当在 loadContextInternal() 调用期间抛出 Exception 以外的 Throwable 时,应用程序上下文不会放入缓存中。
显然,即使是单个 JUnit 测试 运行 getApplicationContext() 方法也被调用了不止一次,期望第二次不需要再次加载它,因为它已经被缓存了。
如果在第一次加载期间抛出错误,它将被掩埋并且 JUnit/Spring 继续测试直到它第二次调用 getApplicationContext()。这就是它捕获异常并崩溃的地方,因为 ehcache 不希望 CacheManager 被初始化多次。
我有一个复杂的企业环境,其中有多个项目相互依赖。配置由 spring 处理,包括疯狂的数字导入等等。
看起来默认情况下 Spring 可以多次构造同名 bean。特别是,在多个 Spring 上下文的情况下。每个上下文都有自己的同一个单例 bean 实例。在Spring架构师的心目中,单例并不是真正的单例...
不幸的是,在我的例子中,有一个 bean 永远不能创建多次。
有没有办法强制 Spring 检查 bean 是否已经创建并且不再尝试再次调用其构造函数?
特别是bean在里面创建ehcache的CacheManager失败,因为同名的CacheManager不能创建两次
<bean id="cacheService" class="somepackage.CacheServiceImpl">
<constructor-arg index="0" value="/somepackage/ehcache.xml" />
</bean>
我无法控制 CacheServiceImpl 代码。我只能更改它周围的配置。
不同的 Spring 应用程序上下文相互不了解。就每个上下文而言,您的对象 是 一个单例。但是,听起来您不希望存在多个上下文。
您如何创建上下文?您的应用程序应该创建 ApplicationContext
的单个实例(可能在 main
或附近的某个地方)。任何其他需要应用程序上下文的东西都应该注入或制作 ApplicationContextAware
.
你提到一个"complex enterprise environment"。在我工作的地方,对我们来说最有效的是让一个项目管理 Spring。假设您的所有项目都在同一个应用程序中 运行(否则 Spring 无法真正帮助您),可能有一些项目启动了一切。对我们来说,这是构建到 war 并部署到我们服务器的项目。
我终于明白了。加载上下文两次的原因是在第一次加载上下文时发生了隐藏错误。下面是冗长乏味的解释。
上下文应该在第一次调用 spring 方法 DefaultTestContext.getApplicationContext() 时加载。
有一个 class DefaultCacheAwareContextLoaderDelegate,它负责实际加载一次上下文并缓存它以备将来使用。方法代码如下:
@Override
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) {
synchronized (this.contextCache) {
ApplicationContext context = this.contextCache.get(mergedContextConfiguration);
if (context == null) {
try {
context = loadContextInternal(mergedContextConfiguration);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Storing ApplicationContext in cache under key [%s]",
mergedContextConfiguration));
}
this.contextCache.put(mergedContextConfiguration, context);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load ApplicationContext", ex);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved ApplicationContext from cache with key [%s]",
mergedContextConfiguration));
}
}
this.contextCache.logStatistics();
return context;
}
}
显然,当在 loadContextInternal() 调用期间抛出 Exception 以外的 Throwable 时,应用程序上下文不会放入缓存中。
显然,即使是单个 JUnit 测试 运行 getApplicationContext() 方法也被调用了不止一次,期望第二次不需要再次加载它,因为它已经被缓存了。
如果在第一次加载期间抛出错误,它将被掩埋并且 JUnit/Spring 继续测试直到它第二次调用 getApplicationContext()。这就是它捕获异常并崩溃的地方,因为 ehcache 不希望 CacheManager 被初始化多次。