在运行具有大量CPU 核的Tomcat的大型服务器上,由于同步块,在getBeanFactory()中看到大量线程阻塞。这是因为我们使用XmlWebApplicationContext,并且在Web请求期间每次查找bean时都需要bean工厂。
在Web应用程序之外,我们还使用GenericXmlApplicationContext拥有另一个上下文,而这个上下文没有此问题。
哪个代码路径通常会碰到那里的障碍? WebApplicationContext引用上某种形式的getBean查找,每次都在内部委派给BeanFactory吗?我们的内部查找通常会保留内部BeanFactory并直接对其进行操作,这也可能是定制检索代码的一种出路。
就是说,可以使用一些更细粒度的锁定来对这个(相当古老的,2008 年spring编写的锁)beanFactoryMonitor锁进行重做,以进行读取访问,甚至有可能用作访问的volatile字段以及(重新)初始化和关闭的专用锁。
这是第一个有趣的纯Spring版本(TenantIgnoreXmlWebApplicationContext是我们的类,但没重写containsBean()或任何方式)。似乎每个通过org.springframework.web.multipart.support.MultipartFilter的请求始终在进行bean查找,并且总是在访问getBeanFactory()。
...
TenantIgnoreXmlWebApplicationContext(AbstractRefreshableApplicationContext).getBeanFactory() line: 175
TenantIgnoreXmlWebApplicationContext(AbstractApplicationContext).containsBean(String) line: 1146
MultipartFilter.lookupMultipartResolver() line: 157
MultipartFilter.lookupMultipartResolver(HttpServletRequest) line: 143
MultipartFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 108
MultipartFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 119
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 193
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 166
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 320
FilterSecurityInterceptor.invoke(FilterInvocation) line: 127
FilterSecurityInterceptor.doFilter(ServletRequest, ServletResponse, FilterChain) line: 91
确实,spring的某些过滤器实现以及某些Web集成的委托(比如JSF)在WebApplicationContext级别调用getBean方法,因此需要加锁。
尽管我们可以修改这些位置以保留嵌套的BeanFactory,但将AbstractRefreshableApplicationContext切换到volatile beanFactory字段似乎更具吸引力。由于AbstractApplicationContext已经应用了startupShutdownMonitor,因此我们应该能够完全摆脱beanFactoryMonitor,而始终访问volatile字段。
我不确定在哪里有嵌套的BeanFactory,因为在我们的堆栈,总是只有这两个方法位于我们自己对getBean(String)的调用之上:
当我们以下面的方式查找当前的Web应用程序上下文时,可能是在调用之前缺少了某些东西。 这是我们遗留代码库所需要的一种静态实用程序方法,以防万一。
private static ApplicationContext getWebApplicationContextIfExists()
{
ServletContext servletContext = null;
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes)
{
servletContext = ((ServletRequestAttributes) requestAttributes).getRequest().getServletContext();
}
...
if (servletContext != null)
{
final ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if (context == null)
{
if (log.isDebugEnabled())
{
log.debug("No web spring configuration for webapp " + servletContext.getServletContextName()
+ " found, using only core configuration.");
}
}
else
{
…
return context;
}
}
return null;
}