一、启动入口
kd.bos.service.bootstrap.Booter#main
重点看这几行:先获取启动类,然后start
看到这说明苍穹还内置了springboot,不过一般都是以下面的jetty方式启动
EmbedJettyServer的start方法主要就是在组装jetty相关类,设置相关属性,比如:
context.setContextPath设置访问根路径
context.setDescriptor设置web.xml路径,可类比tomcat项目的web.xml
context.setResourceBase设置前端资源路径
这里添加了一个处理器集合,里面有四个处理器,但是没看到这些处理器addServlet(web项目处理请求一般都是在servlet里的,比如spring的DispatchServlet),然后启动(jetty的源码咱就不看了......)
二、请求分发
这样看下来好像还是不知道苍穹是在哪处理请求的?
还记得上面设置过的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方法会构造一个过滤器链来处理
这个build方法会先从servletList找到匹配uri的servlet去构建FilterChain返回(后续会调用其service方法),再从filterList找到匹配uri的filter去构建FilterChain返回(后续会调用其doFilter方法),打断点可以看到servletList为空,走下面filter那块的逻辑了
filterList里有很多filter,比如设置traceId的TraceFilter,和登录相关的LoginFilter,我们找个业务单据的代码打个断点,能发现调用栈里是走的ActionFilter。
它会先根据路径判断有没有这个请求的配置,再去获取Invoker(封装了处理类对象和method,方便反射调用)
这个寻找逻辑也还比较简单,利用了两层map和一个实例对象map,拿请求/ierp/form/batchInvokeAction.do来说,先根据form拿到它下面的action配置,再根据batchInvokeAction找到对应的action配置,再获取实例对象,最后组装Invoker
我们回过头看看这几个map的初始化过程,可以发现是在ActionConfig类的静态方法里,会解析/action/actionconfig.xml配置文件(也是在bos-resources-5.0.jar里)
配置内容如下,后续解析文件的代码咱就不看了,其实就是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方法
推荐阅读
您的鼓励与嘉奖将成为创作者们前进的动力,如果觉得本文还不错,可以给予作者创作打赏哦!
请选择打赏金币数 *