在上一节中我们剖析了Play framework的启动原理,很容易就能发现Play framework的启动主入口在play.server.Server中,在本节,我们来一起看看Server类中主要发生了什么。
Server类 既然是程序运行的主入口,那么必然是由main方法进入的,Server类中的main方法十分简单。源码如下:
public static void main(String[] args) throws Exception { File root = new File(System.getProperty("application.path")); //获取参数中的precompiled if (System.getProperty("precompiled", "false").equals("true")) { Play.usePrecompiled = true; } //获取参数中的writepid if (System.getProperty("writepid", "false").equals("true")) { //这个方法的作用是检查当前目录下是否存在server.pid文件,若存在表明当前已有程序在运行 writePID(root); } //Play类的初始化 Play.init(root, System.getProperty("play.id", "")); if (System.getProperty("precompile") == null) { //Server类初始化 new Server(args); } else { Logger.info("Done."); } } main方法执行的操作很简单:
获取程序路径 检查是否存在precompiled参数 检查是否存在writepid参数,若存在则检查是否存在server.pid文件,若存在则表明已有程序在运行,不存在则将当前程序pid写入server.pid play类初始化 检查是否存在precompile参数项,若存在表示是个预编译行为,结束运行,若没有则启动服务 这其中最重要的便是Play类的初始化以及Server类的初始化 这里我们先来看Server类的初始化过程,现在可以先简单的将Play类的初始化理解为Play框架中一些常量的初始化以及日志、配置文件、路由信息等配置的读取。 这里贴一下Server类的初始化过程:
public Server(String[] args) { //设置文件编码为UTF-8 System.setProperty("file.encoding", "utf-8"); //p为Play类初始化过程中读取的配置文件信息 final Properties p = Play.configuration; //获取参数中的http与https端口信息,若不存在则用配置文件中的http与https端口信息 httpPort = Integer.parseInt(getOpt(args, "http.port", p.getProperty("http.port", "-1"))); httpsPort = Integer.parseInt(getOpt(args, "https.port", p.getProperty("https.port", "-1"))); //若没有配置则设置默认端口为9000 if (httpPort == -1 && httpsPort == -1) { httpPort = 9000; } //http与https端口不能相同 if (httpPort == httpsPort) { Logger.error("Could not bind on https and http on the same port " + httpPort); Play.fatalServerErrorOccurred(); } InetAddress address = null; InetAddress secureAddress = null; try { //获取配置文件中的默认http地址,若不存在则在系统参数中查找 //之前还是参数配置大于配置文件,这里不知道为什么又变成了配置文件的优先级高于参数配置,很迷 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"); Play.fatalServerErrorOccurred(); } try { //同上,获取https地址 if (p.getProperty("https.address") != null) { secureAddress = InetAddress.getByName(p.getProperty("https.address")); } else if (System.getProperties().containsKey("https.address")) { secureAddress = InetAddress.getByName(System.getProperty("https.address")); } } catch (Exception e) { Logger.error(e, "Could not understand https.address"); Play.fatalServerErrorOccurred(); } //netty服务器启动类初始化,使用nio服务器,无限制线程池 //这里的线程池是netty的主线程池与工作线程池,是处理连接的线程池,而Play实际执行业务操作的线程池在另一个地方配置 ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool()) ); try { //初始化http端口 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); Play.fatalServerErrorOccurred(); } //下面是https端口服务器的启动过程,和http一致 bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool()) ); try { if (httpsPort != -1) { //这里的管道工厂类变成了SslHttpServerPipelineFactory bootstrap.setPipelineFactory(new SslHttpServerPipelineFactory()); bootstrap.bind(new InetSocketAddress(secureAddress, httpsPort)); bootstrap.setOption("child.tcpNoDelay", true); if (Play.mode == Mode.DEV) { if (secureAddress == null) { Logger.info("Listening for HTTPS on port %s (Waiting a first request to start) ...", httpsPort); } else { Logger.info("Listening for HTTPS at %2$s:%1$s (Waiting a first request to start) ...", httpsPort, secureAddress); } } else { if (secureAddress == null) { Logger.info("Listening for HTTPS on port %s ...", httpsPort); } else { Logger.info("Listening for HTTPS at %2$s:%1$s ...", httpsPort, secureAddress); } } } } catch (ChannelException e) { Logger.error("Could not bind on port " + httpsPort, e); Play.fatalServerErrorOccurred(); } if (Play.mode == Mode.DEV) { // print this line to STDOUT - not using logger, so auto test runner will not block if logger is misconfigured (see #1222) //输出启动成功,以便进行自动化测试 System.out.println("~ Server is up and running"); } } server类的初始化没什么好说的,重点就在于那2个管道工厂类,HttpServerPipelineFactory与SslHttpServerPipelineFactory
...