为什么在 Spring Boot 中,超类上的@Bean泛型创建方法的调用晚于子类上的泛型创建方法?



我有一个 spring boot 基础抽象配置类,它创建了一个 bean。如果我随后从它继承,则 bean 的创建时间将晚于我的控制器(需要自动连接它,因此失败)。注意:它确实是在控制器之后创建的。所以它不能自动连线,但必须通过appContext.getBean( BeanType.class )找到

如果我改用子类中的 bean 方法,那么它会在控制器之前创建,并且可以自动连线。

如何解决此问题并使超类 Bean 定义与子类同时加载?

@SpringBootApplication
public class ChildConfig extends ParentConfig<PCTestBean>
{
public ChildConfig()
{
super();
}
@Override
public PCTestBean getT()
{
return new PCTestBean();
}
}
public abstract class ParentConfig<T>
{
public ParentConfig() {}
@Bean
public T createTestBean()
{
return getT();
}
public abstract T getT();
}
public class PCTestBean
{
}
@RestController
@RequestMapping( "/client" )
public class MyController
{
@Autowired
private PCTestBean pcTestBean;
@RequestMapping( "/get" )
@ResponseBody
public String getClient(HttpServletRequest request) throws Exception
{
return pcTestBean.toString();
}
}
@RunWith( SpringJUnit4ClassRunner.class )
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
@ContextConfiguration(
classes = {
ChildConfig.class
}
)
public class TestConfigs
{
@LocalServerPort
private String port;
private MockMvc mockMvc;
@Autowired
private WebApplicationContext context;
@Before
public void setUp() throws Exception
{
mockMvc = MockMvcBuilders
.webAppContextSetup( context )
.build();
}
@Test
public void testValidCall() throws Exception
{
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get( new URI( "http://localhost:" + port + "/client/get" ) )
)
.andExpect( MockMvcResultMatchers.status().isOk() ).andReturn();
System.out.println( result.getResponse().getContentAsString() );
}
}

当 Spring 扫描你的配置类时,ChildConfig,它会发现这个继承的方法

@Bean
public T createTestBean() {
return getT();
}

并为其注册 Bean 定义。该 Bean 定义包含有关 Bean 类型的元数据。该类型是从方法的返回类型推断出来的。在这种情况下,它被解析为Object,因为类型变量T在其声明中没有边界,并且因为 Spring 不会尝试根据ChildConfigextends ParentConfig<PCTestBean>子句中提供的类型参数解析它。

当春天然后尝试处理

@Autowired
private PCTestBean pcTestBean;

注入目标,它寻找一个PCTestBeanBean,它认为它没有,因为缺少元数据。如果Bean 没有通过其他强制顺序进行初始化,那么 Spring 就没有其他信息可以继续,并且认为 Bean 不存在。

当您将代码更改为

而是重写子类中的 Bean 方法

该方法的返回类型是PCTestBean然后 Spring 可以匹配@Autowired注入需求,找到(并初始化)bean,然后注入它。

当您使用ApplicationContext#getBean(Class)时,PCTestBean已初始化。因此,Spring 可以依赖于实例的实际类型。 它会或多或少地遍历所有 bean 并检查是否beanClass.isInstance(eachBean),返回匹配的 bean(如果有多个匹配,则失败)。

Pankaj在他们的回答中建议使用@DependsOn(在你编辑问题之前,他们建议这样做是错误的)。这可以帮助建立我前面提到的秩序。


我不知道您的配置类有多广泛,以至于您认为需要泛型来抽象某些行为,但我建议放弃泛型行为并在每个类中显式。

尝试依赖注释,它保证子 Bean 应该在父 Bean 之后创建

@Configuration
public class ChildConfig extends ParentConfig
{
public ChildConfig()
{
super();
}

@DependsOn("parentConfig")
@Override
public TestBean createTestBean()
{
return super.createTestBean();
}*/
}
public abstract class ParentConfig
{
public ParentConfig() {}
@Bean (name ="parentConfig")
public TestBean createTestBean()
{
return new TestBean();
}
}

最新更新