Spring 中 @Autowired 的替代方法,它不会在测试 class 设置之前初始化 bean
Alternative for @Autowired in Spring, which won't initialize the bean before test class setup
我有一个与数据库通信的 Spring Bean class,我正在为它编写一个测试用例。
我正在使用 DbUnit,它使用 JUnit Annotation @Before.
问题出在我试图测试的 @Autowired bean 上:它在其 @Postconstruct 方法中访问数据库,这发生在数据库设置之前,请注意 class 本身用 @Component.
注释
所以,我需要一种方法或注释来使用,以便我正在测试的 bean 仅在 @Before JUnit 方法运行后才被初始化。
这是 spring 的常见问题,并且有解决它的通用解决方案。
你需要建立所谓的 "three phase construction".
通常,您希望仅在设置所有 spring 上下文(使用数据库)后才初始化 bean。所以你需要监听什么时候 spring 会被初始化,然后执行你的逻辑。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PostProxy {
}
这个 class 应该在您的上下文中定义为一个 bean。
public class PostProxySpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger LOG = LoggerFactory.getLogger(PostProxySpringContextListener.class);
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
String originalClassName = getOriginalClassName(beanDefinitionName, event);
if (originalClassName != null) {
invokeAnnotatedMethods(context, beanDefinitionName, originalClassName);
}
}
}
private String getOriginalClassName(String name, ContextRefreshedEvent event) {
try {
ConfigurableListableBeanFactory factory =
(ConfigurableListableBeanFactory)event.getApplicationContext().getAutowireCapableBeanFactory();
BeanDefinition beanDefinition = factory.getBeanDefinition(name);
return beanDefinition.getBeanClassName();
} catch (NoSuchBeanDefinitionException e) {
LOG.debug("Can't get bean definition for : " + name);
return null;
}
}
private void invokeAnnotatedMethods(ApplicationContext context, String beanDefinitionName, String originalClassName) {
try {
Class<?> originalClass = Class.forName(originalClassName);
Method[] methods = originalClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(PostProxy.class)) {
LOG.info("Executing @PostProxy annotated initialization method: " + method.toString());
Object bean = context.getBean(beanDefinitionName);
Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
currentMethod.invoke(bean);
}
}
} catch (ClassNotFoundException e) {
LOG.trace("No class instance for bean " + beanDefinitionName + " with class name " + originalClassName);
} catch (NoSuchMethodException e) {
LOG.error("Error finding @PostProxy method for bean " + beanDefinitionName, e);
} catch (InvocationTargetException e) {
LOG.error("Error invoking @PostProxy method for bean " + beanDefinitionName, e);
} catch (IllegalAccessException e) {
LOG.error("Can't invoke annotated method in bean" + beanDefinitionName + " with class name " + originalClassName
+ ". Please check access modifiers on @PostProxy methods.", e);
}
}
}
我有一个与数据库通信的 Spring Bean class,我正在为它编写一个测试用例。
我正在使用 DbUnit,它使用 JUnit Annotation @Before.
问题出在我试图测试的 @Autowired bean 上:它在其 @Postconstruct 方法中访问数据库,这发生在数据库设置之前,请注意 class 本身用 @Component.
注释所以,我需要一种方法或注释来使用,以便我正在测试的 bean 仅在 @Before JUnit 方法运行后才被初始化。
这是 spring 的常见问题,并且有解决它的通用解决方案。 你需要建立所谓的 "three phase construction".
通常,您希望仅在设置所有 spring 上下文(使用数据库)后才初始化 bean。所以你需要监听什么时候 spring 会被初始化,然后执行你的逻辑。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PostProxy {
}
这个 class 应该在您的上下文中定义为一个 bean。
public class PostProxySpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger LOG = LoggerFactory.getLogger(PostProxySpringContextListener.class);
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
String originalClassName = getOriginalClassName(beanDefinitionName, event);
if (originalClassName != null) {
invokeAnnotatedMethods(context, beanDefinitionName, originalClassName);
}
}
}
private String getOriginalClassName(String name, ContextRefreshedEvent event) {
try {
ConfigurableListableBeanFactory factory =
(ConfigurableListableBeanFactory)event.getApplicationContext().getAutowireCapableBeanFactory();
BeanDefinition beanDefinition = factory.getBeanDefinition(name);
return beanDefinition.getBeanClassName();
} catch (NoSuchBeanDefinitionException e) {
LOG.debug("Can't get bean definition for : " + name);
return null;
}
}
private void invokeAnnotatedMethods(ApplicationContext context, String beanDefinitionName, String originalClassName) {
try {
Class<?> originalClass = Class.forName(originalClassName);
Method[] methods = originalClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(PostProxy.class)) {
LOG.info("Executing @PostProxy annotated initialization method: " + method.toString());
Object bean = context.getBean(beanDefinitionName);
Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
currentMethod.invoke(bean);
}
}
} catch (ClassNotFoundException e) {
LOG.trace("No class instance for bean " + beanDefinitionName + " with class name " + originalClassName);
} catch (NoSuchMethodException e) {
LOG.error("Error finding @PostProxy method for bean " + beanDefinitionName, e);
} catch (InvocationTargetException e) {
LOG.error("Error invoking @PostProxy method for bean " + beanDefinitionName, e);
} catch (IllegalAccessException e) {
LOG.error("Can't invoke annotated method in bean" + beanDefinitionName + " with class name " + originalClassName
+ ". Please check access modifiers on @PostProxy methods.", e);
}
}
}