转载自:http://blog.csdn.net/songhuiqiao/article/details/50311781
通过此介绍:http://www.oschina.net/p/cat-dianping
得知,运行cat-home项目里的‘com.dianping.cat.TestServer’可以启动CAT服务。so,我们就通过这个类来分析一下cat服务端的启动流程。
准备工作:需要把cat的代码导入IDE(我这里用的是eclipse),下载依赖(吐槽一下,太慢了),编译成功;
好,接着我们断点TestServer类跟踪整个启动流程。
----------------------------------------------分割线------------------------------------------------------
直接上TsetServer代码:
- /**
- * Junit4最大的改进是大量使用注解(元数据),很多实际执行过程都在Junit的后台做完了,
- * 而且写test case 的类不需要继承TestCase,只需要在所要做test case的方法前加@Test 注解即可。
- * @author admin
- */
- @RunWith(JUnit4.class)//测试运行于JUnit4测试环境
- public class TestServer extends JettyServer {
- public static void main(String[] args) throws Exception {
- TestServer server = new TestServer();
- System.setProperty("devMode", "true");//设置指定键对值的系统属性:开发模式(true)
- server.startServer();//开启一个jetty服务
- server.startWebApp();//在默认浏览器中打开一个页面
- server.stopServer();//停止一个jetty服务
- }
- /**
- * @Before, @After注解过的方法将在测试方法之前/之后执行。
- * @throws Exception
- */
- @Before
- public void before() throws Exception {
- System.setProperty("devMode", "true");
- super.startServer();
- }
- @Override
- protected String getContextPath() {
- return "/cat";
- }
- /**
- * 得到服务器端口
- */
- @Override
- protected int getServerPort() {
- return 2281;
- }
- @Override
- protected void postConfigure(WebAppContext context) {
- context.addFilter(GzipFilter.class, "/*", Handler.ALL);
- }
- @Test
- public void startWebApp() throws Exception {
- // open the page in the default browser
- // 在系统默认浏览器中打开一个页面,这个地方要注意一下,因为调用的是默认浏览器,而cat的对于别的浏览器页面不兼容,建议把默认浏览器设置为chrome
- display("/cat/r");
- waitForAnyKey();
- }
- }
从上面的代码可以看出重点在main方法,从中基本可以看出整个服务器的启动过程如下:
1,因为cat项目在开发模式下集成了jetty,所以第一步就是启动一个jetty服务(server.startServer();//开启一个jetty服务);
2,调用本地操作系统的默认浏览器打开一个页面(server.startWebApp();//在默认浏览器中打开一个页面);
3,停止服务(server.stopServer();//停止一个jetty服务);
所以,cat服务的启动过程可以通过以上三个过程步骤进行分析,接下来我们看看cat在startServer的时候都干了一些什么,而startServer整个过程又分为如下步骤:
JettyServer类
此类的代码点评封装在了一个jar里面(test-framework-2.2.0.jar)
1,安装Plexus IOC容器setupContainer()(没有接触过plexus ioc的请查看:http://blog.csdn.net/songhuiqiao/article/details/49908165)
Plexus提供完整的软件栈,用于创建和执行软件项目。基于Plexus容器,应用程序可以利用面向组件的编程方式来构建模块化,容易集成和重复使用的可复用组件。
虽然Plexus是一个类似控制反转(IoC)或依赖注入(DI)框架的框架 ,事实上它更是一个支持如下许多功能的全面的容器:
● 组件生命周期(Componentlifecycles)
● 组件实例化战略(Componentinstantiation strategies)
● 嵌套容器(Nestedcontainers)
● 组件配置(Componentconfiguration)
● 自动布线(Auto-wiring)
● 组件依赖关系以及各种依赖注入技术,包括构造函数注入,setter注入和private注入。(Componentdependencies,
and Various dependency injection techniques including constructorinjection, setter injection, and private field injection.)
setupContainer()
protected void setupContainer() throwsException {
PlexusContainercontainer = ContainerLoader.getDefaultContainer();
DefaultContextcontext = new DefaultContext();
context.put("plexus", container);
contextualize(context);
}
//得到默认的Plexus容器:首先创建一个默认的容器配置对象,设置容器的配置文件为:/META-INF/plexus/plexus.xml,然后根据配置对象得到默认容器返回;
public static PlexusContainergetDefaultContainer() {
DefaultContainerConfiguration configuration = newDefaultContainerConfiguration();
configuration.setContainerConfiguration("/META-INF/plexus/plexus.xml");///D:/workspace/cat/trunk/cat-client/target/classes/META-INF/plexus/plexus.xml
returngetDefaultContainer(configuration);//得到默认ioc容器
}
public static PlexusContainergetDefaultContainer(ContainerConfiguration configuration) {
if (s_container ==null) {//判断容器是否为空
。。。。。。
preConstruction(configuration);//前置构造
s_container =new DefaultPlexusContainer(configuration);//创建默认Plexus容器
postConstruction(s_container);//后置构造
。。。。。。
}
@SuppressWarnings("unchecked")
private static voidpreConstruction(ContainerConfiguration configuration) throws Exception {
LifecycleHandlerplexus =configuration.getLifecycleHandlerManager().getLifecycleHandler("plexus");//得到Plexus生命周期handler
Field field =Reflects.forField().getDeclaredField(AbstractLifecycleHandler.class,"beginSegment");
field.setAccessible(true);
List<Phase>segment = (List<Phase>) field.get(plexus);
segment.add(0, neworg.unidal.lookup.extension.PostConstructionPhase());
try {
newContainerConfigurationDecorator().process(configuration);
} catch (Exception e){
e.printStackTrace();
}
}
在前置构造中调用getLifecycleHandlerManager()创建Plexus,Basic,Plexusconfigurable,passive,Bootstrap等生命周期handler;已Plexus为例,对应的BeginSegment和EndSegment分别如下:
DefaultContainerConfiguration.getLifecycleHandlerManager()
List<Phase> segment =(List<Phase>) field.get(plexus);下面是List中的BeginSegment的4个阶段
[org.codehaus.plexus.personality.plexus.lifecycle.phase.LogEnablePhase@6f866002,
org.codehaus.plexus.personality.plexus.lifecycle.phase.ContextualizePhase@5f095c81,
org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializePhase@5f9849e5,
org.codehaus.plexus.personality.plexus.lifecycle.phase.StartPhase@71b8a6b]
然后接着把PostConstructionPhase阶段增加到List中的第0位;
segment list变成如下:
[org.unidal.lookup.extension.PostConstructionPhase@3ee3f8b9, org.codehaus.plexus.personality.plexus.lifecycle.phase.LogEnablePhase@6f866002, org.codehaus.plexus.personality.plexus.lifecycle.phase.ContextualizePhase@5f095c81, org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializePhase@5f9849e5, org.codehaus.plexus.personality.plexus.lifecycle.phase.StartPhase@71b8a6b]
接着通过容器配置Decorator(new ContainerConfigurationDecorator())进行处理,在process方法中获取容器配置/META-INF/plexus/plexus.xml,然后在生成一个临时文件:C:\Users\admin\AppData\Local\Temp\plexus-9216030128668808777.xml,最后把这个临时文件configuration.setContainerConfigurationURL(tmp.toURI().toURL());
接着s_container = new DefaultPlexusContainer(configuration);
进行改造,然后初始化(Plexus生命周期管理,其中发现组件discoverComponents方法执行比较慢),开始:
紧接着进行后置改造:
主要是注册一下组件的管理工厂类;
最终得到的PlexusContainer对象container的结构如下:
到此,安装PlexusIOC容器已经完毕。
2,根据服务器端口创建一个jetty服务,打开socket连接,创建服务对象;
3,创建一个WebAppContext对象,具体参考:http://blog.csdn.net/kobejayandy/article/details/20165937
4,安装应用;
@SuppressWarnings("unchecked")
protected voidconfigure(WebAppContext context) {
File warRoot = getWarRoot();//获取war包路径:src\main\webapp
context.getInitParams().put("org.mortbay.jetty.servlet.Default.dirAllowed","false");
context.setContextPath(getContextPath());///cat
context.setDescriptor(new File(warRoot, "WEB-INF/web.xml").getPath());//设置描述符位置 src\main\webapp\WEB-INF\web.xml
context.setResourceBase(warRoot.getPath()); //
}
5,添加到处理器server.start();
6,启动jetty服务;
当jetty容器其中的时候,会去读取src\main\webapp\WEB-INF\web.xml这个文件,下面接着分析一下web.xml这个文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>cat-filter</filter-name>
<filter-class>com.dianping.cat.servlet.CatFilter</filter-class>
</filter>
<filter>
<filter-name>domain-filter</filter-name>
<filter-class>com.dianping.cat.report.view.DomainFilter</filter-class>
</filter>
<servlet>
<servlet-name>cat-servlet</servlet-name>
<servlet-class>com.dianping.cat.servlet.CatServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>mvc-servlet</servlet-name>
<servlet-class>org.unidal.web.MVC</servlet-class>
<init-param>
<param-name>cat-client-xml</param-name>
<param-value>client.xml</param-value>
</init-param>
<init-param>
<param-name>init-modules</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<filter-mapping>
<filter-name>cat-filter</filter-name>
<url-pattern>/r/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>domain-filter</filter-name>
<url-pattern>/r/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>cat-filter</filter-name>
<url-pattern>/s/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>cat-filter</filter-name>
<url-pattern>/jsp/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<servlet-mapping>
<servlet-name>mvc-servlet</servlet-name>
<url-pattern>/r/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>mvc-servlet</servlet-name>
<url-pattern>/s/*</url-pattern>
</servlet-mapping>
<jsp-config>
<taglib>
<taglib-uri>/WEB-INF/app.tld</taglib-uri>
<taglib-location>/WEB-INF/app.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
在web.xml文件中会先执行cat-servlet中的initComponents方法,然后再执行mvc-servlet的初始化方法,还会加载cat-filter,domain-filter等过滤器。
先看cat-servlet的initComponents方法:
/**
*初始化组件
*/
@Override
protected voidinitComponents(ServletConfig servletConfig) throws ServletException {
try {
ModuleContext ctx = new DefaultModuleContext(getContainer());
ModuleInitializer initializer = ctx.lookup(ModuleInitializer.class);
FileclientXmlFile = getConfigFile(servletConfig, "cat-client-xml","client.xml");
FileserverXmlFile = getConfigFile(servletConfig, "cat-server-xml","server.xml");
ctx.setAttribute("cat-client-config-file", clientXmlFile);//设置cat客户端配置文件:\data\appdatas\cat\client.xml
ctx.setAttribute("cat-server-config-file", serverXmlFile);//设置cat服务端配置文件:\data\appdatas\cat\server.xml
initializer.execute(ctx); //模块初始化容器执行
} catch (Exceptione) {
m_exception= e;
System.err.println(e);
throw newServletException(e);
}
}
@Override
public voidexecute(ModuleContext ctx) {
Module[] modules = m_manager.getTopLevelModules(); //得到顶级模块CatHomeModule
execute(ctx, modules); //安装模块
}
@Override
public voidexecute(ModuleContext ctx, Module... modules) {
Set<Module> all= new LinkedHashSet<Module>();
info(ctx,"Initializing top level modules:");
for (Module module :modules) {
info(ctx, " " + module.getClass().getName());
}
try {
expandAll(ctx, modules, all); //全部展开所有模块
for(Module module : all) {
if (!module.isInitialized()) {
executeModule(ctx, module, m_index++);
}
}
} catch (Exception e){
thrownew RuntimeException("Error when initializing modules! Exception: " +e, e);
}
}
private synchronized voidexecuteModule(ModuleContext ctx, Module module, int index) throws Exception {
long start =System.currentTimeMillis();
// set flat to avoidre-entrance
module.setInitialized(true);
info(ctx, index +" ------ " + module.getClass().getName());
// execute itselfafter its dependencies
module.initialize(ctx);
long end =System.currentTimeMillis();
info(ctx, index +" ------ " + module.getClass().getName() + " DONE in " +(end - start) + " ms.");
}
7,后置安装;