在Webwork和Struts2框架中,ActionContext扮演了全局上下文的功能。无论是在JSP页面、Action类、或是其他诸如Service、DAO或工具类中,都可以通过这个类提供的方法,来访问诸如Request、Session等范围对象,以及所谓的“值栈(ValueStack)”对象。该类内部采用ThreadLocal保存当前线程的实例,因此使用时只需要调用ActionContext.getContext()方法即可访问当前线程的上下文。而在实际应用中,我们还可以自己根据需要创建类似的Context对象,来实现对上下文变量的封装。
由上述代码可见,对于每个线程,调用AppContext都会返回相同的实例。但是,只能获得实例还不够,还必须添加访问上下文变量的方法:
这样一个基本的Context类就有了。下面要解决的就是Context中的变量在何时,通过何种方式设定到Context中的问题。
通常,会有一个Filter,当请求进入时,该Fileter会将当前请求相关的变量,如Response,Request,Session等等设定到当前线程的Context对象中。如果是我们自己编写Context类的话,可能也需要编写一个实现类似功能的Filter类。
比如,我在开发公司的CMS系统的时候,需要频繁访问诸如项目(Project),模板(Template),栏目(Column)......等等对象。而在请求时,往往会有诸如?pid=3&tid=4&cid=5之类的请求参数。此时便开发一个CMSContext类,通过Filter根据请求参数将当前请求相关的项目信息、模板信息和栏目信息设定到Context类中。之后,无论在JSP页面还是在JAVA类中,只要通过
就可以直接获得当前请求相关的项目、模板和栏目信息,而不需要在Action、Service和DAO之间传递pid、tid和cid。
这样一方面简化了方法方法声明,加快了开发速度,另一方面,提供了访问环境变量的一致方法,提高了代码的规范性。
除了可以采用Filter预设变量以外,这种模式还可以更加灵活地加以运用,不仅可以通过与Filter来实现全局上下文,还可以在局部的复杂调用过程中充当上下文。
再有一点需要注意的是就是关于线程继承的问题。大多数应用环境都是单线程的,也就是说所有处理都在当前请求线程内进行。但有时,可能会出现需要创建子线程的情况。在这种情况下,在子线程内调用Context.getContext()方法将获得新的(未初始化的)上下文对象,因为ThreadLocal中保存的是子线程的Context实例,而非父线程的。
要解决这种问题,就需要在Context类中使用InheritableThreadLocal来代替ThreadLocal:
这样,在当前线程中创建的子线程就也能通过Context.getContext()的方式访问父线程中的上下文变量了。