可访问 Bean 的位置或时间的限制
Limitations on where or when Beans are accessible
我正面临无法访问我创建的 Bean 的情况,这让我想知道它们在哪里以及何时!?
我制作了两个 Bean,一个作用域为 singleton
,另一个作用域为 request
。我已通过在 RestController
class 中自动装配它们来确保它们已正确实现。而且他们人口众多,毫无疑问。
现在我写了一个授权检查器 class 扩展 PreInvocationAuthorizationAdvice
。作为授权 class,我需要访问当前用户的信息。所以我将当前用户的 Bean 自动连接到这个 class,这是 request
范围的 Bean。我还需要一个自定义的 ACL 引擎,它以 singleton
方式自动装配。但是当我到达需要使用这两个属性的地步时,它们都是 null
!
那么,对于可以访问 Bean 的位置和时间有哪些限制?
顺便说一句,我的 @Configuration
class 也被 @ComponentScan({"my.base.package"})
注释,这是我指定的 class 的父包,包括 @Autowired
属性.
[更新]
我想我找到了问题所在,但我仍在努力寻找解决方案。
具有 @Autowired
属性的 class 正在实例化为 Bean 本身。我认为这个已故的 Bean 在它所依赖的其他 Bean 之前被实例化,因此它们尚不可用。无论如何我可以指定被实例化的 Bean 的顺序吗?
[P.S.]
任何将此问题标记为 "off-topic because: This question does not appear to be about programming" 的人都非常有趣 :)
[更新]
只是 @Autowired
属性 为 null 时的示例。
这些是我的配置classes:
@Configuration
@PropertySource("/config.properties")
@ComponentScan({"my.package"})
public class AppConfig implements ApplicationContextAware
{
private ApplicationContext appContext;
@Autowired
private Environment env;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.appContext = applicationContext;
}
@Bean
public RedissonClient getRedisson()
{
//Code ommited: returning a redisson connection.
}
}
@Configuration
@ComponentScan({"my.pacakge"})
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends GlobalMethodSecurityConfiguration
{
@Bean
public AclEngine getAclEngine()
{
return new AclEngine();
}
@Autowired
private RedissonClient redisson;
@Bean
@Scope(value = "request")
public User getCurrentUser()
{
//Code ommited: retrieving the user from Redisson and returning it.
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception
{
auth.authenticationProvider(authenticator());
}
@Bean
public AuthenticationProvider authenticator()
{
return new AclAuthenticationProvider();
}
@Bean
HttpSessionSecurityContextRepository getHttpSessionSecurityContextRepository()
{
HttpSessionSecurityContextRepository x = new HttpSessionSecurityContextRepository();
x.setAllowSessionCreation(false);
return x;
}
@Bean
SecurityContextPersistenceFilter getSecurityContextPersistenceFilter()
{
return new SecurityContextPersistenceFilter(getHttpSessionSecurityContextRepository());
}
@Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
public class AclAuthenticationProvider implements AuthenticationProvider
{
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
return null;
}
@Override
public boolean supports(Class<?> authentication)
{
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer
{
public SessionInitializer()
{
super(SecurityConfig.class);
}
}
}
最后我遇到的问题是:
public class ResourceBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice
{
@Autowired
private User currentUser;
@Autowired
private AclEngine aclEngine;
@Override
public boolean before(Authentication authentication, MethodInvocation methodInvocation, PreInvocationAttribute preInvocationAttribute)
{
//Where I want to access currentUser and aclEngine but they are null.
//I can trace the code to this point without any Exception thrown!
}
}
@Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
Spring 只会将引用注入它管理的 class 个实例(又名 beans)。当您在方法内部创建 bean 并将它们直接注入其他 bean 时,那些新创建的 bean 是 Spring 托管 bean,因此不符合任何自动连接或 post 处理的条件 spring 随便。
而不是
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
您应该将该代码移动到 @Bean
方法,以便它成为 Spring 托管 bean 并将注入依赖项。
@Bean
public ResourceBasedPreInvocationAdvice expressionAdvice() {
return new ResourceBasedPreInvocationAdvice();
}
并且只引用此方法而不是创建新实例。
ResourceBasedPreInvocationAdvice expressionAdvice = expressionAdvice();
我正面临无法访问我创建的 Bean 的情况,这让我想知道它们在哪里以及何时!?
我制作了两个 Bean,一个作用域为 singleton
,另一个作用域为 request
。我已通过在 RestController
class 中自动装配它们来确保它们已正确实现。而且他们人口众多,毫无疑问。
现在我写了一个授权检查器 class 扩展 PreInvocationAuthorizationAdvice
。作为授权 class,我需要访问当前用户的信息。所以我将当前用户的 Bean 自动连接到这个 class,这是 request
范围的 Bean。我还需要一个自定义的 ACL 引擎,它以 singleton
方式自动装配。但是当我到达需要使用这两个属性的地步时,它们都是 null
!
那么,对于可以访问 Bean 的位置和时间有哪些限制?
顺便说一句,我的 @Configuration
class 也被 @ComponentScan({"my.base.package"})
注释,这是我指定的 class 的父包,包括 @Autowired
属性.
[更新]
我想我找到了问题所在,但我仍在努力寻找解决方案。
具有 @Autowired
属性的 class 正在实例化为 Bean 本身。我认为这个已故的 Bean 在它所依赖的其他 Bean 之前被实例化,因此它们尚不可用。无论如何我可以指定被实例化的 Bean 的顺序吗?
[P.S.]
任何将此问题标记为 "off-topic because: This question does not appear to be about programming" 的人都非常有趣 :)
[更新]
只是 @Autowired
属性 为 null 时的示例。
这些是我的配置classes:
@Configuration
@PropertySource("/config.properties")
@ComponentScan({"my.package"})
public class AppConfig implements ApplicationContextAware
{
private ApplicationContext appContext;
@Autowired
private Environment env;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.appContext = applicationContext;
}
@Bean
public RedissonClient getRedisson()
{
//Code ommited: returning a redisson connection.
}
}
@Configuration
@ComponentScan({"my.pacakge"})
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends GlobalMethodSecurityConfiguration
{
@Bean
public AclEngine getAclEngine()
{
return new AclEngine();
}
@Autowired
private RedissonClient redisson;
@Bean
@Scope(value = "request")
public User getCurrentUser()
{
//Code ommited: retrieving the user from Redisson and returning it.
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception
{
auth.authenticationProvider(authenticator());
}
@Bean
public AuthenticationProvider authenticator()
{
return new AclAuthenticationProvider();
}
@Bean
HttpSessionSecurityContextRepository getHttpSessionSecurityContextRepository()
{
HttpSessionSecurityContextRepository x = new HttpSessionSecurityContextRepository();
x.setAllowSessionCreation(false);
return x;
}
@Bean
SecurityContextPersistenceFilter getSecurityContextPersistenceFilter()
{
return new SecurityContextPersistenceFilter(getHttpSessionSecurityContextRepository());
}
@Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
public class AclAuthenticationProvider implements AuthenticationProvider
{
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
return null;
}
@Override
public boolean supports(Class<?> authentication)
{
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer
{
public SessionInitializer()
{
super(SecurityConfig.class);
}
}
}
最后我遇到的问题是:
public class ResourceBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice
{
@Autowired
private User currentUser;
@Autowired
private AclEngine aclEngine;
@Override
public boolean before(Authentication authentication, MethodInvocation methodInvocation, PreInvocationAttribute preInvocationAttribute)
{
//Where I want to access currentUser and aclEngine but they are null.
//I can trace the code to this point without any Exception thrown!
}
}
@Override
protected AccessDecisionManager accessDecisionManager()
{
try {
AffirmativeBased ab = (AffirmativeBased) super.accessDecisionManager();
List<AccessDecisionVoter<? extends Object>> advs = ab.getDecisionVoters();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
List<AccessDecisionVoter<? extends Object>> toBeRemoved = new ArrayList<>();
for (AccessDecisionVoter<? extends Object> adv : advs) {
if (adv instanceof PreInvocationAuthorizationAdviceVoter) {
toBeRemoved.add(adv);
}
}
for (AccessDecisionVoter<? extends Object> adv : toBeRemoved) {
advs.remove(adv);
}
advs.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return ab;
}
catch (ClassCastException ex) {
ArrayList decisionVoters = new ArrayList();
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
return new AffirmativeBased(decisionVoters);
}
}
Spring 只会将引用注入它管理的 class 个实例(又名 beans)。当您在方法内部创建 bean 并将它们直接注入其他 bean 时,那些新创建的 bean 是 Spring 托管 bean,因此不符合任何自动连接或 post 处理的条件 spring 随便。
而不是
ResourceBasedPreInvocationAdvice expressionAdvice = new ResourceBasedPreInvocationAdvice();
您应该将该代码移动到 @Bean
方法,以便它成为 Spring 托管 bean 并将注入依赖项。
@Bean
public ResourceBasedPreInvocationAdvice expressionAdvice() {
return new ResourceBasedPreInvocationAdvice();
}
并且只引用此方法而不是创建新实例。
ResourceBasedPreInvocationAdvice expressionAdvice = expressionAdvice();