您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何用Play源代碼分析Server啟動過程,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Play是個Rails風格的Java Web框架。
如何調試請看此處。以下進入正題^_^
Server啟動過程主要涉及三個地方:
play.Play類:代表Play本身業務模型。
play.server.Server類:負責服務器啟動。
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."); } }
做兩件事:
Play.init
然后創建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<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"); … // Load modules loadModules(); … // Enable a first classloader classloader = new ApplicationClassloader(); // Plugins loadPlugins(); // Done ! if (mode == Mode.PROD ||preCompile() ) { start(); } … }
主要做:
加載配置
new ApplicationClasses();加載app、views和conf路徑到VirtualFile中,VirtualFile是Play內部的統一文件訪問接口,方便后續讀取文件
加載route
加載Module,Play的應用擴展組件。
加載Plugin,Play框架自身的擴展組件。
工作在產品模式則啟動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<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; }
主要步驟:
plugin.compileAll,給所有plugin一次機會進行自定義編譯。
Play.classes.compiler.compile(classNames.toArray(new String[classNames.size()]));編譯所有.java文件。編譯后的.class存儲在ApplicationClass中。內部使用了eclipse的JDT編譯器。
loadApplicationClass,取出ApplicationClass中的.class加入List<Class>中返回。
到此完成.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); } ... }
主要步驟:
設置端口,地址
new ServerBootstrap,創建jboss netty服務器。Play1.1.1使用了netty作為底層通訊服務器。
new HttpServerPipelineFactory(),設置netty所需的請求處理管道工廠。它負責當請求到達時提供處理者。
bootstrap.bind(new InetSocketAddress(address, httpPort),綁定地址,端口。
上述就是小編為大家分享的如何用Play源代碼分析Server啟動過程了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。