Spring 常用上下文容器有哪些
- ApplicationContext
- ClassPathXmlApplicationContext
- ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
- AnnotationConfigApplicationContext
- AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
应该来说是很少使用这种方法用于生产开发, 常常在学习 Spring 做 demo 的时候会使用到. 更有可能出现在 Spring 项目的代码测试, 不过呢, 单元测试的框架 (比如 JUnit) 已经提供了简单的方式, 也就不建议直接实例化上下文. 因为实例化一个上下文还得要做维护, 再者现在常用的是基于 web 的开发, 也就是常用 Spring MVC. 如果没有基于 Web 应用的开发, 那么很可能就是一个小的程序, 类似于提供给第三方使用的 SDK 代码, 那么使用 Spring 感觉会太重, 最重要是自己要维护一个 ApplicationContext, 感觉不是那么方便
Web ApplicationContext
以下两个是针对 Spring MVC 的应用上下文. WebApplicationContext 实例会在应用启动之后由 Spring 实例化并维护, 而平常在学习的时候也往往不会自己去实例化 WebApplicationContext 对象, 因为将因为部署到 web 容器(比如 tomcat), 启动之后就可以直接测试了. 单元测试有专门的框架处理(比如 JUnit), 可以很简单的实现测试. web 项目的开发关键点在于让 web 容器初始化之后提醒 Spring ApplicationContext 初始化, 例如 tomcat 的 ServletContext 会维护一个 WebApplicationContext.
- XmlWebApplicationContext
- @Test
- public void handlerBeanNotFound() throws Exception {
- MockServletContext sc = new MockServletContext("");
- XmlWebApplicationContext root = new XmlWebApplicationContext();
- root.setServletContext(sc);
- root.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map1.xml"});
- root.refresh();
- XmlWebApplicationContext wac = new XmlWebApplicationContext();
- wac.setParent(root);
- wac.setServletContext(sc);
- wac.setNamespace("map2err");
- wac.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map2err.xml"});
- try {
- wac.refresh();
- fail("Should have thrown NoSuchBeanDefinitionException");
- }
- catch (FatalBeanException ex) {
- NoSuchBeanDefinitionException nestedEx = (NoSuchBeanDefinitionException) ex.getCause();
- assertEquals("mainControlle", nestedEx.getBeanName());
- }
- }
AnnotationConfigWebApplicationContext: 没找到合适的示例代码
ApplicationContext 入口
这种方式需要自己维护 ApplicationContext 实例, 也就是开发使用的时候 new ApplicationContext, 入口自己控制
WebApplicationContext 入口
web.xml 配置和 全注解 配置启动会有一些差别.
web.xml 配置
web.xml 中关于 Spring 的配置项, 也非常常见.
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
- <!-- 上下文参数, 在监听器中被使用, 实际就是 key-value,key=contextConfigLocation 写死 -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:applicationContext.xml
- </param-value>
- </context-param>
- <!-- 监听器配置, 初始化 WebApplicationContext -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- 前端控制器配置 -->
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:applicationContext-mvc.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- </web-app>
WebApplicationContext 的初始化调用链路: ContextLoaderListener.contextInitialized-->ContextLoader.initWebApplicationContext-->ContextLoader.createWebApplicationContext-->ContextLoader.determineContextClass-->ContextLoader.determineContextClass.
determineContextClass 源码如下:
- protected Class<?> determineContextClass(ServletContext servletContext) {
- // 自定义的 ApplicationContext
- String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
- if (contextClassName != null) {
- try {
- return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException(
- "Failed to load custom context class [" + contextClassName + "]", ex);
- }
- }
- else {
- // 缺省为 XmlWebApplicationContext
- contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
- try {
- return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException(
- "Failed to load default context class [" + contextClassName + "]", ex);
- }
- }
- }
默认 WebApplicationContext
根据上面的 web.xml 配置是没有指定 ApplicationContext 的实现的, 所以会执行: contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());.defaultStrategies 的实现如下:
- /**
- * Name of the class path resource (relative to the ContextLoader class)
- * that defines ContextLoader's default strategy names.
- */
- private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
- private static final Properties defaultStrategies;
- static {
- // 读取文件 ContextLoader.properties 中的配置
- try {
- ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
- defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load'ContextLoader.properties':" + ex.getMessage());
- }
- }
根据 ContextLoader.properties 中的配置完成, 里边的内容如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
所以呢, 通过 web.xml 配置 Spring MVC 默认的上下文是: XmlWebApplicationContext
指定 WebApplicationContext
如果在 web.xml 中配置 contextClass 属性, 例如下面的方式, 摘自 StackOverflow:How to register Spring @Configuration annotated class instead of applicationContext.xml file in web.xml? https://stackoverflow.com/questions/8075790/how-to-register-spring-configuration-annotated-class-instead-of-applicationcont
- <web-app>
- <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
- instead of the default XmlWebApplicationContext -->
- <context-param>
- <param-name>contextClass</param-name>
- <param-value>
- org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- </param-value>
- </context-param>
- <!-- Configuration locations must consist of one or more comma- or space-delimited
- fully-qualified @Configuration classes. Fully-qualified packages may also be
- specified for component-scanning -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>com.acme.AppConfig</param-value>
- </context-param>
- <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Declare a Spring MVC DispatcherServlet as usual -->
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
- instead of the default XmlWebApplicationContext -->
- <init-param>
- <param-name>contextClass</param-name>
- <param-value>
- org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- </param-value>
- </init-param>
- <!-- Again, config locations must consist of one or more comma- or space-delimited
- and fully-qualified @Configuration classes -->
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>com.acme.web.MvcConfig</param-value>
- </init-param>
- </servlet>
- <!-- map all requests for /app/* to the dispatcher servlet -->
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>/app/*</url-pattern>
- </servlet-mapping>
- </web-app>
感觉很神奇, 但是也很麻烦的样子. 我是不建议混合 web.xml 配置启动和全注解启动, 乱且不好看懂.
全注解配置
全注解方式一般是实现 WebApplicationInitializer 或者通过继承 AbstractAnnotationConfigDispatcherServletInitializer.AbstractAnnotationConfigDispatcherServletInitializer 是 WebApplicationInitializer 的实现类. 想知道全注解配置下 tomcat 如何 Spring IOC 怎样被加载, 可以阅读篇文章 Spring 揭秘 -- 寻找遗失的 web.xml https://mp.weixin.qq.com/s?__biz=MzI0NzEyODIyOA==&mid=2247483924&idx=1&sn=3f97325c3e271df9b39373d2f6b5b42b&chksm=e9b58bdfdec202c9bba143df9ee8b4de2dc270db448036bef31ae6f88ac9d9137ef19fd3a14a&mpshare=1&scene=1&srcid=0504q30oHY5NhdWPiXZu2M0k#rd
全注解方式配置常用类似如下的代码:
- import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
- /**
- * @author imssbora
- */
- public class MyWebAppInitializer extends
- AbstractAnnotationConfigDispatcherServletInitializer{
- @Override
- protected Class<?>[] getRootConfigClasses() {
- return new Class[]{RootConfig.class};
- }
- @Override
- protected Class<?>[] getServletConfigClasses() {
- return new Class[]{WebConfig.class};
- }
- @Override
- protected String[] getServletMappings() {
- return new String[]{"/"};
- }
- }
启动路径: SpringServletContainerInitializer.onStartup-->AbstractContextLoaderInitializer.onStartup-->AbstractContextLoaderInitializer.registerContextLoaderListener-->AbstractAnnotationConfigDispatcherServletInitializer.createRootApplicationContext.
createRootApplicationContext 源码如下:
- protected WebApplicationContext createRootApplicationContext() {
- Class<?>[] configClasses = this.getRootConfigClasses();
- if (!ObjectUtils.isEmpty(configClasses)) {
- AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
- rootAppContext.register(configClasses);
- return rootAppContext;
- } else {
- return null;
- }
- }
可以看出实例化了 AnnotationConfigWebApplicationContext 对象.
参考
关于 web.xml 配置启动, Spring 的加载流程网络上资料很多, 所以有可能会有很多重复的, 选择一遍排版不错, 写得相对完整的, 编写时间比较新的: Spring MVC 启动过程源码分析 https://juejin.im/post/5b207dc86fb9a01e49294f42 .
排版精美, 写得很棒的文章: Spring 揭秘 -- 寻找遗失的 web.xml https://mp.weixin.qq.com/s?__biz=MzI0NzEyODIyOA==&mid=2247483924&idx=1&sn=3f97325c3e271df9b39373d2f6b5b42b&chksm=e9b58bdfdec202c9bba143df9ee8b4de2dc270db448036bef31ae6f88ac9d9137ef19fd3a14a&mpshare=1&scene=1&srcid=0504q30oHY5NhdWPiXZu2M0k#rd
从 StackOverflow 上找到的资料: How to register Spring @Configuration annotated class instead of applicationContext.xml file in web.xml? https://stackoverflow.com/questions/8075790/how-to-register-spring-configuration-annotated-class-instead-of-applicationcont
时间: 2018.9.3 16:20
来源: https://www.cnblogs.com/xiaoheike/p/9574240.html