至少有两种方法可以将Spring bean放入上下文配置中:
@Bean
声明一个方法。@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
之间的方法为何不同?我错过了什么吗?
答案 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();
}
}
}