Spring中Junit测试-@WebAppConfiguration与WebApplicationContext

先看看Spring官方对@WebAppConfiguration的注释说明

//@WebAppConfiguration是一个类级注释,始于Spring3.2,用于声明集成测试加载的ApplicationContext应该是WebApplicationContext。
//Spring会以value属性(默认为"src/main/webapp")指定的目录路径来为测试加载WebApplicationContext。要覆盖默认值,请通过value属性指定一个显式资源路径。
//请注意, @WebAppConfiguration必须在单个测试类中或在测试类层次结构中与@ContextConfiguration结合使用。
//从Spring Framework 4.0开始,此注释可用作创建自定义组成的注释的元注释。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebAppConfiguration {
	//指向Web应用程序根目录的资源路径。
	//不包括Spring资源前缀的路径(例如,classpath:, file:等等)将被解释为文件系统资源,并且该路径不应以斜杠结尾。
	//也可以指向classpath中的资源路径,而不是文件系统。eg.@WebAppConfiguration("classpath:testResources")
	//默认以"src/main/webapp"作为文件系统资源。 请注意,这是Web应用程序根目录的标准目录,该目录遵循WAR的标准Maven项目布局。
	String value() default "src/main/webapp";
}

看完Spring官方的注释说明,大概可以明白:若你在一个测试类上加了@WebAppConfiguration注解,则表示告诉Spring该集成测试加载的ApplicationContext应该是WebApplicationContext,那么看看WebApplicationContextApplicationContext的基础上扩展了什么内容:
在这里插入图片描述

//该接口为Web应用程序提供配置。在应用程序运行时,它是只读的,但是如果实现支持,则可以重新加载。
//此接口给通用的ApplicationContext接口添加了getServletContext()方法,并定义了一个众所周知的应用程序属性名称,在引导过程中必须将根上下文绑定到该名称。
//像通用应用程序上下文一样,Web应用程序上下文是分层的。 每个应用程序只有一个root context,而应用程序中的每个servlet(包括MVC框架中的调度程序servlet)都有自己的子上下文。
//除了标准的应用程序上下文生命周期功能外,WebApplicationContext的实现类还需要检测ServletContextAware Bean并相应地调用setServletContext方法。
public interface WebApplicationContext extends ApplicationContext {
	String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
	String SCOPE_REQUEST = "request";
	//......
	String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
	//......
	//返回此应用程序的标准Servlet API ServletContext。除PortletContext外,它还可用于Portlet应用程序。
	ServletContext getServletContext();
}

可以看到WebApplicationContext也是一个接口,主要只是定义了getServletContext()方法,项目成功启动时Spring会将RootWebApplicationContext绑定到的该Contextparent属性,即为它的父容器。

另外,WebApplicationContext的实现类还需要检测 ServletContextAware Bean并相应地调用setServletContext()方法。且分析ServletContextAwareProcessor及相关联的一系列类源码得知: Spring会往Web应用程序上下文中注册ServletRequest、ServletResponse、HttpSession等等bean ,我们在代码中通过@Autowired注入就可以很方便地获取到它们了。

/**
 * 是BeanPostProcessor接口的实现类,将ServletContext传递给实现了ServletContextAware接口的Bean。
 * web应用程序上下文(Web application contexts)将自动将这个类注册到其底层bean工厂。 应用程序不直接使用它。
 * 
 * @see org.springframework.web.context.ServletContextAware
 * @see org.springframework.web.context.support.XmlWebApplicationContext#postProcessBeanFactory
 */
public class ServletContextAwareProcessor implements BeanPostProcessor {
	private ServletContext servletContext;
	private ServletConfig servletConfig;
	
}
public interface ServletContextAware extends Aware {
	/**
	 * Set the {@link ServletContext} that this object runs in.
	 * <p>Invoked after population of normal bean properties but before an init
	 * callback like InitializingBean's {@code afterPropertiesSet} or a
	 * custom init-method. Invoked after ApplicationContextAware's
	 * {@code setApplicationContext}.
	 * @param servletContext ServletContext object to be used by this object
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext
	 */
	void setServletContext(ServletContext servletContext);
}
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
		implements ConfigurableWebApplicationContext, ThemeSource {
	/**
	 * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
	 */
	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

		//注册web应用的所有域
		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
	}
}
public abstract class WebApplicationContextUtils {
	/**
	 * Register web-specific scopes ("request", "session", "globalSession", "application")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 * @param beanFactory the BeanFactory to configure
	 * @param sc the ServletContext that we're running within
	 */
	public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
		beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
		beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
		beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
		if (sc != null) {
			ServletContextScope appScope = new ServletContextScope(sc);
			beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
			// Register as ServletContext attribute, for ContextCleanupListener to detect it.
			sc.setAttribute(ServletContextScope.class.getName(), appScope);
		}

		//注册ServletRequest bean!
		beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
		beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
		beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
		beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
		if (jsfPresent) {
			FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
		}
	}
}
public abstract class WebApplicationContextUtils {
	/**
	 * Factory that exposes the current request object on demand.
	 */
	@SuppressWarnings("serial")
	private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}
}

这里有个关联知识点:向Spring中注入HttpServletRequest

值得注意的是,若测试类上只有@WebAppConfiguraton而没有@ContextConfiguration。则启动报错:

Caused by: java.lang.IllegalArgumentException: Cannot load an ApplicationContext with a NULL 'contextLoader'. Consider annotating your test class with @ContextConfiguration or @ContextHierarchy.
	at org.springframework.util.Assert.notNull(Assert.java:112)
	at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:57)
	at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
	... 26 more
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring JUnit 注解测试是一种基于 Spring 框架的单元测试方法,通过使用注解来实现依赖注入、AOP、事务控制等功能,从而方便地进行单元测试。 下面是 Spring JUnit 注解测试常用的注解及其作用: 1. @RunWith(SpringJUnit4ClassRunner.class):指定运行测试的类为 SpringJUnit4ClassRunner,该类会创建 Spring 的上下文环境,并自动加载指定的配置文件或配置类。 2. @ContextConfiguration:指定 Spring 的配置文件或配置类,用于创建 Spring 的上下文环境。 3. @Autowired:自动装配 Spring 容器的 Bean,可以省略 setter 方法。 4. @Transactional:在测试方法添加该注解可以实现事务控制,测试方法执行完成后自动回滚事务。 5. @Test:用于标记测试方法。 下面是一个简单的 Spring JUnit 注解测试的例子: ```java @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = AppConfig.class) @Transactional public class UserServiceTest { @Autowired private UserService userService; @Test public void testAddUser() { User user = new User(); user.setUsername("test"); user.setPassword("123456"); userService.addUser(user); User addedUser = userService.getUserByName("test"); Assert.assertEquals("test", addedUser.getUsername()); } } ``` 在该例子,@RunWith 指定了运行测试的类为 SpringJUnit4ClassRunner,@ContextConfiguration 指定了需要加载的配置类 AppConfig,@Transactional 用于进行事务控制,@Autowired 实现了依赖注入,@Test 标记了测试方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值