@ComponentScan和@Bean在上下文配置中有什么区别?

时间:2019-08-16 23:50:10

标签: java spring spring-security spring-test

至少有两种方法可以将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()上失败。原因是bean userInfoService没有被Spring Security代理。因此,呼叫userInfoService.getUserInfo()不会被注释@PreAuthorize("isAuthenticated()")阻塞。

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

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

P.S。完整的示例是here,上面提到的修复程序是here

1 个答案:

答案 0 :(得分:3)

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

如果@ComponentScan上没有UserInfoServiceTest.Config,则在父上下文中定义与安全相关的bean,而在子上下文中UserInfoService bean不可见与安全相关的bean,因此它确实没有被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();
        }
    }
}