千家信息网

如何用Play源代码分析Server启动过程

发表于:2025-11-21 作者:千家信息网编辑
千家信息网最后更新 2025年11月21日,这期内容当中小编将会给大家带来有关如何用Play源代码分析Server启动过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Play是个Rails风格的Java W
千家信息网最后更新 2025年11月21日如何用Play源代码分析Server启动过程

这期内容当中小编将会给大家带来有关如何用Play源代码分析Server启动过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

Play是个Rails风格的Java Web框架。

如何调试请看此处。以下进入正题^_^

Server启动过程主要涉及三个地方:

  1. play.Play类:代表Play本身业务模型。

  2. play.server.Server类:负责服务器启动。

  3. play.classloading包:负责.java文件读取、编译和加载。

总体流程:

Server.main为入口方法:

public static void main(String[] args) throws Exception {          …          Play.init(root, System.getProperty("play.id", ""));          if (System.getProperty("precompile") == null) {              new Server();          } else {              Logger.info("Done.");          }      }

做两件事:

  1. Play.init

  2. 然后创建Server对象。

Play.init

public static void init(File root, String id) {   …   readConfiguration();            Play.classes = new ApplicationClasses();           …           // Build basic java source path          VirtualFile appRoot = VirtualFile.open(applicationPath);          roots.add(appRoot);          javaPath = new ArrayList(2);          javaPath.add(appRoot.child("app"));          javaPath.add(appRoot.child("conf"));           // Build basic templates path          templatesPath = new ArrayList(2);          templatesPath.add(appRoot.child("app/views"));           // Main route file          routes = appRoot.child("conf/routes");           …           // Load modules          loadModules();           …           // Enable a first classloader          classloader = new ApplicationClassloader();           // Plugins          loadPlugins();           // Done !          if (mode == Mode.PROD ||preCompile() ) {                  start();              }           …      }

主要做:

  1. 加载配置

  2. new ApplicationClasses();加载app、views和conf路径到VirtualFile中,VirtualFile是Play内部的统一文件访问接口,方便后续读取文件

  3. 加载route

  4. 加载Module,Play的应用扩展组件。

  5. 加载Plugin,Play框架自身的扩展组件。

  6. 工作在产品模式则启动Play.

关键步骤为new ApplicationClasses(),执行computeCodeHashe(),后者触发目录扫描,搜索.java文件。相关过程简化代码如下:

public ApplicationClassloader() {          super(ApplicationClassloader.class.getClassLoader());          // Clean the existing classes          for (ApplicationClass applicationClass : Play.classes.all()) {              applicationClass.uncompile();          }          pathHash = computePathHash();         …      }
int computePathHash() {          StringBuffer buf = new StringBuffer();          for (VirtualFile virtualFile : Play.javaPath) {              scan(buf, virtualFile);          }          return buf.toString().hashCode();      }
void scan(StringBuffer buf, VirtualFile current) {          if (!current.isDirectory()) {              if (current.getName().endsWith(".java")) {                  Matcher matcher = Pattern.compile("\\s+class\\s([a-zA-Z0-9_]+)\\s+").matcher(current.contentAsString());                  buf.append(current.getName());                  buf.append("(");                  while (matcher.find()) {                      buf.append(matcher.group(1));                      buf.append(",");                  }                  buf.append(")");              }          } else if (!current.getName().startsWith(".")) {              for (VirtualFile virtualFile : current.list()) {                  scan(buf, virtualFile);              }          }      }

Start流程

简化代码如下:

public static synchronized void start() {          try {                          ...              // Reload configuration              readConfiguration();                           ...                            // Try to load all classes              Play.classloader.getAllClasses();               // Routes              Router.detectChanges(ctxPath);               // Cache              Cache.init();               // Plugins              for (PlayPlugin plugin : plugins) {                  try {                      plugin.onApplicationStart();                  } catch(Exception e) {                      if(Play.mode.isProd()) {                          Logger.error(e, "Can't start in PROD mode with errors");                      }                      if(e instanceof RuntimeException) {                          throw (RuntimeException)e;                      }                      throw new UnexpectedException(e);                  }              }               ...               // Plugins              for (PlayPlugin plugin : plugins) {                  plugin.afterApplicationStart();              }           } catch (PlayException e) {              started = false;              throw e;          } catch (Exception e) {              started = false;              throw new UnexpectedException(e);          }      }

关键步骤为执行Play.classloader.getAllClasses()加载app目录中的类型。简化代码如下:

public List getAllClasses() {          if (allClasses == null) {              allClasses = new ArrayList();               if (Play.usePrecompiled) {                  ...              } else {                  List all = new ArrayList();                   // Let's plugins play                  for (PlayPlugin plugin : Play.plugins) {                      plugin.compileAll(all);                  }                   for (VirtualFile virtualFile : Play.javaPath) {                      all.addAll(getAllClasses(virtualFile));                  }                  List classNames = new ArrayList();                  for (int i = 0; i < all.size(); i++) {                      if (all.get(i) != null && !all.get(i).compiled) {                          classNames.add(all.get(i).name);                      }                  }                   Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));                   for (ApplicationClass applicationClass : Play.classes.all()) {                      Class clazz = loadApplicationClass(applicationClass.name);                      if (clazz != null) {                          allClasses.add(clazz);                      }                  }                                  ...              }          }          return allClasses;      }

主要步骤:

  1. plugin.compileAll,给所有plugin一次机会进行自定义编译。

  2. Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));编译所有.java文件。编译后的.class存储在ApplicationClass中。内部使用了eclipse的JDT编译器。

  3. loadApplicationClass,取出ApplicationClass中的.class加入List中返回。

到此完成.java的加载。相关对象关系如下图:

接着new Server()启动HTTP服务,监听请求

简化代码如下:

public Server() {               ...          if (httpPort == -1 && httpsPort == -1) {              httpPort = 9000;          }          ...          InetAddress address = null;          try {              if (p.getProperty("http.address") != null) {                  address = InetAddress.getByName(p.getProperty("http.address"));              } else if (System.getProperties().containsKey("http.address")) {                  address = InetAddress.getByName(System.getProperty("http.address"));              }           } catch (Exception e) {              Logger.error(e, "Could not understand http.address");              System.exit(-1);          }                    ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(                  Executors.newCachedThreadPool(), Executors.newCachedThreadPool())          );          try {              if (httpPort != -1) {                  bootstrap.setPipelineFactory(new HttpServerPipelineFactory());                  bootstrap.bind(new InetSocketAddress(address, httpPort));                  bootstrap.setOption("child.tcpNoDelay", true);                   if (Play.mode == Mode.DEV) {                      if (address == null) {                          Logger.info("Listening for HTTP on port %s (Waiting a first request to start) ...", httpPort);                      } else {                          Logger.info("Listening for HTTP at %2$s:%1$s (Waiting a first request to start) ...", httpPort, address);                      }                  } else {                      if (address == null) {                          Logger.info("Listening for HTTP on port %s ...", httpPort);                      } else {                          Logger.info("Listening for HTTP at %2$s:%1$s  ...", httpPort, address);                      }                  }               }           } catch (ChannelException e) {              Logger.error("Could not bind on port " + httpPort, e);              System.exit(-1);          }          ...      }

主要步骤:

  1. 设置端口,地址

  2. new ServerBootstrap,创建jboss netty服务器。Play1.1.1使用了netty作为底层通讯服务器。

  3. new HttpServerPipelineFactory(),设置netty所需的请求处理管道工厂。它负责当请求到达时提供处理者。

  4. bootstrap.bind(new InetSocketAddress(address, httpPort),绑定地址,端口。

上述就是小编为大家分享的如何用Play源代码分析Server启动过程了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

文件 编译 过程 分析 代码 步骤 服务 服务器 源代码 关键 内容 地址 对象 框架 流程 目录 端口 组件 处理 三个 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 新基建的网络安全迎来高速增长 asp判断服务器文件 网络配置与网络安全 中国专利全文数据库包括哪三个字 数据库系统安全技术 debian服务器可视化 excel 数据库搜索 网络安全大专怎么找工作 曙光服务器做系统按哪个键 无锡hpe机架式服务器厂家 淘小二网络技术有限公司绵竹 卫星网络安全专家 网络安全保卫局政委孙劲峰 盘点两会十二个网络安全热点 阿里云服务器服务哪些国家 2k21主机连不上服务器 做服务器的公司属于什么行业 软件开发环境相关证明怎么写 免费的人脸数据库 对整个服务器系统进行备份 软件开发所有文件模板 c 链接数据库条件查询 网络技术与信息工程 三方r软件开发合作合同 搞软件开发的都很忙吗 我认识的软件开发专业 在软件开发过程中采用结构化 云南常用的外贸软件开发公司 计算机网络安全3.0 大数据网络安全可视化方案
0