一个简单的基于 spring-Junit 测试基础搭建——spring必须了解的几个接口系列一
分为以下几部:
1.写上下文的工具类,需继承 ApplicationContextAware 并实现 setApplicationContext方法,定义静态变量 private static ApplicationContext applicationContext; 给静态变量ApplicationContext赋值,再定义一个静态方法来获取上线文。
package com.sun.applicationContextAware.intf;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
//@Component 去掉注解,直接配置在spring上下文文件中
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 这个接口的就是帮助我们的程序获取spring的 上下文的,如何获取呢 我们需要定义一个 ApplicationContext 的上下文
* 思考细节:setApplicationContext 是什么时候被调用的???
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* 写一个方法 来获取 applicationContext
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
2.编写spring上下文xml文件,越简单越好,这里我们只配置了一个
通过注解@Component方式,applicationContext有时会为null的问题解决方法?
这时我们就可以不用注解的方式直接在Spring的配置文件applicationContext.xml中单独配置这个bean并且把他放在包扫描之前.SpringContextUtil类,由于加载顺序的问题如果只依靠注解来加载,有可能setApplicationContext方法 还没执行,导致我们获取的applicationContext获取的属性对象为空,所以配置在xml文件里面最好
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="springContextUtil" class="com.sun.applicationContextAware.intf.SpringContextUtil"></bean>
</beans>
3.编写测试基类,用来加载配置,注意需要加@RunWith和@ContextConfiguration两个注解 ,一个是使用spring集成的JUnit 另一个是 指定加载的上下问文件
package com.sun.base;
import com.sun.applicationContextAware.intf.SpringContextUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
public class SpringBaseTest {
// 可以写一些基类需要处理的事情
}
4.最终写测试文件,继承基类,打印只要获取到上下文,下面想获取任何bean都可以了
package com.sun.test;
import com.sun.applicationContextAware.intf.SpringContextUtil;
import com.sun.base.SpringBaseTest;
import org.junit.Test;
public class ApplicationContextAwareTest extends SpringBaseTest {
@Test
public void testSpringContextUtil(){
ApplicationContext ctx = SpringContextUtil.getApplicationContext();
System.out.println("==================" + ctx);
//使用这种方式获取bean,ctx.getBean("beanId");
}
}
那么ApplicationContextAware的setApplicationContext方法是什么时候执行的?
从图中可以看出,最后会调用initializeBeanf方法又会调用applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization方法,也就是调用bean后处理器,bean后处理器其中就有一个叫ApplicationContextAwareProcessor,其中的处理方法如下代码,就有调用到setApplicationContext方法,部分源码如下:
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
你是不是会问ApplicationContextAwareProcessor什么时候放到容器中的,看org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory方法中就放进来了
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
······
}
prepareBeanFactory又在org.springframework.context.support.AbstractApplicationContext#refresh方法中被调用,看是不是又回到容器初始化的入口了,整个流程是不是打通了