Spring框架学习记录 6 集成 Junit 和 Web环境

集成 Junit:

示例:

(1)导入相关依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.5.RELEASE</version>
    <scope>test</scope>
</dependency>

需要注意的是,spring-test 的版本要与之前引入的 spring-context 版本一致

(2)创建测试类 SpringJunitTest

import Service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringJunitTest {

	@Autowired
	private UserService userService;

	@Test
	public void test1() {
		userService.say();
	}

}

@RunWith(SpringJUnit4ClassRunner.class) 用于让测试在 Spring 容器环境下执行

@ContextConfiguration 用于指定 xml 配置文件或配置类,如下

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@ContextConfiguration(classes = SpringConfiguration.class)

分析: 

Spring 集成 Junit 后,可以通过如上方式进行单元测试,程序自动创建 Spring 容器,我们只需要通过 @Autowired 注解,注入需要测试的 bean 即可

集成 Web 环境:

示例:

(1)创建 Web 层,编写 UserServlet 类

Spring框架学习记录 6 集成 Junit 和 Web环境

 

package Web;

import Config.SpringConfiguration;
import Service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
		UserService userService = app.getBean(UserService.class);
		userService.say();
	}

}

重写 doGet 方法,内容为创建 Spring 容器并取得 Service 层对象 userService,调用其 say 方法

(2)在 web.xml 中配置 servlet 映射

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>Web.UserServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>

</web-app>

将访问路径与对应的 servlet 进行映射

(3)在浏览器中访问 localhost:8080/userService 进行测试

启动 Tomcat ,浏览器中访问 localhost:8080/userService

Spring框架学习记录 6 集成 Junit 和 Web环境

控制台成功输出结果

Spring框架学习记录 6 集成 Junit 和 Web环境

弊端分析:

当 Web 层的接口较多时,每个 servlet 的处理方法中都需要创建 Spring 容器,导致配置文件/类被加载多次,Spring 容器也被创建多次,影响性能

解决方案:

在 Web 项目中,使用 ServletContextListener 监听 Web 应用的启动,在 Web 应用启动时创建 ApplicationContext 对象,并将其存入 servletContext 域中,便于在任意位置取用 ApplicationContext 对象

(1)创建监听器 ContextLoaderListener

Spring框架学习记录 6 集成 Junit 和 Web环境

package Listener;

import Config.SpringConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextLoaderListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
		sce.getServletContext().setAttribute("app", app);
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {

	}
}

监听器类实现 ServletContextListener 接口,并重写 contextInitialized 方法,该方法在 Web 应用启动时会被调用,方法中创建 Spring 容器,并存入 servletContext 域中

(2)在 web.xml 中配置监听器

<listener>
    <listener-class>Listener.ContextLoaderListener</listener-class>
</listener>

(3)在 UserServlet 类的 doGet 方法中通过 servletContext 域获取 ApplicationContext 对象

package Web;

import Service.UserService;
import org.springframework.context.ApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		ServletContext servletContext = this.getServletContext();
		ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
		UserService userService = app.getBean(UserService.class);
		userService.say();
	}

}

对于 ServletContext 对象的获取,有如下两种方式

ServletContext servletContext = req.getServletContext();
ServletContext servletContext = this.getServletContext();

配置文件解耦:

对于监听器中的 ApplicationContext 的创建,需指定配置文件,可以在 web.xml 中配置全局参数,并将配置文件指定为该全局参数,实现配置文件解耦

(1)在 web.xml 中配置全局参数

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>applicationContext.xml</param-value>
</context-param>

(2)在监听器中取得参数,创建 ApplicationContext 对象

public void contextInitialized(ServletContextEvent sce) {
	ServletContext servletContext = sce.getServletContext();
	String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
	ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
	sce.getServletContext().setAttribute("app", app);
}

通过 ServletContext 对象的 getInitParameter 方法可以获取全局参数

取用 ApplicationContext 优化

(1)编写工具类 WebApplicationContextUtils

Spring框架学习记录 6 集成 Junit 和 Web环境

package Util;

import org.springframework.context.ApplicationContext;

import javax.servlet.ServletContext;

public class WebApplicationContextUtils {
	public static ApplicationContext getWebApplicationContext(ServletContext servletContext) {
		return (ApplicationContext) servletContext.getAttribute("app");
	}
}

其中对于静态方法 getWebApplicationContext ,只需传入 ServletContext ,即可返回 ApplicationContext 对象

(2)修改 doGet 方法中对 ApplicationContext 的取用

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	ServletContext servletContext = this.getServletContext();
	ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
	UserService userService = app.getBean(UserService.class);
	userService.say();
}

直接调用 WebApplicationContextUtils 的静态方法获取 ApplicationContext 对象即可

使用 Spring 工具进行 ApplicationContext 对象的获取:

对于上文中创建的 ContextLoaderListener 、WebApplicationContextUtils ,Spring 提供了完全相同的功能,并封装在了 spring-web 包中

(1)导入相关依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>

 注意 Spring-web 的版本需要与先前导入的别的 Spring 包的版本相同

(2)在 web.xml 中配置监听器和配置文件全局参数

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

 这里的配置文件路径需要加 classpath

(3)使用 WebApplicationContextUtils 获取 ApplicationContext 对象

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	ServletContext servletContext = this.getServletContext();
	ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
	UserService userService = app.getBean(UserService.class);
	userService.say();
}
上一篇:[Swift]LeetCode1079. 活字印刷 | Letter Tile Possibilities


下一篇:SpringIOC 面试题(下)