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

先放出这个问题的来源,是我在对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分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • IOC 控制反转容器控制程序对象之间的关系,而不是传统实现中,有程序代码之间控制,又名依赖注入。All 类的创建,...
    irckwk1阅读 4,626评论 0 0
  • 16. Web MVC 框架 16.1 Spring Web MVC 框架介绍 Spring Web 模型-视图-...
    此鱼不得水阅读 4,729评论 0 4
  • 监听器(listener) 监听器简介 :监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个...
    奋斗的老王阅读 7,370评论 0 53
  • J2EE是一套全然不同于传统应用开发的技术架构,包含许多组件,主要可简化且规范应用系统的开发与部署,进而提高可移植...
    大佛爱读书阅读 1,373评论 0 0
  • 1.内存泄露 内存泄漏两种情况: 在堆中申请的空间没有被释放(虚拟机gc可以解决) 对象已不在使用,但仍然在内存中...
    Aimerwhy阅读 3,813评论 0 0

友情链接更多精彩内容