【问题标题】:What's the difference between @ComponentScan and @Bean in a context configuration?@ComponentScan 和 @Bean 在上下文配置中有什么区别?
【发布时间】:2019-08-16 23:50:10
【问题描述】:

至少有两种方法可以将 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()") 阻塞。

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

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

附:完整示例为here,上述修复为here

【问题讨论】:

  • 如果使用@Bean,工厂方法不应该也用@PreAuthorize注解吗?

标签: java spring spring-security spring-test


【解决方案1】:

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

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

另一方面,如果您在UserInfoServiceTest.Config 上定义@ComponentScan,它还将扫描包含UserInfoService(及其所有子包)的包中的所有@Configuration 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();
        }
    }
}

【讨论】:

  • 您的意思是子上下文中的UserInfoService bean 对于父上下文中的安全相关bean 是不可见的?您的第二段似乎相反?
  • 是的。他们是一样的。关键是与安全相关的 bean 仅可见并应用于同一上下文中的 bean。
  • 我明白了。非常感谢您的回答。
猜你喜欢
  • 2016-05-02
  • 2014-09-12
  • 2017-10-07
  • 1970-01-01
  • 2011-05-07
  • 2021-03-10
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
相关资源
最近更新 更多