2011-05-02 73 views
4

我想创建自己的自定义作用域bean,它将使用HTTP会话(类型的Flash范围)。Spring MVC自定义范围bean

根据春季手册我需要实现org.springframework.beans.factory.config.Scope接口

public class CustomScope implements Scope { 

    @Override 
    public Object get(String arg0, ObjectFactory<?> arg1) { 
     // TODO Auto-generated method stub 
     return null; 
    } 
    @Override 
    public String getConversationId() { 
     // TODO Auto-generated method stub 
     return null; 
    } 
    @Override 
    public void registerDestructionCallback(String arg0, Runnable arg1) { 
     // TODO Auto-generated method stub 
    } 
    @Override 
    public Object remove(String arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 
    @Override 
    public Object resolveContextualObject(String arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 
} 

我的问题是我怎么能获得HTTP这个bean的内部会议?我明白,如果我在ServletContext范围中创建bean,我会实现ServletContextAware接口。

请帮助:)

回答

9

我希望这将是有人在将来有用的,所以我想分享。

我做了一些研究,发现不幸的是,Spring MVC的HTTP Session是不可能的。

我的目的是使用PRG模式为我的Spring MVC控制器执行Flash Scope。

在春季论坛上做更多的研究我已经找到了使用HandlerInterceptor的方法。

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import javax.servlet.http.HttpSession; 

import java.util.*; 
import java.util.Map.Entry; 

import org.springframework.web.servlet.HandlerInterceptor; 
import org.springframework.web.servlet.ModelAndView; 

public class FlashScopeInterceptor implements HandlerInterceptor { 

    public static final String DEFAULT_ATTRIBUTE_NAME = "flashScope"; 
    public static final String DEFAULT_SESSION_ATTRIBUTE_NAME = FlashScopeInterceptor.class.getName(); 
    public static final int DEFAULT_RETENTION_COUNT = 2; 

    private String sessionAttributeName = DEFAULT_SESSION_ATTRIBUTE_NAME; 
    private String attributeName = DEFAULT_ATTRIBUTE_NAME; 
    private int retentionCount = DEFAULT_RETENTION_COUNT; 

    /** 
    * Unbinds current flashScope from session. Rolls request's flashScope to 
    * the next scope. Binds request's flashScope, if not empty, to the session. 
    * 
    */ 

    @Override 
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
      throws Exception { 
     if (request.getSession(false) != null) 
     { 
      request.getSession().removeAttribute(this.sessionAttributeName); 
     } 
     Object requestAttribute = request.getAttribute(this.attributeName); 
     if (requestAttribute instanceof MultiScopeModelMap) 
     { 
      MultiScopeModelMap attributes = (MultiScopeModelMap) requestAttribute; 
      if (!attributes.isEmpty()) 
      { 
       attributes.next(); 
       if (!attributes.isEmpty()) 
       { 
        request.getSession(true).setAttribute(this.sessionAttributeName, attributes); 
       } 
      } 
     } 
    } 

    /** 
    * merge modelAndView.model['flashScope'] to current flashScope 
    */ 
    @Override 
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 
      ModelAndView modelAndView) throws Exception { 
     if (modelAndView != null) 
     { 
      Map<String, Object> modelFlashScopeMap = null; 
      for (Iterator<Entry<String, Object>> iterator = ((Map<String, Object>) modelAndView.getModel()).entrySet() 
        .iterator(); iterator.hasNext();) 
      { 
       Entry<String, Object> entry = iterator.next(); 
       if (this.attributeName.equals(entry.getKey()) && entry.getValue() instanceof Map) 
       { 
        if (modelFlashScopeMap == null) 
        { 
         modelFlashScopeMap = (Map) entry.getValue(); 
        } 
        else 
        { 
         modelFlashScopeMap.putAll((Map) entry.getValue()); 
        } 
        iterator.remove(); 
       } 
       else if (entry.getKey().startsWith(this.attributeName + ".")) 
       { 
        String key = entry.getKey().substring(this.attributeName.length() + 1); 
        if (modelFlashScopeMap == null) 
        { 
         modelFlashScopeMap = new HashMap<String, Object>(); 
        } 
        modelFlashScopeMap.put(key, entry.getValue()); 
        iterator.remove(); 
       } 
      } 
      if (modelFlashScopeMap != null) 
      { 
       MultiScopeModelMap flashScopeMap; 
       if (request.getAttribute(this.attributeName) instanceof MultiScopeModelMap) 
       { 
        flashScopeMap = (MultiScopeModelMap) request.getAttribute(this.attributeName); 
       } 
       else 
       { 
        flashScopeMap = new MultiScopeModelMap(this.retentionCount); 
       } 
       flashScopeMap.putAll(modelFlashScopeMap); 
       request.setAttribute(this.attributeName, flashScopeMap); 
      } 
     } 
    } 

    /** 
    * binds session flashScope to current session, if not empty. Otherwise cleans up empty 
    * flashScope 
    */ 
    @Override 
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 

     HttpSession session = request.getSession(false); 
     if (session != null) 
     { 
      Object sessionAttribute = session.getAttribute(this.sessionAttributeName); 
      if (sessionAttribute instanceof MultiScopeModelMap) 
      { 
       MultiScopeModelMap flashScope = (MultiScopeModelMap) sessionAttribute; 
       if (flashScope.isEmpty()) 
       { 
        session.removeAttribute(this.sessionAttributeName); 
       } 
       else 
       { 
        request.setAttribute(this.attributeName, flashScope); 
       } 
      } 
     } 
     return true; 
    } 
} 

现在MultiScopeModelMap.java

import java.io.Serializable; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.Map; 
import java.util.Set; 

import org.apache.commons.collections.map.CompositeMap; 
import org.apache.commons.collections.map.CompositeMap.MapMutator; 

public class MultiScopeModelMap extends CompositeMap implements Serializable, MapMutator 
{ 
    public MultiScopeModelMap(int num) 
    { 
     super(); 
     setMutator(this); 
     for(int i = 0; i < num; ++i) 
     { 
      addComposited(new HashMap()); 
     } 
    } 

    /** Shadows composite map. */ 
    private final LinkedList<Map> maps = new LinkedList<Map>(); 

    @Override 
    public synchronized void addComposited(Map map) throws IllegalArgumentException 
    { 
     super.addComposited(map); 
     this.maps.addLast(map); 
    } 



    @Override 
    public synchronized Map removeComposited(Map map) 
    { 
     Map removed = super.removeComposited(map); 
     this.maps.remove(map); 
     return removed; 
    } 



    /** 
    * Starts a new scope. 
    * All items added in the session before the previous session are removed. 
    * All items added in the previous scope are still retrievable and removable. 
    */ 
    public void next() 
    { 
     removeComposited(this.maps.getFirst()); 
     addComposited(new HashMap()); 
    } 

    public Object put(CompositeMap map, Map[] composited, Object key, Object value) 
    { 
     if(composited.length < 1) 
     { 
      throw new UnsupportedOperationException("No composites to add elements to"); 
     } 
     Object result = map.get(key); 
     if(result != null) 
     { 
      map.remove(key); 
     } 
     composited[composited.length-1].put(key, value); 
     return result; 
    } 

    public void putAll(CompositeMap map, Map[] composited, Map mapToAdd) 
    { 
     for(Entry entry: (Set<Entry>)mapToAdd.entrySet()) 
     { 
      put(map, composited, entry.getKey(), entry.getValue()); 
     } 
    } 

    public void resolveCollision(CompositeMap composite, Map existing, Map added, Collection intersect) 
    { 
     existing.keySet().removeAll(intersect);  
    } 

    @Override 
    public String toString() 
    { 
     return new HashMap(this).toString(); 
    } 


} 

用法:

@RequestMapping(value="/login.do", method=RequestMethod.POST) 
    public ModelAndView login(@Valid User user){ 
     ModelAndView mv = new ModelAndView("redirect:result.html"); 
     if (authService.authenticate(user.getUserName(), user.getPassword())) 
      mv.addObject("flashScope.message", "Success"); 
     //else 
      mv.addObject("flashScope.message", "Login Failed"); 
     return mv; 
    } 

@RequestMapping(value ="/result.html", method=RequestMethod.GET) 
    public ModelAndView result(){ 
     ModelAndView mv = new ModelAndView("login/loginAction"); 
     return mv; 
    } 

在JSP中的用法很简单:

${flashScope.message} 

另外,你需要配置FlashScopeInterceptor类如在terceptor。

<bean id="flashScopeInterceptor" class="x.y.z.FlashScopeInterceptor" /> 
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name="interceptors"> 
    <list><ref bean="flashScopeInterceptor"/></list> 
    </property> 
</bean> 
+0

伟大的解决方案! – nMoncho 2013-04-05 17:24:14

+1

@nMoncho,但如果您在引入RedirectAttributes对象的情况下使用Spring 3.1或更高版本,则不再相关。 – 2013-04-05 20:55:27

+0

是的,我知道,但该应用程序已经在生产(我不知道改变Spring版本风险多大,可能不是很多) – nMoncho 2013-04-08 19:27:29

2

我建议看看的org.springframework.web.context.request.SessionScope的源代码。这个范围必须解决一些相同的问题。

看起来,他们使用: RequestContextHolder.currentRequestAttributes().getSessionId()

+0

我同意(+1)。生成自定义范围是一个非常高级的主题,不应该没有理由地完成 – 2011-05-03 07:32:23

+0

那么在Spring MVC中实现Flash范围的正确方法是什么? – 2011-05-03 08:47:38

+0

danny.lesnik:“我的问题是如何获得这个bean内部的HTTP会话?” - 我给你提供了SessionScope的提示 - 这只是一个提示 – Ralph 2011-05-03 08:58:24

0

您可以访问会话属性使用Spring MVC中的范围类的方法在未来的代码(320工程):

RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); 
attributes.getAttribute("some key", NativeWebRequest.SCOPE_SESSION); 
attributes.setAttribute("some key", YouObject, NativeWebRequest.SCOPE_SESSION); 

RequestAttributes实现(ServletRequestAttributes)内部会调用设置/的getAttribute ()方法当前会话对象。