【问题标题】:Spring MVC custom scope beanSpring MVC 自定义范围 bean
【发布时间】:2011-05-02 23:12:15
【问题描述】:

我想创建自己的自定义范围 bean,它将使用 HTTP 会话(一种 Flash 范围)。

根据 Spring Manual 我需要实现 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;
    }
}

我的问题是如何在这个 bean 中获取 HTTP 会话?我知道如果我在 ServletContext 范围内创建 bean,我会实现 ServletContextAware 接口。

请帮忙:)

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    希望以后对大家有用,分享一下。

    我对此进行了一些研究,不幸的是,无法获得 Spring MVC 的 HTTP Session。

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

    在 Spring 论坛中进行更多研究,我找到了使用 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类作为拦截器。

    <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> 
    

    【讨论】:

    • @nMoncho,但如果您使用引入了 RedirectAttributes 对象的 Spring 3.1 或更高版本,则不再相关。
    • 是的,我知道,但该应用程序已经投入生产(我不知道更改 Spring 版本的风险有多大,可能风险不大)
    • 这是有风险的,如果你想使用 RedirectAttributes 你的请求映射方法应该只返回 String 而不是 ModelAndView 如果你返回 ModelAndView 你必须改变 MVC 逻辑。
    • 我已经尝试过这种方法,并且一直成功到在 jsp 中呈现。我知道这是旧的,但我可能在这里做错了什么:stackoverflow.com/questions/19481378/…
    【解决方案2】:

    我建议看一下org.springframework.web.context.request.SessionScope的源代码。这个范围一定以某种方式解决了同样的问题。

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

    【讨论】:

    • 我同意 (+1)。生成自定义作用域是一个非常高级的话题,不应该无缘无故地这样做
    • 那么在 Spring MVC 中实现 Flash Scope 的正确方法是什么?
    • danny.lesnik:“我的问题是如何在这个 bean 中获取 HTTP 会话?” - 我给了你 SessionScope 的提示 - 这只是一个提示
    • @Ralph,我知道这是提示 :),但 @Sean 只是告诉这是高级主题,所以也许在 Spring MVC 中实现 Flash 范围可能有更简单的方法 :)
    • @danny:如果您在问题中解释了“闪存范围”是什么,也许可以。我知道这是来自 JSF 的东西,但我不知道它是什么意思。
    【解决方案3】:

    您可以使用 Spring MVC 中范围类方法中的下一个代码访问会话属性(适用于 3.2):

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

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

    【讨论】:

      猜你喜欢
      • 2013-03-03
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多