【5.0苍穹源码浅析】一、项目启动与请求分发原创
金蝶云社区-丁梦洋
丁梦洋
4人赞赏了该文章 345次浏览 未经作者许可,禁止转载编辑于2024年04月06日 14:21:25

一、启动入口

kd.bos.service.bootstrap.Booter#main

重点看这几行:先获取启动类,然后start

image.png

看到这说明苍穹还内置了springboot,不过一般都是以下面的jetty方式启动

image.png

EmbedJettyServer的start方法主要就是在组装jetty相关类,设置相关属性,比如:

  1. context.setContextPath设置访问根路径

  2. context.setDescriptor设置web.xml路径,可类比tomcat项目的web.xml

  3. context.setResourceBase设置前端资源路径

image.png

这里添加了一个处理器集合,里面有四个处理器,但是没看到这些处理器addServlet(web项目处理请求一般都是在servlet里的,比如spring的DispatchServlet),然后启动(jetty的源码咱就不看了......)

image.png


二、请求分发

这样看下来好像还是不知道苍穹是在哪处理请求的?

还记得上面设置过的web.xml不(bos-resources-5.0.jar!/webapp/web.xml),里面兴许有配servlet

<filter>
     <filter-name>kFilter</filter-name>
     <filter-class>kd.bos.service.web.filter.KDCommonFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>kFilter</filter-name>
   <url-pattern>*</url-pattern>
</filter-mapping>


  <servlet>
       <servlet-name>KCFServlet</servlet-name>
       <servlet-class>kd.bos.kcf.KCFServlet</servlet-class>
      <init-param>
        <param-name>route_path</param-name>
        <param-value>/sys/{formId}/{operationId}:kd.bos.web.api.ApiServiceType;/ai/do:kd.bos.web.api.AIServiceType;/app/{appId}/{servicename}/{method}:kd.bos.web.api.CustomApiServiceType</param-value>
    </init-param>
   </servlet>
   <servlet-mapping>
   <servlet-name>KCFServlet</servlet-name>
   <url-pattern>/kapi/*</url-pattern>
</servlet-mapping>
<servlet>
   <servlet-name>KWSServlet</servlet-name>
   <servlet-class>kd.bos.kws.KWSServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>KWSServlet</servlet-name>
   <url-pattern>/kws/*</url-pattern>
</servlet-mapping>

<servlet>
   <servlet-name>HystrixMetricsStreamServlet</servlet-name>
   <servlet-class>com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>HystrixMetricsStreamServlet</servlet-name>
   <url-pattern>/hystrix.stream</url-pattern>
</servlet-mapping>

确实有配servlet,但都有各自的匹配url规则,比如KCFServlet是处理/ierp/kapi请求的(开放平台),正常单据页面的请求大多是/ierp/form/xxx,这里没看到匹配的servlet,不慌,还有个kFilter拦截了所有请求。

它的doFilter方法会构造一个过滤器链来处理

image.png

这个build方法会先从servletList找到匹配uri的servlet去构建FilterChain返回(后续会调用其service方法),再从filterList找到匹配uri的filter去构建FilterChain返回(后续会调用其doFilter方法),打断点可以看到servletList为空,走下面filter那块的逻辑了

image.png

filterList里有很多filter,比如设置traceId的TraceFilter,和登录相关的LoginFilter,我们找个业务单据的代码打个断点,能发现调用栈里是走的ActionFilter。

它会先根据路径判断有没有这个请求的配置,再去获取Invoker(封装了处理类对象和method,方便反射调用)

image.png

这个寻找逻辑也还比较简单,利用了两层map和一个实例对象map,拿请求/ierp/form/batchInvokeAction.do来说,先根据form拿到它下面的action配置,再根据batchInvokeAction找到对应的action配置,再获取实例对象,最后组装Invoker

image.png

我们回过头看看这几个map的初始化过程,可以发现是在ActionConfig类的静态方法里,会解析/action/actionconfig.xml配置文件(也是在bos-resources-5.0.jar里

image.png

配置内容如下,后续解析文件的代码咱就不看了,其实就是package标签对应packageMap里的一个value,name就是key,同理action标签对应actionMap,objectMap就是根据class实例化的对象集合,而且这里命名还挺有规律的,form对应FormAction类,batchInvokeAction对应batchInvokeAction方法

<?xml version="1.0" encoding="utf-8"?>
<root>
   <include file="action/qing-actionconfig.xml" />

   <package name="form" class="kd.bos.web.actions.FormAction">
      <action name="getConfig" method="getConfig" />
      <action name="getConfigByPageId" method="getConfigByPageId" />
      <action name="getConfigByParameter" method="getConfigByParameter" />
      
      <action name="releaseRootPage" method="releaseRootPage" />
      <action name="getMetadata" method="getMetadata" />
      <action name="batchInvokeAction" method="batchInvokeAction" />
      <action name="invokeAction" method="batchInvokeAction" />
      <action name="timerElapsed" method="timerElapsed" />
      <action name="pushAICommand" method="pushAICommand" />
      <action name="getQingColumns" method="getQingColumns" />
      <action name="getQingData" method="getQingData" />
      <action name="export" method="export" />
      <action name="ksqlTranslate" method="ksqlTranslate" /><!-- 临时ksql翻译界面TransUtil.jsp -->
      <!-- 获取界面主题样式内容 -->
      <action name="getThemeCSS" method="getThemeCSS" />
      <!-- 获取自定义控件版本号 -->
      <action name="getCustomCtlVersion" method="getCustomCtlVersion" />
   </package>
    <!--轻量卡片-->
    <package name="card" class="kd.bos.web.actions.CardAction">
       <action name="getCardInfo" method="getCardInfo" />
       <action name="batchInvokeAction" method="batchInvokeAction" />
    </package>
   ......
</root>

所以做个总结,以后排查源码时,大部分接口,如/ierp/x1/x2,都可以直接定位到x1Action类的x2方法


赞 4