首页 > 解决方案 > @ComponentScan 和 @Bean 在上下文配置中有什么区别?

问题描述

至少有两种方法可以将 Spring bean 放入上下文配置中:

  1. @Bean在配置类中声明一个方法。
  2. 放上@ComponentScan配置类。

我期望这两种方法在生成的 Spring bean 方面没有区别。

但是,我找到了一个示例来说明差异:

// UserInfoService.java

public interface UserInfoService
{
    @PreAuthorize("isAuthenticated()")
    String getUserInfo ();
}
// UserInfoServiceTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
        @ContextConfiguration(classes = TestSecurityContext.class),
        @ContextConfiguration(classes = UserInfoServiceTest.Config.class)
})
public class UserInfoServiceTest
{
    @Configuration
    public static class Config
    {
        @Bean
        public UserInfoService userInfoService ()
        {
            return new UserInfoServiceImpl();
        }
    }

    @Autowired
    private UserInfoService userInfoService;

    @Test
    public void testGetUserInfoWithoutUser ()
    {
        assertThatThrownBy(() -> userInfoService.getUserInfo())
                .isInstanceOf(AuthenticationCredentialsNotFoundException.class);
    }

    @Test
    @WithMockUser
    public void testGetUserInfoWithUser ()
    {
        String userInfo = userInfoService.getUserInfo();
        assertThat(userInfo).isEqualTo("info about user");
    }

上面的代码是为了测试服务中的安全注解UserInfoService。但是,它会在testGetUserInfoWithoutUser(). 原因是 beanuserInfoService没有被 Spring Security 代理。因此,调用userInfoService.getUserInfo()没有被注释阻止@PreAuthorize("isAuthenticated()")

但是,如果我用 替换@Bean注释@ComponentScan,一切都会开始工作。也就是说,beanuserInfoService将被代理并且调用userInfoService.getUserInfo()将被@PreAuthorize注解阻塞。

@Bean为什么和之间的方法@ComponentScan不同?我错过了什么?

PS 完整的例子在这里,上面提到的修复在这里

标签: javaspringspring-securityspring-test

解决方案


这是因为@ContextHierarchy将创建具有父子层次结构的多个弹簧上下文。在您的情况下,TestSecurityContext 为父上下文定义 bean 配置,同时UserInfoServiceTest.Config为子上下文定义。

如果 没有@ComponentScanUserInfoServiceTest.Config则安全相关的 bean 定义在父上下文中,对子上下文中的UserInfoServicebean 是不可见的,因此它不会被 Spring Security 代理。

另一方面,如果您@ComponentScan在 上定义UserInfoServiceTest.Config ,它还将扫描@Configuration包含UserInfoService(及其所有子包)的包中的所有 bean。因为TestSecurityContext也在这个包内,因此它被扫描并且与安全相关的bean也被配置为子上下文。UserInfoService然后在子上下文中将由 Spring Security 代理。(注意:在这种情况下,父上下文和子上下文都有自己的一组安全相关 bean)

顺便说一句,如果您只需要一个上下文,您可以简单地使用@ContextConfiguration

@ContextConfiguration(classes= {TestSecurityContext.class,UserInfoServiceTest.Config.class})
public class UserInfoServiceTest {

    public static class Config {
        @Bean
        public UserInfoService userInfoService() {
            return new UserInfoServiceImpl();
        }
    }
}

推荐阅读