美文网首页
如何在servlet无关的类中取到HttpSession?

如何在servlet无关的类中取到HttpSession?

作者: 就这些吗 | 来源:发表于2019-12-25 02:16 被阅读0次

    先放出这个问题的来源,是我在对Controller进行AOP编程的时候遇到的,大致需求是在切面前获取HttpSession中的对象,但是AOP的切面方法又与servlet无关,怎么获得呢?。又可以延伸出,SpringMVC是怎么获得HttpSession的?虽然平常我们在编写Controller的时候直接写在方法参数就行了,但是底层大致是怎么实现的呢?今天就来探究一下。

    先直接抛出答案,怎么在AOP获得HttpSession?

    ServletRequestAttributes attr = >(ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
    HttpSession session=attr.getRequest().getSession(true);
    

    看到这个代码,业务方面是解决了,又得到新的问题
    HttpSession是怎么保证这个就是本次请求的HttpSession?
    RequestContextHolder是什么东西?

    首先分析RequestContextHolder这个类,
    里面有两个ThreadLocal保存当前线程下的request(这两个request又是怎么放进ThreadLocal的下面再讲)

    1.png

    有一个getRequestAttributes()来获取ThreadLocal中的对象,保证了这个线程获得的就是当前线程的Request,而HttpSession存在Request中。

    T@HGC2X97}4ANQ$J%TUB7HF.png

    在我们最上面解决业务的代码中用到了currentRequestAttributes(),调用了上面的getRequestAttributes()来获得Request。

    3.png

    至此稍微理了一下RequestContextHolder的调用,那么SpringMVC是怎么把request放到ThreadLocal里来的呢?
    先回忆下SpringMVC的工作流程

    1.http请求过来,请求到DispatcherServlet
    2.DispatcherServlet调用HandlerMapping,解析请求对应的Handler
    3.解析到对应的Handler之后由HandlerAdapter(也就是我们的平常写的Controller)根据这个Handler处理请求和业务
    4.处理完之后返回一个ModelAndView给DispatcherServlet,拿到这个参数后由DispatcherServlet调用ViewResolver
    5.ViewResolver解析后返回给DispatcherServlet一个view
    6.DispatcherServlet通过这个view渲染视图,再返回给用户

    我们经常在Controller里直接写(HttppSession session)来拿到这个HttpSession,那么很明显了,这个应该是DispatcherServlet的功劳_。

    Ctrl+Shift+T找到DispatcherServlet,按下F4查看继承关系,可以看到明显父类有HttpServlet,在FrameworkServlet按下Ctrl+O,找到了里面有重写一堆service(),doGet()之类的方法,里面调用了processRequest(request,response)。这就找到线索了嘛,跟进去看看

    33.png

    方法写的有点多,这里我们只关心request这一参数的传递,这里列出每个方法大概的意思,里面有个方法名叫initContextHolders,意思是初始化的方法,还记得一开始讲的那个类叫啥不,RequestContextHolder,我觉得差不多就要发现真相了。注意两个红框框起来的方法哦。

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
     
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            //获取上一个请求保存的LocaleContext
            LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
            //建立新的LocaleContext
            LocaleContext localeContext = buildLocaleContext(request);
            //获取上一个请求保存的RequestAttributes
            RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
            //建立新的RequestAttributes
            ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
     
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
            //具体设置的方法
            initContextHolders(request, localeContext, requestAttributes);
    
    87MNVQH`8_KOLGNIZUN9RSM.png

    先来看看initContextHolders(),果然嘛,跟之前的肯定有联系,不然怎么通过RequestContextHolder得到的Request中的HttpSession呢,这个时候有眼尖的小伙伴可能会说了,你这里是requestAttributes啊,不是request,我们倒回去看上面的方法,这个requestAttributes到底是个啥?_

    L{U3~FFS8448DMN~3V%@IR5.png

    那我们看看这是个啥?哦豁,返回了一个ServletRequestAttributes,大家都长差不多,你有啥不一样??

    A4R~Y}1}Y920H$VW4P0TZSH.png

    妥了,这个看起来就很心旷神怡,就是初始化了这个类中的request和response嘛,那我们上面这个问题也解决了,ServletRequestAttributes封装了request和response。
    还记得我们为什么要找这个ServletRequestAttributes吗,在前面的方法里没有把request放进ThreadLocal里,而是放了这个对象,所以我们能拿到request,甚至能拿到response,还顺便把前面为什么能强转成ServletRequestAttributes都解释了

    T`}GM5}B4WYA3835I3YQPCS.png

    —————————————————————————————————————————
    基本就讲完啦,觉得晕吗?再来梳理一遍

    ServletRequestAttributes attr = >(ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
    HttpSession session=attr.getRequest().getSession(true);
    

    1.RequestContextHolder中有ThreadLocal,用来存储本线程的ServletRequestAttributes(这个是reque和response的封装类,里面有request哦,request里面有HttpSession哦)

    2.FrameworkServlet继承了HttpServlet,重写了一堆doGet(),doPost()方法,这些重写的方法调用了processRequest(),别管上层是怎么封装的,你在发Http请求的时候肯定会用到这些方法,就调用了嘛,然后就触发了这个方法了。

    3.这个方法会先把request和response封装成ServletRequestAttributes类,然后放到RequestContextHolder的ThreadLocal的里面。

    4.至此,我们就可以通过调用RequestContextHolder来获得本线程的ServletRequestAttributes啦(再重申一遍,这里面有request和response,request里有HttpSession)

    参考:SpringMVC学习记录(九)--RequestContextHolder分析

    相关文章

      网友评论

          本文标题:如何在servlet无关的类中取到HttpSession?

          本文链接:https://www.haomeiwen.com/subject/htrqoctx.html