我试图使用Jersey的Spring Boot(1.3.5)构建一个概念证明。我的pom将spring-boot-starter-jersey
工件列为依赖项。
只定义了单例bean,它运行正常。但是,我现在尝试将请求范围的bean注入单例bean并且遇到以下异常:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.jndi': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.getTarget(CglibAopProxy.java:687) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:637) ~[spring-aop-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at $java.util.Properties$$EnhancerBySpringCGLIB$$1a020092.size(<generated>) ~[na:na]
at org.springframework.jndi.JndiTemplate.createInitialContext(JndiTemplate.java:133) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiTemplate.getContext(JndiTemplate.java:103) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:85) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.ejb.access.AbstractRemoteSlsbInvokerInterceptor.lookup(AbstractRemoteSlsbInvokerInterceptor.java:99) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.ejb.access.AbstractSlsbInvokerInterceptor.refreshHome(AbstractSlsbInvokerInterceptor.java:121) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.ejb.access.SimpleRemoteSlsbInvokerInterceptor.refreshHome(SimpleRemoteSlsbInvokerInterceptor.java:162) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.ejb.access.AbstractSlsbInvokerInterceptor.afterPropertiesSet(AbstractSlsbInvokerInterceptor.java:108) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean.afterPropertiesSet(SimpleRemoteStatelessSessionProxyFactoryBean.java:101) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
... 21 common frames omitted
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
... 40 common frames omitted
根据我所读到的内容,我需要启用我已经完成的RequestContextListener
,但它没有任何区别:
@Configuration
public class AppConfig {
@Bean(name="jndi")
@Scope(scopeName=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public Properties getJNDIContext(){
Properties p = new Properties();
p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" );
p.setProperty("java.naming.provider.url", "jnp://localhost:1099");
p.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory");
p.setProperty(Context.SECURITY_PRINCIPAL,"test" );
p.setProperty(Context.SECURITY_CREDENTIALS,"12345678" );
return p;
}
@Bean
public FactoryBean<?> getAthleteManagerFactory(@Qualifier("jndi") Properties p){
SimpleRemoteStatelessSessionProxyFactoryBean factory = new SimpleRemoteStatelessSessionProxyFactoryBean();
String beanName = "zone.jndi.ejb3.RemoteAthleteManager";
factory.setJndiName(beanName);
factory.setBusinessInterface(AthleteManager.class);
factory.setJndiEnvironment(p);
return factory;
}
@Bean
public RequestContextListener requestContextListener(){
return new RequestContextListener();
}
}
我的控制器非常简单:
@Component
@Path("/athlete")
public class AthleteController {
@Autowired
private AthleteManager athleteManager;
....
....
}
我怀疑错误是工厂正在尝试使用代理请求范围的bean来实例化对象以注入到我的Controller中,并且请求范围还不存在。
有没有办法解决这个问题?我本来期望proxyMode
能够处理这个问题,但它似乎没有任何区别。无论是否启用proxyMode
,我都会得到类似的(尽管略有不同)异常堆栈跟踪。
如果我将我的工厂标记为Lazy,它仍然没有什么区别,因为Spring需要实例化它(及其对象)以自动装配我的控制器类。
有没有办法做到这一点?我需要一个过滤器在每次调用时覆盖getJNDIContext()
属性对象中的凭据。
答案 0 :(得分:0)
我终于找到了解决问题的方法,虽然我不是特别喜欢它。对于初学者,我需要从启动时延迟JNDI bean查找/初始化,以便容器不会尝试填充JNDI属性,直到它们实际可用(仅在请求时)。我还注意到我无法将JNDI范围扩展到请求范围,因此每次都必须查找它(或者身份验证凭据可能不正确)。
@Bean(name="jndi")
@Scope(scopeName=WebApplicationContext.SCOPE_REQUEST, proxyMode=ScopedProxyMode.TARGET_CLASS)
public Properties getJNDIContext(){
Properties p = new Properties();
p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" );
p.setProperty("java.naming.provider.url", "jnp://localhost:1099");
p.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory");
return p;
}
@Bean
public FactoryBean<?> getLoginManagerFactory(@Qualifier("jndi") Properties p){
return getEjbFactoryBean(LoginManager.class, p );
}
/**
* Helper method to setup the factory
* @param clazz
* @param jndi
* @return
*/
private SimpleRemoteStatelessSessionProxyFactoryBean getEjbFactoryBean(Class clazz, Properties jndi){
SimpleRemoteStatelessSessionProxyFactoryBean factory = new SimpleRemoteStatelessSessionProxyFactoryBean();
// need to delay lookup since security params are only available in a request thread
factory.setLookupHomeOnStartup(false);
// refresh home in case EJB server is restarted
factory.setRefreshHomeOnConnectFailure(true);
// do not cache - need to lookup every time due to changing security params. They are only set upon initial bean lookup
factory.setCacheHome(false);
// set the JNDI properties
factory.setJndiEnvironment(jndi);
String beanName = REMOTE_EJB_PREFIX + clazz.getSimpleName();
factory.setJndiName(beanName);
factory.setBusinessInterface(clazz);
return factory;
}
最后,我设置了一个过滤器,以便在每个请求中指定我的凭据:
@Provider
public class SecurityFilter implements ContainerRequestFilter {
@Autowired @Qualifier("jndi")
private Properties userCreds;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// extract username/password from basic auth
String[] auth = decode( requestContext.getHeaderString(HttpHeaders.AUTHORIZATION) );
userCreds.setProperty(Context.SECURITY_PRINCIPAL, auth[0] );
userCreds.setProperty(Context.SECURITY_CREDENTIALS, auth[1] );
}
}
从概念上讲,这是有效的,但效率非常低。我想找到一个允许我做同样的解决方案,但至少在请求级别对我的bean进行范围调整,这样每次都不会检索它。