为啥在 Spring Boot 中超类上的 @Bean 泛型创建方法比子类上的方法调用晚?
Posted
技术标签:
【中文标题】为啥在 Spring Boot 中超类上的 @Bean 泛型创建方法比子类上的方法调用晚?【英文标题】:Why are @Bean Generics creation methods on a superclass called later than ones on the child-class in Spring Boot?为什么在 Spring Boot 中超类上的 @Bean 泛型创建方法比子类上的方法调用晚? 【发布时间】:2018-07-10 00:03:04 【问题描述】:我有一个创建 bean 的 Spring Boot 基础抽象配置类。如果我从它继承,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() );
【问题讨论】:
请提供一个minimal reproducible example 表明Spring 未能将TestBean
bean 注入@Autowired
注释字段。
@SotiriosDelimanolis 我现在正在做,谢谢,它表明它不仅仅是一个超类,超类方法使用泛型。这就是导致问题的原因(它确实创建它,只是直到控制器之后)。但奇怪的是,覆盖它消除了这个问题。
@SotiriosDelimanolis 我更新了示例
【参考方案1】:
当 Spring 扫描您的配置类 ChildConfig
时,它会发现这个继承方法
@Bean
public T createTestBean()
return getT();
并为其注册一个bean定义。该 bean 定义包含有关 bean 类型的元数据。该类型是从方法的返回类型推断出来的。在这种情况下,它被解析为Object
,因为类型变量T
在其声明中没有边界,并且因为Spring 不会尝试根据ChildConfig
的extends ParentConfig<PCTestBean>
子句中提供的类型参数来解析它。
当 Spring 然后尝试处理
@Autowired
private PCTestBean pcTestBean;
注入目标,它寻找PCTestBean
bean,它认为它没有,因为缺少元数据。 如果 bean 没有通过其他一些强制命令被初始化,那么 Spring 没有其他信息可以继续并认为 bean 不存在。
当您将代码更改为
改为覆盖子类中的bean方法
方法的返回类型是PCTestBean
,然后Spring可以匹配@Autowired
注入需求,找到(并初始化)bean,然后注入它。
当您使用ApplicationContext#getBean(Class)
时,PCTestBean
已被初始化。因此,Spring 可以依赖于实例的实际类型。它会或多或少地循环遍历所有 bean 并检查 beanClass.isInstance(eachBean)
是否返回匹配的那个(或者如果超过一个则失败)。
Pankaj,in their answer,建议使用@DependsOn
(在您编辑问题之前,他们提出的建议是错误的)。这有助于建立我之前提到的顺序。
我不知道你的配置类有多么广泛,你认为你需要泛型来抽象一些行为,但我建议只删除泛型行为并在每个类中明确。
【讨论】:
您的解释 (it looks for a PCTestBean bean, which it doesn't think it has, because the metadata is lacking.
) 似乎不正确,因为它确实找到了它。我可以通过输入 appContext.getBean( BeanType.class )
搜索 bean,它确实找到了它,但要等到以后。
您提到可以通过某种强制方式初始化bean。我该怎么做?
@DonRhummy @DependsOn
是一种方式。我暂时想不到其他人。您还可以使用 bean 名称限定您的 @Autowired
,即。用@Resource(name = "createTestBean")
注释它。【参考方案2】:
试试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();
【讨论】:
使用@Autowired
,已经有一个隐含的依赖关系。 Spring 必须先初始化 bean,然后才能注入它。
好吧,你必须删除 @Bean
注释,同时在子类中覆盖它。以上是关于为啥在 Spring Boot 中超类上的 @Bean 泛型创建方法比子类上的方法调用晚?的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring Boot 测试类上使用 @WebMvcTest 注释时出错
如何在 Spring Boot 应用程序的同一个域类上同时使用 Spring Data JPA 和 Spring Data Elasticsearch 存储库?