English 中文(简体)
Spring:注入依赖于上下文的bean(会话/web或本地线程/后台进程)
原标题:Spring: Inject bean depended on context (session/web or local thread/background process)

是否可以创建一个工厂或代理来决定线程是在(Web)Request还是后台进程(即scheduler)中运行,然后根据这些信息创建会话bean还是原型bean?

示例(伪Spring配置:)

<bean id="userInfoSession" scope="session" />
<bean id="userInfoStatic" scope="prototype" />

<bean id="currentUserInfoFactory" />

<bean id="someService" class="...">
    <property name="userInfo" ref="currentUserInfoFactory.getCurrentUserInfo()" />
</bean>

我希望这能让我的问题更容易理解。。。


我的解决方案

更新自己的问题永远不会太迟;)。我用两个不同的客户端会话实例解决了这个问题,一个是SessionScoped客户端会话,另一个是SingletonScoped会话。两者都是普通的豆子。

<bean id="sessionScopedClientSession" class="com.company.product.session.SessionScopedClientSession" scope="session">
    <aop:scoped-proxy />
</bean>

<bean id="singletonScopedClientSession" class="com.company.product.session.SingletonScopedClientSession" />

<bean id="clientSession" class="com.company.product.session.ClientSession">
    <property name="sessionScopedClientSessionBeanName" value="sessionScopedClientSession" />
    <property name="singletonScopedClientSessionBeanName" value="singletonScopedClientSession" />
</bean>

然后,ClientSession将决定是单例作用域还是会话作用域:

private IClientSession getSessionAwareClientData() {
    String beanName = (isInSessionContext() ? sessionScopedClientSessionBeanName : singletonScopedClientSessionBeanName);
    return (IClientSession) ApplicationContextProvider.getApplicationContext().getBean(beanName);
}

可以通过以下方式收集会话类型:

private boolean isInSessionContext() {
    return RequestContextHolder.getRequestAttributes() != null;
}

所有类都实现了一个名为IClientSession的接口。singletonScoped和sessionScoped bean都是从找到实现的BaseClientSession扩展而来的。

然后每个服务都可以使用客户端会话,即:

@Resource
private ClientSession clientSession;

    ...

public void doSomething() {
    Long orgId = clientSession.getSomethingFromSession();
}

现在,如果我们更进一步,我们可以为会话编写类似Emulator的东西。这可以通过初始化clientSession(它不在请求的上下文中)单例会话来完成。现在,所有服务都可以使用相同的clientSession,我们仍然可以“模拟”用户,即:

        clientSessionEmulator.startEmulateUser( testUser );
        try {
            service.doSomething();
        } finally {
            clientSessionEmulator.stopEmulation();
        }

还有一个建议:注意SingletonScoped clientSession实例中的线程处理!哦,我想我可以用更少的台词来完成它;)如果你想了解更多关于这种方法的信息,请随时与我联系。

最佳回答

你的措辞确实简单得多:)

您的currentUserInfoFactory可以使用RequestContextHolder.getRequestAttributes()。如果存在会话并与调用线程关联,则这将返回一个非null对象,然后您可以从上下文中安全地检索会话范围的bean。如果它返回null,那么您应该获取原型范围的bean。

它不是很整洁,但很简单,应该可以用。

问题回答

我创建了一个小的通用解决方案,根据上下文注入bean。

我猜我们有两颗豆子:

<bean class="xyz.UserInfo" id="userInfo" scope="session" />
<bean class="xyz.UserInfo" id="userInfoSessionLess" />

We want to use "userInfo" bean for web user actions and "userInfoSessionLess" bean for background services for example. Wa also want to write code and don t want to think about context, for example:

@Autowired
//You will get "java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request?" for session less services.
//We can fix it and autowire "userInfo" or "userInfoSessionLess" depends on context...
private UserInfo userInfo;

public save(Document superSecureDocument) {
    ...
    superSecureDocument.lastModifier = userInfo.getUser();
    ...
}

现在,我们需要创建自定义会话范围以使其工作:

public class MYSessionScope extends SessionScope implements ApplicationContextAware {
  private static final String SESSION_LESS_POSTFIX = "SessionLess";
  private ApplicationContext applicationContext;
  public Object get(String name, ObjectFactory objectFactory) {
    if (isInSessionContext()) {
      log.debug("Return session Bean... name = " + name);
      return super.get(name, objectFactory);
    } else {
      log.debug("Trying to access session Bean outside of Request Context... name = " + name + " return bean with name = " + name + SESSION_LESS_POSTFIX);
      return applicationContext.getBean(name.replace("scopedTarget.", "") + SESSION_LESS_POSTFIX);
    }
  }
  private boolean isInSessionContext() {
    return RequestContextHolder.getRequestAttributes() != null;
  }
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}

注册新范围:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="mySession">
                <bean class="com.galantis.gbf.web.MYSessionScope" />
            </entry>
        </map>
    </property>
</bean>

现在我们需要修改beans定义如下:

<bean class="xyz.UserInfo" id="userInfo" scope="mySession" autowire-candidate="true"/>
<bean class="xyz.UserInfo" id="userInfoSessionLess" autowire-candidate="false"/>

仅此而已。如果我们在实际的web请求线程之外使用Bean,那么名称为“SessionLess”的Bean将用于所有“mySession”范围的Bean。

创建两个自定义上下文加载器,将相同的作用域定义绑定到不同的实现:

public final class SessionScopeContextLoader extends GenericXmlContextLoader {

   protected void customizeContext(final GenericApplicationContext context) {
     final SessionScope testSessionScope = new SessionScope();
     context.getBeanFactory().registerScope("superscope", testSessionScope);
   }
    ...
}

然后为singleton创建一个相应的范围(只使用statics创建自己的范围)

然后,您只需在xml启动中为这两个上下文中的每一个指定适当的上下文加载程序。





相关问题
why the session in iis automatically log out?

I used iis6, and when i called a function Directory.delete(), the all the session relate this website will be logged out. and i debugged the website, i found no exception. any one have ideas on this ? ...

Check session from a view in CodeIgniter

What is the best way to check session from a view in CodeIgniter, it shows no way in their user guide, otherwise I will have to make two views on everything, which is kinda weird...still a newbie to ...

Can I get the size of a Session object in bytes in c#?

Is it possible to get the size(in bytes) of a Session object after storing something such as a datatable inside it? I want to get the size of a particular Session object, such as Session["table1"], ...

提供严格分类的出席会议物体

提供严格分类的与会机会的最佳方式是什么? 我正计划转而选择矩阵,这正在促使汇编者抱怨我的幻觉方案拟订方法......

PHP Session is not destroying after user logout

I m trying to create an authentication mechanism for my PHP Application and I m having difficulty destroying the session. I ve tried unsetting the authentication token which was previously set within ...

热门标签