关于struts2中ActionContext的实现原理

简介:

   北京,雾霾天气阻止了今天的马拉松之行,蜗居一天。为一个问题“struts2如何保证ActionContext每次取的都是本次请求所对应的实例?”,给一个网友解释了半天。

   首先,我们知道,struts2struts1的一个重要区别就是它进行了Action类和Servlet的解耦。而又提供了获取Servlet API的其它通道,就是ActionContext(别跟我说还有个ServletActionContext,其实ServletActionContext只是ActionContext的一个子类而已)。源码为证:

1
public  class  ServletActionContext  extends  ActionContext  implements  StrutsStatics

   其次,他也知道,ActionContextAction执行时的上下文,可以看作是一个容器,并且这个容器只是一个Map而已,在容器中存放的是Action在执行时需要用到的VALUE_STACKACTION_NAMESESSIONAPPLICATIONACTION_INVOCATION等等对象,还可以存放自定义的一些对象。我想用过struts2的朋友们,大多也都知道这些吧。

   第三,他奇怪的是,在一个请求的处理过程拦截器、action类和result中任何时候获取的ActionContext都是跟当前请求绑定那一个。为什么!?

 

我给他的建议是,带着问题读源码,呵呵。那我们一起来看看吧:

首先ActionContext类的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public  class  ActionContext  implements  Serializable{
   static  ThreadLocal actionContext =  new  ThreadLocal();
   public  static  final  String ACTION_NAME =  "com.opensymphony.xwork2.ActionContext.name" ;
   public  static  final  String VALUE_STACK =  "com.opensymphony.xwork2.util.ValueStack.ValueStack" ;
   public  static  final  String SESSION =  "com.opensymphony.xwork2.ActionContext.session" ;
   public  static  final  String APPLICATION =  "com.opensymphony.xwork2.ActionContext.application" ;
   public  static  final  String PARAMETERS =  "com.opensymphony.xwork2.ActionContext.parameters" ;
   public  static  final  String LOCALE =  "com.opensymphony.xwork2.ActionContext.locale" ;
   public  static  final  String TYPE_CONVERTER =  "com.opensymphony.xwork2.ActionContext.typeConverter" ;
   public  static  final  String ACTION_INVOCATION =  "com.opensymphony.xwork2.ActionContext.actionInvocation" ;
   public  static  final  String CONVERSION_ERRORS =  "com.opensymphony.xwork2.ActionContext.conversionErrors" ;
   public  static  final  String CONTAINER =  "com.opensymphony.xwork2.ActionContext.container" ;
   Map<String, Object> context;
   public  ActionContext(Map<String, Object> context)
   {
     this .context = context;
   }
   //... ...
   public  static  void  setContext(ActionContext context)
   {
     actionContext.set(context);
   }
   public  static  ActionContext getContext()
   {
     return  (ActionContext)actionContext.get();
   }
   public  void  setContextMap(Map<String, Object> contextMap)
   {
     getContext().context = contextMap;
   }
   public  Map<String, Object> getContextMap()
   {
     return  this .context;
   }
   //... ...
   public  void  setSession(Map<String, Object> session)
   {
     put( "com.opensymphony.xwork2.ActionContext.session" , session);
   }
   public  Map<String, Object> getSession()
   {
     return  (Map)get( "com.opensymphony.xwork2.ActionContext.session" );
   }
   //... ...
   public  Object get(String key)
   {
     return  this .context.get(key);
   }
   public  void  put(String key, Object value)
   {
     this .context.put(key, value);
   }
}

源码清晰的说明了我们编程中再熟悉不过的一行代码:ActionContext ctx = ActionContext.getContext();,原来我们所取得的ctx来自于ThreadLocal啊!熟悉ThreadLocal的朋友都知道它是与当前线程绑定的,而且是我们Java中处理多线程问题的一种重要方式。我们再看,类中有个Map类型的变量context,其实,它才是前面我们提到的真正意义上的“容器”,用来存放Action在执行时所需要的那些数据。

    到这里,他最初的那个问题已经很了然了。但是,他紧接着又一个疑惑提出来了:“那既然每个请求处理线程都有自己的ActionContext,那里面的那些数据是什么时候放进去的呢”?

   这次我给他的建议是,动脑筋,用源码验证。既然ActionContext存放有HttpServletRequest及其中的参数,既然ActionContext贯穿于整个请求处理过程,那就从struts2请求处理的入口(过滤器StrutsPrepareAndExecuteFilter)找,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public  class  StrutsPrepareAndExecuteFilter  implements  StrutsStatics, Filter
{
   // ... ...
   public  void  doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
     throws  IOException, ServletException
   {
     HttpServletRequest request = (HttpServletRequest)req;
     HttpServletResponse response = (HttpServletResponse)res;
     try
     {
       this .prepare.setEncodingAndLocale(request, response);
       this .prepare.createActionContext(request, response); //就是在这里进行创建并初始化ActionContext实例
       this .prepare.assignDispatcherToThread();
       if  (( this .excludedPatterns !=  null ) && ( this .prepare.isUrlExcluded(request,  this .excludedPatterns))) {
         chain.doFilter(request, response);
       else  {
         request =  this .prepare.wrapRequest(request);
         ActionMapping mapping =  this .prepare.findActionMapping(request, response,  true );
         if  (mapping ==  null ) {
           boolean  handled =  this .execute.executeStaticResourceRequest(request, response);
           if  (!handled)
             chain.doFilter(request, response);
         }
         else  {
           this .execute.executeAction(request, response, mapping);
         }
       }
     finally  {
       this .prepare.cleanupRequest(request);
     }
   }
    //... ...
}

再找到prepare对应的类PrepareOperations,查看方法createActionContext(),就一目了然了。

   对于ServletActionContext作为ActionContext一个直接子类,原理也是类似的,感兴趣的朋友可以看一下。

   帮助别人,同时也是帮助自己。把这个处理的过程记录下来,希望对需要的朋友有所帮助。



     本文转自NightWolves 51CTO博客,原文链接:http://blog.51cto.com/yangfei520/1565698,如需转载请自行联系原作者



相关文章
|
消息中间件 存储 数据可视化
kafka高可用集群搭建
kafka高可用集群搭建
318 0
|
前端开发 Java 关系型数据库
基于javaweb旅游景点线路预定系统设计与实现
基于javaweb旅游景点线路预定系统设计与实现
279 0
|
8月前
|
安全 Java 开发者
Java 泛型中的通配符 T,E,K,V,?有去搞清楚吗?
本文介绍了Java泛型及其通配符的使用方法与作用。泛型是JDK5引入的特性,提供编译时类型安全检测,避免运行时类型转换异常。文中详细解析了常见通配符T、E、K、V、?的含义及应用场景,如无界通配符`&lt;?&gt;`、上界通配符`&lt;? extends T&gt;`和下界通配符`&lt;? super T&gt;`。通过代码示例,展示了泛型在类型安全、消除强制转换、代码复用和增强可读性等方面的优势。最后强调深入理解技术原理的重要性,以提升开发能力。
351 0
|
移动开发 安全 测试技术
『App自动化测试之Appium应用篇』| 继承于selenium常用的元素定位方法有哪些?如何使用?
『App自动化测试之Appium应用篇』| 继承于selenium常用的元素定位方法有哪些?如何使用?
615 0
|
边缘计算 持续交付 数据中心
Docker在边缘计算中的崭露头角:探索容器技术如何驱动边缘计算的新浪潮
边缘计算是一项快速发展的技术,它旨在将计算能力更接近数据源和终端用户,以提供低延迟、高性能的计算体验。在这个充满活力的领域,Docker容器技术崭露头角,成为推动边缘计算革新的一股新力量。本文将深入探讨Docker在边缘计算中的应用,介绍其优势和挑战,并穿插一些示例代码,以帮助读者更好地理解这一新兴趋势。 第一部分:边缘计算和Docker容器 边缘计算的定义 边缘计算是一种计算范式,它将计算资源和数据处理能力推向网络边缘,靠近数据源和终端用户。这与传统的集中式云计算模型形成鲜明对比,后者将大部分计算任务集中在中央数据中心。边缘计算的关键目标是减少数据传输的延迟,提高响应速度,以满足对实时性要
758 0
|
Java Web App开发 存储
解决java poi海量数据导出内存溢出问题
解决java poi海量数据导出导致内存溢出的问题
13779 0
|
应用服务中间件 测试技术 Linux
|
安全 JavaScript Linux
PHPStorm常用快捷键
PHPStorm常用快捷键
391 0
|
SQL Java 关系型数据库
Hibernate - hibernate.cfg.xml配置文件详解
Hibernate 配置文件主要用于配置数据库连接和 Hibernate 运行时所需的各种属性。每个 Hibernate 配置文件对应一个 Configuration 对象–这样说不太正确,应该是项目初始化时会实例化Configuration对象,该实例对象(Configuration对象)会读取配置文件配置的属性值为实例对象赋值。
892 0

热门文章

最新文章