当前位置:首页>财经>正文

spring中为什么报这个错

2023-06-14 21:05:39 互联网 未知 财经

 spring中为什么报这个错

spring中为什么报这个错

问题
在Java中,一般使用JUnit作为单元测试框架,测试的对象一般是Service和DAO,也可能是RemoteService和Controller。所有这些测试对象基本都是Spring托管的,不会直接new出来。而每个TestCase类却是由JUnit创建的。如何在每个TestCase实例中注入这些依赖呢?
预期效果
我们希望能够达到这样的效果:
package me.arganzheng.study import static org.junit.Assert.* import org.junit.Test import org.springframework.beans.factory.annotation.Autowired /** * @author arganzheng */ public class FooServiceTest{ @Autowired private FooService fooService @Test public void testSaveFoo() { Foo foo = new Foo() // ... long id = fooService.saveFoo(foo) assertTrue(id > 0) } }

解决思路
其实在我前面的文章:Quartz与Spring的整合-Quartz中的job如何自动注入spring容器托管的对象 ,已经详细的讨论过这个问题了。Quartz是一个框架,Junit同样是个框架,Spring对于接入外部框架,采用了非常一致的做法。对于依赖注入,不外乎就是这个步骤:
首先,找到外部框架创建实例的地方(类或者接口),比如Quartz的jobFactory,默认为org.quartz.simpl.SimpleJobFactory,也可以配置为org.quartz.simpl.PropertySettingJobFactory。这两个类都是实现了org.quartz.spi.JobFactory接口。对于JUnit4.5 ,则是org.junit.runners.BlockJUnit4ClassRunner类中的createTest方法。
/** * Returns a new fixture for running a test. Default implementation executes * the test classs no-argument constructor (validation should have ensured * one exists). */ protected Object createTest() throws Exception { return getTestClass().getOnlyConstructor().newInstance() }

继承或者组合这些框架类,如果需要使用他们封装的一些方法的话。如果这些类是有实现接口的,那么也可以直接实现接口,与他们并行。然后对创建出来的对象进行依赖注入。
比如在Quartz中,Spring采用的是直接实现org.quartz.spi.JobFactory接口的方式:
public class SpringBeanJobFactory extends AdaptableJobFactory implements SchedulerContextAware { ... } public class AdaptableJobFactory implements JobFactory { ... }

但是Spring提供的org.springframework.scheduling.quartz.SpringBeanJobFactory并没有自动依赖注入,它其实也是简单的根据job类名直接创建类:
/** * Create an instance of the specified job class. *

Can be overridden to post-process the job instance. * @param bundle the TriggerFiredBundle from which the JobDetail * and other info relating to the trigger firing can be obtained * @return the job instance * @throws Exception if job instantiation failed */ protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { return bundle.getJobDetail().getJobClass().newInstance() }

不过正如它注释所说的,Can be overridden to post-process the job instance,我们的做法也正是继承了org.springframework.scheduling.quartz.SpringBeanJobFactory,然后覆盖它的这个方法:
public class OurSpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory{ @Autowire private AutowireCapableBeanFactory beanFactory /** * 这里我们覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。 */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object jobInstance = super.createJobInstance(bundle) beanFactory.autowireBean(jobInstance) return jobInstance } }

由于OurSpringBeanJobFactory是配置在Spring容器中,默认就具备拿到ApplicationContext的能力。当然就可以做ApplicationContext能够做的任何事情。
题外话
这里体现了框架设计一个很重要的原则:开闭原则——针对修改关闭,针对扩展开放。 除非是bug,否者框架的源码不会直接拿来修改,但是对于功能性的个性化需求,框架应该允许用户进行扩展。 这也是为什么所有的框架基本都是面向接口和多态实现的,并且支持应用通过配置项注册自定义实现类, 比如Quartz的`org.quartz.scheduler.jobFactory.class`和`org.quartz.scheduler.instanceIdGenerator.class`配置项。

解决方案
回到JUnit,其实也是如此。
Junit4.5 是通过org.junit.runners.BlockJUnit4ClassRunner中的createTest方法来创建单元测试类对象的。
/** * Returns a new fixture for running a test. Default implementation executes * the test classs no-argument constructor (validation should have ensured * one exists). */ protected Object createTest() throws Exception { return getTestClass().getOnlyConstructor().newInstance() }

那么根据前面的讨论,我们只要extendsorg.junit.runners.BlockJUnit4ClassRunner类,覆盖它的createTest方法就可以了。如果我们的这个类能够方便的拿到ApplicationContext(这个其实很简单,比如使用ClassPathXmlApplicationContext),那么就可以很方便的实现依赖注入功能了。JUnit没有专门定义创建UT实例的接口,但是它提供了@RunWith的注解,可以让我们指定我们自定义的ClassRunner。于是,解决方案就出来了。

随便看看