- Play Framework介绍1--主要概念
- Play Framework介绍2—Helloworld
Server启动过程主要涉及三个地方:
- play.Play类:代表Play本身业务模型。
- play.server.Server类:负责服务器启动。
- play.classloading包:负责.java文件读取、编译和加载。
![Play代码分析启动源代码,Play源代码分析1—Server启动过程](/Files/20114/9bd4c5c0-143f-4740-bfeb-d68c1f48fc48.jpg)
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."); } }
做两件事:
- Play.init
- 然后创建Server对象。
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
主要做:
- 加载配置
- new ApplicationClasses();加载app、views和conf路径到VirtualFile中,VirtualFile是Play内部的统一文件访问接口,方便后续读取文件
- 加载route
- 加载Module,Play的应用扩展组件。
- 加载Plugin,Play框架自身的扩展组件。
- 工作在产品模式则启动Play.
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流程
![过程Play代码分析启动源代码,Play源代码分析1—Server启动过程](/Files/20114/07ddb46c-f20c-4f73-8720-07f9b9ec54cc.jpg)
简化代码如下:
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
主要步骤:
- plugin.compileAll,给所有plugin一次机会进行自定义编译。
- Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));编译所有.java文件。编译后的.class存储在ApplicationClass中。内部使用了eclipse的JDT编译器。
- loadApplicationClass,取出ApplicationClass中的.class加入List
中返回。
![Play代码分析过程Play代码分析启动源代码,Play源代码分析1—Server启动过程](/Files/20114/9399b279-3a61-4a76-89a9-031e92b5dffb.jpg)
接着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 _disibledevent=>", 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 _disibledevent=>", httpPort); } else { Logger.info("Listening for HTTP at %2$s:%1$s ...", httpPort, address); } } } } catch (ChannelException e) { Logger.error("Could not bind _disibledevent=>" + httpPort, e); System.exit(-1); } ... }
主要步骤:
- 设置端口,地址
- new ServerBootstrap,创建jboss netty服务器。Play1.1.1使用了netty作为底层通讯服务器。
- new HttpServerPipelineFactory(),设置netty所需的请求处理管道工厂。它负责当请求到达时提供处理者。
- bootstrap.bind(new InetSocketAddress(address, httpPort),绑定地址,端口。
到此万事具备,只等东风了…
最新评论