Web项目中重复创建Spring容器的解决办法

Web 项目使用 Spring 的问题

[内容时间:2022.01]

在 Web 项目中使用 Spring 框架,首先要解决在 web 层(这里指 Servlet )中获取到 Spring 容器的问题。只要在 web 层获取到了 Spring 容器,就可以在容器中获取到 Service 对象

代码示例

次数省略了 index 页面、pom文件、spring 配置文件、mybatis 配置文件、mapper 文件

RegisterServlet (重点代码)

// RegisterServlet 中 doGet() 或者 doPost() 方法 
protected void doGet(HttpServletRequest request,HttpServletResponse response){
    
    //省略获取参数 ************
    //************************
    
    //创建spring容器
    String configLocation = "application.xml";
    //重点↓↓↓
    ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocation);
    //重点↓↓↓
    System.out.println("容器的信息:" + ctx);
    
    //省略从容器获取对象 *******
    //省略执行业务 *************
    
    //调用视图层,跳转
    request.getRequestDispatcher("/result.jsp").forward(request,response);
}
复制代码

运行结果分析

当表单提交,跳转到 success.jsp 后,多刷新几次页面,查看后台控制台输出,发现每次刷新页面,就 new 出一个新的 Spring 容器。

也就是说,每提交一次请求,每跑一次 doGet 或 doPost 方法,就会创建一个新的 Spring 容器,对于一个应用来说,只需要一个 Spring 容器即可。

所以,将 Spring 容器的创建语句放在 Servlet 的 doGet() 或 doPost() 方法中是有问题的

rongqi.png

此时可以考虑,将 Spring 容器的创建爱你放在 Servlet 初始化的时候进行。即:执行 init() 方法的时候执行。并且,Servlet 还是单例多线程的,一个业务只有一个 Servlet 实例,所有执行这个业务的用户执行的都是这一个 Servlet 实例。志这样子 Spring 容器就具有唯一性了。

但是,Servlet 是一个业务对应一个 Servlet 实例,LoginServlet 只有一个,但还会有 StudentServlet、TeacherServlet 等等。每个业务都会对应一个 Servlet,都会执行自己的 init() 方法。也就会创建一个 Spring 容器。

扫描二维码关注公众号,回复: 14137179 查看本文章

这样一来,Spring 容器又不唯一了

使用 Spring 的监听器 ContextLoaderListener(掌握)

对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个 ServletContext 对象。该对象是 Web 应用装载时初始化的。若将 Spring 容器的创建时机,放在 ServletContext 初始化时,就能保证 Spring 容器的创建只会执行一次,也就保证了 Spring 容器在整个应用中的唯一性

当 Spring 容器创建好之后,在整个应用生命周期中,Spring 容器应该是可以随时被访问的,具有全局性。而放入 ServletContext 对象的属性,就具有应用的全局性。所以,将创建好的 Spring 容器以属性的形式放入 ServletContext 的空间中,就保证了 Spring 容器的全局性

上述的这些工作,已经被封装在了如下 Spring 的 jar 包相关的 API 中:spring-web-5.2.5.RELEASE

步骤

添加 Maven 依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
复制代码

注册监听器

如果要在 ServletContext 初始化时创建 Spring 容器,就需要使用监听器接口 ServletContextListener 对 ServletContext 进行监听,在 web.xml 中注册该监听器

<!-- 此监听器的作用是初始化spring容器:在ServletContext创建的时候,初始化spring容器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
复制代码

为监听器指定 Spring 配置文件路径

ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。默认的 Spring 配置文件与名称为:WEB-INF/applicationContext.xml

但是一般不将配置文件放在此目录文件夹下。一般放置在 classpath 下,也就是 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置以及名称进行指定

<!-- 为监听器指定Spring配置文件的位置 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
复制代码

获取 Spring 容器对象

在 Servlet 中获取容器对象的常用方式有两种

1、直接从 ServletContext 中获取

从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 中存放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 这个常量

所以,可以直接通过 ServletContext 的 getAttribute() 方法,按照指定的 key 将容器对象获取到

String attribute = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
WebApplicationContext ac = (WebApplicationContext) this.getServletContext().getAttribute(attribute);
复制代码

2、通过 WebApplicationContextUtils 获取

工具类 WebApplicationContextUtils 中有一个方法专门用于 ServletContext 中获取 Spring 容器对象:getRequiredWebApplicationContext(ServletContext sc)

//框架提供的方法,获取容器对象
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext);
复制代码

查看源码,看调用关系,就可以看到它是从 ServletContext 中读取的属性值,即 Spring 容器

06_spring_yuanma.png

以上两种方式,无论使用哪种获取容器对象,刷新 success 页面后,可看到代码中使用的 Spring 容器均为同一个对象。 06_jiejuele.png

猜你喜欢

转载自juejin.im/post/7096782920790048782