2014-10-31 102 views
1

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

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

以下是代码的提取物:

@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); 
    } 


} 

这里是应用程序-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>

任何帮助,非常感谢。

+0

你有多少上下文?你可以发布你的web.xml吗?还有上下文配置? – grid 2014-11-01 09:45:25

回答

0

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

+0

您能否详细说明您的意思是“GenericFilter不在应用程序上下文中”?我在我的应用程序上下文中定义了MyFilter,并且没有得到任何实例化例外...... – 2014-10-31 23:01:48

+0

所以,这意味着您有两个不同的上下文。这就是为什么你有两个实例。在你的代码中,服务和控制器是注释的,但过滤器不是。通过** GenericFilter **的想法,它不应该在应用程序上下文中。 – Dmytro 2014-11-01 12:56:44

+0

那么为什么/如何创建两个上下文?我没有注释过滤器,因为我在我的app-config中定义了它(参见上面的编辑)。我必须在应用程序上下文中定义它,因为它被其他元素引用。 – 2014-11-02 22:41:23

0

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

如果这是真的,有的MyService类的两个实例,有可能当你把一些数据映射,您使用的MyService的实例的,当你从地图上获取数据使用实例B我的服务 ...我会等待你的回应继续/停止这种想法。

+0

这是我的第一个想法,但正如我在上面提到的问题中提到的同一个MyService实例注入。所以这不是问题的根源。 – 2014-10-31 22:19:31

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推断出一些默认的配置文件名,如果它不存在就会投诉。

+0

谢谢。您的评论指出了我的正确方向。我无法解决上述问题,因为这会破坏控制器中的映射,所以我创建了两个单独的配置文件,将一个配置文件传递给DispatcherServlet。 – 2014-11-03 18:29:28