当我大约10年前开始我的职业生涯时,Struts MVC就是市场上的常态。 但是,多年来,我观察到Spring MVC逐渐流行起来。 鉴于Spring MVC与Spring容器的无缝集成以及它提供的灵活性和可扩展性,这对我来说并不奇怪。
从到目前为止的Spring旅程中,我通常会看到人们在配置Spring框架时犯了一些常见的错误。 与人们仍然使用Struts框架的时间相比,这种情况发生的频率更高。 我想这是灵活性和可用性之间的权衡。 另外,Spring文档中有很多示例,但缺乏解释。 为了填补这一空白,本文将尝试阐述和解释我经常看到的3个常见问题。
在Servlet上下文定义文件中声明bean
因此,我们每个人都知道Spring使用ContextLoaderListener加载Spring应用程序上下文。 不过,当宣布
DispatcherServlet ,我们需要创建名称为“ $ {servlet.name} -context.xml”的servlet上下文定义文件。 有没有想过为什么?
应用程序上下文层次结构
并非所有开发人员都知道Spring应用程序上下文具有层次结构。 让我们看一下这种方法:
org.springframework.context.ApplicationContext.getParent()
它告诉我们Spring Application Context具有父级。 那么,这个父母干什么呢?
如果下载源代码并进行快速引用搜索,则应该发现Spring Application Context将parent作为其扩展名。 如果您不介意阅读代码,请让我向您展示方法BeanFactoryUtils.beansOfTypeIn includedAncestors()中的用法示例:
if (lbf instanceof HierarchicalBeanFactory) {HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {Map parentResult = beansOfTypeIncludingAncestors((ListableBeanFactory) hbf.getParentBeanFactory(), type);...}
}
return result;
}
如果遍历整个方法,您将发现在搜索父上下文之前,Spring Application Context会扫描以在内部上下文中查找bean。 通过这种策略,Spring Application Context将有效地进行反向广度优先搜索以查找bean。
ContextLoaderListener
这是每个开发人员都应该知道的众所周知的类。 它有助于从预定义的上下文定义文件中加载Spring应用程序上下文。 由于实现了ServletContextListener ,因此将在加载Web应用程序后立即加载Spring应用程序上下文。 当加载包含带有@PostContruct批注或批处理作业的bean的Spring容器时,这带来了无可争议的好处。
相反,在初始化servlet之前,不会构造servlet上下文定义文件中的任何bean定义。 何时初始化Servlet? 这是不确定的。 在最坏的情况下,您可能需要等到用户对servlet映射URL进行第一次点击才能加载spring上下文。
根据以上信息,您应该在哪里声明所有珍贵的豆子? 我觉得这样做的最佳位置是ContextLoaderListener加载的上下文定义文件,而没有其他地方。 这里的窍门是将ApplicationContext作为servlet属性存储在键org.springframework.web.context.WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE下
稍后, DispatcherServlet将从ServletContext加载此上下文,并将其分配为父应用程序上下文。
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());...
}
由于这种行为,强烈建议创建一个空的servlet应用程序上下文定义文件,并在父上下文中定义您的bean。 这将有助于避免在加载Web应用程序时重复创建Bean,并确保立即执行批处理作业。
从理论上讲,在servlet应用程序上下文定义文件中定义bean会使该bean唯一且仅对该servlet可见。 但是,在使用Spring的8年中,除了定义Web Service端点之外,我几乎没有发现此功能的任何用途。
在
这是一个小错误,但是如果您不注意它,它将引起您的注意。 Log4jConfigListener是我在-Dlog4j.configuration首选的解决方案,我们可以控制的log4j加载不改变服务器的引导过程。
显然,这应该是在web.xml中声明的第一个侦听器。 否则,您浪费所有的时间来声明正确的日志记录配置。
由于对Bean的探索管理不善而复制了Bean
在Spring的早期,开发人员在xml文件上打字的时间比Java类花费的时间更多。 对于每个新bean,我们需要自己声明和连接依赖项,这是干净,整洁但非常痛苦的。 毫无疑问,Spring框架的更高版本向更高的可用性发展。 如今,开发人员可能只需要声明事务管理器,数据源,属性源,Web服务端点,其余的就可以进行组件扫描和自动装配。
我喜欢这些新功能,但是这种强大的力量需要承担巨大的责任。 否则,事情会很快变得混乱。 XML文件中的组件扫描和bean声明是完全独立的。 因此,如果对bean进行注释以进行组件扫描并手动进行声明,则在bean容器中完全可能具有相同类的相同bean。 幸运的是,这种错误应该只发生在初学者身上。
当我们需要将一些嵌入式组件集成到最终产品中时,情况变得更加复杂。 然后,我们确实需要一种策略来避免重复的bean声明。
上图显示了我们日常生活中遇到的种种现实问题。 大多数情况下,系统是由多个组件组成的,通常,一个组件可为多个产品提供服务。 每个应用程序和组件都有自己的bean。 在这种情况下,最好的声明方式是避免重复的bean声明?
这是我建议的策略:
- 确保每个组件都必须以专用的软件包名称开头。 当我们需要进行组件扫描时,它使我们的工作变得更轻松。
- 不要指示开发组件的团队采用在组件本身中声明Bean的方法(注释与xml声明)。 开发人员负责将组件打包到最终产品中,以确保没有重复的bean声明。
- 如果组件中包含上下文定义文件,请给它一个包,而不是放在classpath的根目录中。 最好给它起一个特定的名字。 例如, src / main / resources / spring-core / spring-core-context.xml比src / main / resource / application-context.xml更好。 想象一下,如果在相同的程序包中打包几个包含相同文件application-context.xml的组件,那该怎么办!
- 如果您已经在一个上下文文件中声明了Bean,则不要为组件扫描提供任何注释( @ Component, @ Service或@Repository )。
- 将特定于环境的bean(例如data-source , property-source)拆分到一个单独的文件中并重用。
- 不要在常规包装上进行组件扫描。 例如,与扫描org.springframework包相比,如果我们扫描几个子包(例如org.springframework.core , org.springframework.context , org.springframework.ui ,…), 则更易于管理。
结论
希望以上技巧对日常使用很有帮助。 如有任何疑问或任何其他想法,请发送反馈以提供帮助。
翻译自: https://www.javacodegeeks.com/2014/07/common-mistakes-when-using-spring-mvc.html