我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

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

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

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

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

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

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

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

总体流程:

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

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) {   &hellip;   readConfiguration();            Play.classes = new ApplicationClasses();           &hellip;           // Build basic java source path          VirtualFile appRoot = VirtualFile.open(applicationPath);          roots.add(appRoot);          javaPath = new ArrayList<VirtualFile>(2);          javaPath.add(appRoot.child("app"));          javaPath.add(appRoot.child("conf"));           // Build basic templates path          templatesPath = new ArrayList<VirtualFile>(2);          templatesPath.add(appRoot.child("app/views"));           // Main route file          routes = appRoot.child("conf/routes");           &hellip;           // Load modules          loadModules();           &hellip;           // Enable a first classloader          classloader = new ApplicationClassloader();           // Plugins          loadPlugins();           // Done !          if (mode == Mode.PROD ||preCompile() ) {                  start();              }           &hellip;      }

主要做:

  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();         &hellip;      }
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流程

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

简化代码如下:

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<Class> getAllClasses() {          if (allClasses == null) {              allClasses = new ArrayList<Class>();               if (Play.usePrecompiled) {                  ...              } else {                  List<ApplicationClass> all = new ArrayList<ApplicationClass>();                   // Let's plugins play                  for (PlayPlugin plugin : Play.plugins) {                      plugin.compileAll(all);                  }                   for (VirtualFile virtualFile : Play.javaPath) {                      all.addAll(getAllClasses(virtualFile));                  }                  List<String> classNames = new ArrayList<String>();                  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<Class>中返回。

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

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

接着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启动过程了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

这期内容当中小编将会给大家带来有关如何用Play源代码分析Server启动过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Play是个Rails风格的Java Web框架。如何调试请看此处。以下进入正题
2023-06-17

Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

在前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机制中的Server角色是如何
2022-06-06

[图解]Android源码分析——Activity的启动过程

Activity的启动过程一.Launcher进程请求AMSLauncher.java的startActivitySafely方法的执行过程:Activity.java中startActivity方法的执行过程:startActivityF
2022-06-06

如何用源代码分析FileZilla

这期内容当中小编将会给大家带来有关如何用源代码分析FileZilla,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,具有多种特色
2023-06-16

怎么用java源代码分析jvm.dll装载过程

怎么用java源代码分析jvm.dll装载过程,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。简述众所周知java.exe是java class文件的执行程序,但
2023-06-03

如何用源码分析Struts2请求处理及过程

这期内容当中小编将会给大家带来有关如何用源码分析Struts2请求处理及过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.1 Struts2请求处理1. 一个请求在Struts2框架中的处理步骤:a)
2023-06-17

如何用JVM源码分析Java对象的创建过程

这篇文章将为大家详细讲解有关如何用JVM源码分析Java对象的创建过程,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。基于HotSpot实现对Java对象的创建过程进行深入分析。定义两个简单的
2023-06-17

Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

在上一篇文章中,我们分析了Android系统进程间通信机制Binder中的Server在启动过程使用Service Manager的addService接口把自己添加到Service Manager守护过程中接受管理。在这一篇文章
2022-06-06

C#中如何使用远程调试和性能分析工具优化代码性能及解决方法

C#中如何使用远程调试和性能分析工具优化代码性能及解决方法引言:在软件开发过程中,优化代码的性能是非常重要的一项任务。通过代码优化,可以使程序运行更加高效,提高用户体验,并减少资源消耗。在C#中,我们可以利用远程调试和性能分析工具来帮助我们
2023-10-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录