Spring singleton @autowired服务和共享状态

时间:2014-10-31 19:15:24

标签: spring applicationcontext

我有一个控制器和一个过滤器,我在其中注入一个特定的服务。 在该服务中,我有一个Hashmap,我尝试存储某些信息。我遇到的问题是,虽然看起来创建了该服务的单个实例并将其注入我的控制器和我的过滤器,但似乎有两个Map实例。我不知道为什么。无论我如何尝试实例化地图(或注入它),行为仍然是相同的。

事实证明,问题在于创建了两个服务实例,并在控制器中注入了一个实例,在过滤器中注入了一个实例。我不清楚为什么会这样,以及如何解决它。

以下是代码的摘录:

@Controller
public MyController {



  @Autowired
  private MyService myService;


  someEndpoint() {
    ....
    myService.putData(key, value);
    .....
  }


}


public class MyFilter extends GenericFilterBean {


  @Autowired
  private MyService myService;


  public void doFilter(...) {

     //this is where I have a problem. 
     // the reference myService.myMap seems to be pointing to a different instance 
     // than the service.myMap in the controller which doesn't make any sense to me
     // the filter obviously intercepts all requests so I would expect that after that particular
     // endpoint is accessed the data will be there for subsequent requests
     myService.getData(..);

  }

  .....
}


@Service
public class MyService {


  private Map <String,String> myMap = new HashMap <String,String> ();


  public String getData(String key) {
    return myMap.get(key);
  }

  public void putData(String key, String value){
     myMap.put(key,value);
  }


}

以下是app-config.xml的摘录

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <context:component-scan base-package="com.mycompany.myPackage"/>
    <context:annotation-config />


     .......

    <security:http
    ........
    ........
    <security:custom-filter ref="myFilter" position="FORM_LOGIN_FILTER" />
    ....


    ...........

    <bean class="com.mycompany.filters.MyFilter" id="myFilter"/>

和web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-config.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>


    <servlet>
        <servlet-name>Dispatcher Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>Dispatcher Servlet</servlet-name>
        <url-pattern>/webservice/*</url-pattern>
    </servlet-mapping>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

在您的web.xml中设置:

 <servlet>
     <servlet-name>Dispatcher Servlet</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-config.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>

创建一个由servlet启动的Web应用程序上下文。

然后你也有:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/app-config.xml</param-value>
</context-param>

还会创建一个父根Web应用程序上下文,在您的情况下是一个重复的上下文。 正确使用此方案有利于您可能拥有更多servlet的情况,每个servlet定义其自己的隔离上下文,但从公共根上下文(服务,数据源等)继承bean定义。它还为创建分层上下文提供了良好的实践路线图,即防止服务bean依赖于您的mvc层。

除非您有多个配置文件,否则应将空值分配给servlet配置的contextConfigLocation:

<servlet>
     <servlet-name>Dispatcher Servlet</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>

小心点。不要省略参数。 Spring会根据你的servlet推断一些默认的配置文件名,如果不存在则会抱怨。

答案 1 :(得分:0)

GenericFilterBean不在应用程序上下文中。我希望上面的代码中有java.lang.InstantiationException。您可以通过过滤器的 ServletContext 获取您的bean。任何其他实例化技巧都会导致地图重复。

答案 2 :(得分:0)

您确定只创建了一个MyService类实例吗?检查这个的最简单方法是提供默认构造函数实现并在其中打印一些文本。请检查一下,让我知道,因为我有一些怀疑。

如果这是真的,并且有两个MyService类实例,那么当您将一些数据放到地图上时,可能会使用 MyService的实例A 并从地图获取数据你使用 MyService的实例B ......我会等你的回复继续/停止这种想法。