Play是个Rails风格的Java Web框架,需要了解背景请看:
如何调试请看此处。以下进入正题^_^
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.");
- }
- }
做两件事:
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();
- }
- …
- }
主要做:
关键步骤为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;
- }
主要步骤:
到此完成.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);
- }
- ...
- }
主要步骤:
到此万事具备,只等东风了…
网站名称:Play源代码分析:Server启动过程
分享URL:http://www.mswzjz.cn/qtweb/news33/426733.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能