Spring MVC custom scope bean

前端 未结 3 1732
时光取名叫无心
时光取名叫无心 2020-12-03 02:09

I would like to create my own custom scope bean which will use HTTP session (kind of Flash scope).

According to Spring Manual I need to implement org.springframewor

相关标签:
3条回答
  • 2020-12-03 02:47

    You can get access to session attributes using the next code within your scope class methods in Spring MVC (works in 3.2):

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

    RequestAttributes implementation (ServletRequestAttributes) internally will call set/getAttribute() methods on current session object.

    0 讨论(0)
  • 2020-12-03 02:59

    I recommend to have a look at the source code of org.springframework.web.context.request.SessionScope. This scope must have solved the same problem some how.

    It looks like that they use: RequestContextHolder.currentRequestAttributes().getSessionId()

    0 讨论(0)
  • 2020-12-03 03:00

    I hope it will be useful for someone in the future, so I would like to share it.

    I made some reserch on it and have found that unfortunately, it is impossible to get HTTP Session for Spring MVC.

    My purpose was Flash Scope implementation for my Spring MVC Controller using PRG pattern.

    Making more research in Spring Forum I've found the way to do it using 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;
        }
    }
    

    Now 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();
        }
    
    
    }
    

    Usage:

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

    In JSP the usage is very simple:

    ${flashScope.message}
    

    In addition you need to configure FlashScopeInterceptor class as interceptor.

    <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 讨论(0)
提交回复
热议问题