在我的代码中有一个Bean Admin 。 Admin 的其中一项操作是创建任务并启动。
任务执行的操作相当复杂。因此它被分成几个不同的 Step 类。
这就是我的代码看起来的样子
public interface Admin{
void start();
//other methods
}
public class AdminImpl implements Admin{
private BeanA bean1;
private BeanB bean2;
//other fields
public Admin(BeanA bean1,
BeanB bean2,
BeanC bean3
//Lot more parameters
BeanO bean15
){
this.bean1 = bean1;
this.bean2 = bean2;
//and so on
}
public void start(){
return new Task(bean1, bean2, bean3,...).start()
}
//other methods
}
public class Task{
private BeanA bean1;
private BeanB bean2;
//other fields
public Task(BeanA bean1,
BeanB bean2,
BeanC bean3
//Lot more parameters
){
//bind parameters to fields
}
public void start(){
new Step1(bean1, bean2,.. other parameters).do();
new Step2(bean3, bean7,.. other parameters).do();
//more steps
}
}
@Configuration
public class MyConfiguration{
@Bean
public Admin admin(BeanA bean1, BeanB bean2....){
return new AdminImpl(bean1, bean2...);
}
}
正如您所看到的,每个 Step 类都有2个或3个Bean依赖项。 Step 类不是Spring Beans,因此它们是从 Task 传递的依赖项。 Task 也不是Spring Bean,因此它从 Admin 获取依赖关系。 这导致 Admin 拥有太多依赖关系(~15)。
我试过了:https://dzone.com/articles/autowiring-spring-beans-into-classes-not-managed-by-spring。
基本上,您创建了一个名为BeanUtil的服务Bean,它是ApplicationContextAware。静态方法getBean使用ApplicationContext获取bean。
Step class现在看起来像这样:
class Step{
public Step(){
BeanA bean1 = BeanUtil.getBean(BeanA.class);
BeanB bean2 = BeanUtil.getBean(BeanB.class);
}
public void do(){
//do stuff
}
}
这解决了最初的问题,但后来我在测试时遇到了困难。 这就是测试类现在的样子。
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class Step1Test extends AbstractTestNGSpringContextTests {
@Test
public void test(){
Step1 step = new Step().do();
}
@Configuration
static class MockConfiguration {
@Bean
public BeanA beanA() {
BeanA mockBeanA=Mockito.mock(BeanA.class);
// set behavior of mock
return mockBeanA;
}
@Bean
public BeanUtil beanUtil(){
return new BeanUtil();
}
}
}
您无法在不创建不同配置类的情况下更改不同测试用例的模拟行为。这就像通过创建另一个问题来解决一个问题。
这是Spring开发人员面临的一个常见问题,其中抽象级别较高的类最终会产生过多的依赖关系吗?或者我的设计有问题吗?处理或避免这种情况的正确方法是什么?
其他似乎相似但不是
的问题编辑: 我从user7得到的一个建议是我将BeanX分组并将抽象传递给Admin。 bean没有任何我可以利用的逻辑分组。此外,许多 Steps 需要对bean进行完全访问(访问界面中的所有方法)。这会导致抽象变得臃肿。
答案 0 :(得分:1)
你可以将Spring bean作为原型bean(因为它们是有状态的,你每次都想要一个不同的实例),并在你的Task bean中注入Provider<Step>
(如果我理解正确,可以是单身豆)。
例如:
public class Step1 {
private Bean1 bean1;
private Bean2 bean2;
private final String someValue;
private final String someOtherValue;
public Step(String someValue, String someOtherValue) {
this.someValue = someValue;
this.someOtherValue = someOtherValue;
}
@Autowired
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
@Autowired
public void setBean2(Bean2 bean2) {
this.bean2 = bean2;
}
do() {
// ...
}
}
在配置类中,然后将各种步骤定义为bean,方法需要所需的参数:
@Bean
@Scope("prototype")
public Step1 step1(String someValue, String someOtherValue) {
return new Step(someValue, someOtherValue);
}
在Task bean中,您注入ObjectProvider<Step1>
:
private ObjectProvider<Step1> stepProvider;
public Service(ObjectProvider<Step1> step1Provider) {
this.stepProvider = stepProvider;
}
public void start() {
Step1 step1 = step1Provider.getObject("a", "b");
step1.do();
}