Struts核心代码ActionServlet的运行漫游

本文深入探讨Struts框架的运行机制,解析其初始化过程与运行时流程,重点介绍ActionServlet的作用及内部实现细节。

    早在3年前就开始用Struts了,因该也算用的比较早的一批人。菜鸟的时候也曾经看过别人写的Struts的运行机制,也看过部分代码,但都是草草了事,自己明白了大致方向就结束了。我经常问别人我们写的Action如UserAction是不是Serlvet的,是不是单例的。其实我也没看过实际代码,这样问别人的时候,内心也经常惶恐,惟恐怕从网上看见的资料有假。

     在公司担任技术经理也很长时间了,虽然公司希望自己在开发深度上有所突破,但是本身作为小公司,自己涉及的事情又太多了,时而搞搞bea的portal,时而出去吹吹牛,还经常带几个什么都不会的菜鸟做项目,这几年做的东西也不算少,eai soa eip bmp都有实际的项目经验,但是哪个都没有坚持一路走下去,今天领导要我给公司做java时间比较短的人讲讲struts,正长是个机会,可以把Struts的运行机制,自己好好看看。因为我觉得如果给大家讲hello world,估计大家也不会满意。说了半天废话(我真的很爱说废话诶)下面进入主题吧。

   Struts两大功能,我自己的体会,Struts一个很实用的功能是它的jsp tag,这个是个独立的模块,我们的页面都用他的tag ,虽然现在的extremecomponents也是不错。

  Struts的另一个主要的功能是业务数据流的传递   jsp---->form—》action,这个是整个Struts的核心流程。

  除了这两个主要的功能,剩下的都,都是些具体的基类和接口,方便我们自己的使用,从Struts1.0  1.1 1.2 1.3我们不难看出Struts的趋势也是越来越简单,越来越使用,也不是很一味的追求概念的真理化了。

    其实Struts的运行机制,只要看看ActionServlet就ok了,今天下午花了2个小时看了一边,也算略有收获吧,把原来的一些猜测的内容看清楚出处了。  如果你在次之前,对 单例和多线程不了解,我建议你 补充一下基础概念,因为所有的开源代码里这2个概念都是核心概念,代码也都是围绕着这2个概念去进行的。

    因为Struts也是jsp Servlet机制运行的,所以我们的入口点还先从Servlet的源头开始看,我们打开一个Struts的web项目。打开web.xml文件。 

 <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>3</param-value>
    </init-param>
    <init-param>
      <param-name>detail</param-name>
      <param-value>3</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

我们发现Struts需要在我们的项目里配置一个Servlet  ActionServlet它有几个初始化参数,和监听url里包含.do的url

我们打开ActionServlet类.先看看它的init()方法,因为这个是启动tomcat时就会执行的代码,看看它一开始做了些什么工作
    public void init() throws ServletException {

        initInternal();  
        initOther();
        initServlet();

        // Initialize modules as needed
        getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
        ModuleConfig moduleConfig = initModuleConfig("", config);
        initModuleMessageResources(moduleConfig);
        initModuleDataSources(moduleConfig);
        initModulePlugIns(moduleConfig);
        moduleConfig.freeze();
        Enumeration names = getServletConfig().getInitParameterNames();
        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            if (!name.startsWith("config/")) {
                continue;
            }
            String prefix = name.substring(6);
            moduleConfig = initModuleConfig
                (prefix, getServletConfig().getInitParameter(name));
            initModuleMessageResources(moduleConfig);
            initModuleDataSources(moduleConfig);
            initModulePlugIns(moduleConfig);
            moduleConfig.freeze();
        }
        destroyConfigDigester();

    }

 

里面都是些初始化的操作,其实不看代码,我们用脚都能想出来,它肯定是把struts-config.xml文件的内容装载到它封状好的配置信息类里,至于配置信息类,我就不介绍了,无非是些工厂模式或者单例的java类,

  我们先看 initInternal()

 internal = MessageResources.getMessageResources(internalName);  //不用具体看了,是把资源文件装载到类里   if (defaultFactory == null)
            defaultFactory = MessageResourcesFactory.createFactory();
        return defaultFactory.createResources(config);  工厂模式,基本上init里都是这么操作的

initOther() 这个方法读取web.xml里配置的

<init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>

并把这个属性值方法actionSerlvet的 protected String config = "/WEB-INF/struts-config.xml";里,因为servlet是单例的,这个属性也就理所当然的成为全局变量了,其实不是特殊位置,没必要在web.xml里配置了,因为这个属性 有默认值。  initOther() 方法对里convertNull也做了处理,如果你在web,xml 配置的话,它就会给beanutil注册默认转化信息。

initServlet() 也是把web.xml里的信息初始化了一下,放到 getServletContext()中了。对整个流程没影响。

getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); 这句话,很有意思,自己把自己注册到

getServletContext().中,在struts中,经常看见这样的用法。

oduleConfig moduleConfig = initModuleConfig("", config);
        initModuleMessageResources(moduleConfig);
        initModuleDataSources(moduleConfig);
        initModulePlugIns(moduleConfig);
        moduleConfig.freeze();

读取struts-config.xml文件的内容,到类里,看名字也能知道都初始化了什么。就不多说了。

Enumeration names = getServletConfig().getInitParameterNames();
        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            if (!name.startsWith("config/")) {
                continue;
            }
            String prefix = name.substring(6);
            moduleConfig = initModuleConfig
                (prefix, getServletConfig().getInitParameter(name));
            initModuleMessageResources(moduleConfig);
            initModuleDataSources(moduleConfig);
            initModulePlugIns(moduleConfig);
            moduleConfig.freeze();
        }

这部分代码,就是把web.xml配置的多个模块的信息读入,重复刚才的初始化信息,因为默认的config已经初始化了,就continue过去了,呵呵,感觉这样写的好处是把config第一初始化,然后再初始化其他模块,但是不明白这样的顺序有什么关系,懒的理了,还有它的初始话,基本上都是把配置信息读到类里,然后再getServletContext().setAttribute,这样就加速了struts的运行期速度,如果有人要用Struts做业务平台,自动产生代码的话,这些东东估计都要重写了。

   Servlet的init工作结束了,省下的我们来看一下运行期的代码轨迹。我们简单建了action

public class UserAction extends DispatchAction {
 public ActionForward hello(
  ActionMapping mapping,
  ActionForm form,
  HttpServletRequest request,
  HttpServletResponse response) {
  UserForm userForm = (UserForm) form;
  System.out.println("hhhhhhhhhhhhhhhhhhh");
  
  return mapping.findForward("tt");
 }

} 然后启动tomcat,请求hello的方法http://localhost:7001/struts1.1/user.do?do=hello

通过web.xml我们知道了,actionServlet  mapping了,do的url,所以这个请求肯定到了actionserlvet的get方法,我们看代码,发现无论是get还是post方法都是调用了process(request, response);我们进入这个方法        RequestUtils.selectModule(request, getServletContext());
        getRequestProcessor(getModuleConfig(request)).process
            (request, response);

呵呵,比较简单,只有2句话,其实可以猜测出这两句话,一个是把url转换成对应的类的调用情况,然后再调用相应的类的相应的方法,我们分别进入,仔细看一下,RequestUtils.selectModule(request, getServletContext());
 RequestUtils这个类看名字都知道它的用处了,这个方法先是检查这个请求是属于哪个配置文件里的,我们这里没有其他的配置文件只有一个,所有没有前缀。这个方法的主要作用是把init方法读入的那些信息从ServletContext中再读去到request对象中。以备下面的方法调用,原因目前不详。不过看代码这些细节不必一次全部搞的特别清楚,能明确大的流程就行了,呵呵。

 getRequestProcessor(getModuleConfig(request)).process
            (request, response);

先是执行getModuleConfig(request))方法,拿到config的真正实现类,也是从request中拿的,如果request没有,则去ServletContext去拿。直接的类是ModuleConfigImpl,再把这个类,作为参数传入actionserlvet的getRequestProcessor方法,这个方法有关键字synchronized,通过这个关键字,来处理Struts的多线程的问题。这个方法是实力化RequestProcessor,如果有的话,就不实例化了,其实也是个单例的应用,并对这个类多了init无非是把那些配置信息又加载到这个类里,下面我们看关键的process方法,我们已经加载了很多配置信息,接下来,肯定要做的就是把这些配置信息和url做匹配,找到真正的类的运行方法,其实也就是找到我们的action类,根据java的面向对象的特性,它肯定是初始化了基类,再调用我们的业务代码。

 request = processMultipart(request);  封装request对象,平时一般的请求,直接返回request对象。 String path = processPath(request, response);  拿到url中的/user  通过这个参数  实例化一个ActionMapping 的类,

这个类是把    <action
      attribute="userForm"
      name="userForm"
      parameter="do"
      path="/user"
      scope="request"
      type="com.nbw.struts.action.UserAction"
      validate="false">
      <forward
        name="tt"
        path="/tt.jsp"
        contextRelative="true" />
    </action>  取取到对象中。再作为参数  实例化ActionForm form = processActionForm(request, response, mapping);   然后processPopulate(request, response, form, mapping);把请求中的request对象参数值,复制到对应的form 的参数中,这里用到了beanutil的开源jar,Struts和beanutil结合的还是比较密切的,它在1.2的版本里也给beanutil捐献了一些基类,但是beanutil本身存在一些问题,这有可能是个隐患。

Action action = processActionCreate(request, response, mapping);根据mapping 对象实例了action类,我们知道,我们的所有的action都是继承过来的,这个方法里的有意思的地方是,它先去HashMap actions 里取寻找类的实例。如果没有再实例,再放到hashmap中,这行代码我们知道了action的子类其实是个单例,这叫要我们写业务代码的人,注意到这点,action里的变量其实是全局变量,是多人共享的。

 processActionPerform(request, response,
                                 action, form, mapping)

利用类的反射机智,执行actio的hello方法,
processForwardConfig(request, response, forward);进行跳转,到页面显示的jsp

再jsp里Struts有封装了很多的tag方便显示。不过那些和Struts的运行轨迹已经没有关系了,大家也没必要看了,

内容概要:本文介绍了一个针对电力系统连锁故障传播路径的N-k多阶段双层优化及故障场景筛选模型,该模型基于混合整数线性规划(MILP)方法构建,旨在全面评估电力系统在遭受多重故障时的脆弱性与恢复能力。通过引入故障传播路径的概念,模型能够动态模拟故障在电网中的逐级扩散过程,并结合多阶段优化策略,实现对关键故障场景的有效识别与优先排序。整个框架不仅考虑了初始故障元件的选取,还涵盖了后续因潮流转移引发的级联跳闸行为,从而提升了风险评估的准确性与时效性。该研究已在Matlab平台上完成代码实现,具备良好的可复现性和工程应用价值,适用于提升现代电网的安全防御水平。; 适合人群:电力系统、能源安全及相关领域的科研人员、高校研究生以及从事电网规划与运行管理的工程技术人员。; 使用场景及目标:①用于电力系统安全评估中识别最危险的N-k故障组合;②支撑电网应急预案制定与薄弱环节改造;③作为学术研究中关于级联故障建模与优化求解的教学与验证工具;④服务于智能电网背景下抵御蓄意攻击或极端事件的风险防控决策。; 阅读建议:建议读者结合Matlab代码深入理解模型的数学 formulation 与求解流程,重点关注目标函数设计、约束条件构建及双层优化结构的实现逻辑,同时可通过调整系统参数和故障设定进行仿真对比分析,以掌握不同因素对连锁故障演化的影响规律。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值