文档章节

How To Do It - HelloWorld 项目总结报告

罗格林
 罗格林
发布于 2018/07/29 19:11
字数 3465
阅读 1391
收藏 11

1. 介绍

HelloWorld 是一个HowToDoIt 组织的第一个项目, 一个简单的 MVC 展示应用. 实现项目需要响应发送到 GET / 端点的请求并显示一个主页

  1. 显示 Hello World - 其中 World 可以被 who 查询参数的值替代
  2. [可选] 显示应用版本
  3. [可选] 显示框架版本
  4. [可选] 使用模板引擎生成主页

2. 实现

下面是所有参与项目的链接:

3. 源码一览

3.1 ActFramework

public class AppEntry {

    /**
     * The home (`/`) endpoint.
     *
     * @param who
     *      request query parameter to specify the hello target.
     *      default value is `World`.
     */
    @GetAction
    public void home(@DefaultValue("World") @Output String who) {}

    public static void main(String[] args) throws Exception {
        Act.start();
    }

}

ActFramework 的实现相当简单, 使用了一个类来启动并响应请求.

ActFramework 使用 Rythm 模板引擎生成主页

<!DOCTYPE html>
<html lang="en">
@args String who
<head>
  <title>Hello World - ActFramework</title>
</head>
<body>
  <h1>Hello @who</h1>
  <div>@act.Act.appVersion().getVersion()</div>
  <p>
    Powered by ActFramework @act.Act.VERSION.getVersion()
  </p>
</body>
</html>

ActFramwork 项目实现了所有的需求, 包括可选项.

值得注意的是 ActFramework 使用了 osgl-version 来管理应用版本, 无需编码即可直接从项目的 pom.xml 定义中拿到版本号, 对应用开发来说非常便利: 直接使用 Act.appVersion() 即可拿到应用定义在 pom.xml 文件的版本号. 作为比较, 获得 ActFramework 框架版本号的方法是访问 Act.VERSION 常量.

ActFramework 实现提供的独特优势: 使用 yaml 文件实现了端到端自动化测试.

Scenario(Hello World):
  description: a web page says Hello
  interactions:
    - description: send request to the app without parameter
      request:
        method: GET
        url: /
      response:
        html:
          h1: Hello World
          head title:
            - contains: Hello World
            - contains: ActFramework
          p:
            <any>:
              - contains: Powered by ActFramework
    - description: send request to the app with [who = ActFramework]
      request:
        method: GET
        url: /?who=ActFramework
      response:
        html:
          h1: Hello ActFramework

3.2 Blade

public class Application {

    private static void index(RouteContext ctx) {

        String message = "Hello " + ctx.request().query("who", "World");

        ctx.attribute("message", message);
        ctx.attribute("appVersion", WebContext.blade().env("app.version", "default version"));
        ctx.attribute("bladeVersion", Const.VERSION);
        ctx.render("index.html");
    }

    public static void main(String[] args) {
        Blade.of()
                .get("/", Application::index)
                .start(Application.class, args);
    }

}

Blade 也使用了一个 Java 源码就实现了所有的特性, 代码非常优雅. Blade 使用了类似 velocity 的模板引擎生成主页:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>
<body>
    <h1>${message}</h1>
    Build By ${name} ${version}
</body>
</html>

和 ActFramework 的实现不同, Blade 没有从 pom.xml 文件中获取应用版本信息, 而是在额外的配置文件中定义应用版本.

3.3 JFinal

public class HellWorldController extends Controller {
	public void index(@Para(value="who", defaultValue="World")String who){
		setAttr("who",who).render("index.html");
	}
}
public class ProjectConfig extends JFinalConfig{

	public static void main(String[] args) {
        JFinal.start("src/main/webapp", 8000, "/");
    }
	
	public void configRoute(Routes me) {
		me.setBaseViewPath("/WEB-INF/views").add("/", HellWorldController.class);
	}
	
	public void configConstant(Constants me) {
	}

	public void configEngine(Engine me) {
	}

	public void configPlugin(Plugins me) {
	}

	public void configInterceptor(Interceptors me) {
	}

	public void configHandler(Handlers me) {
	}
}

JFinal 使用了两个 Java 文件, 其中的 Controller 类非常简洁. 不过额外的 ProjectConfig.java 类让整个项目看上去就稍稍繁复了一点点.

JFinal 使用自己的 Enjoy 模板引擎生成主页:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HelloWorld</title>
</head>
<body>
<h1>Hello,#(who ?? 'UNKNOWN')!!^_^</h1>
<form action="/">
        <fieldset>
            <legend>Say Hello To The World</legend>
            <div>who: <input type="text" name="who"/></div>
            <input type="submit" value="==问好==">
          </fieldset>
        </body>
</form>
<div style="text-align:center;color:red;padding:10px;">Power By JFinal#(com.jfinal.core.Const::JFINAL_VERSION)</div>
</html>

JFinal 提供了框架版本号,但没有提供应用版本号

3.4 Nutz

@IocBean
public class MainLauncher {
    
    @Inject
    protected PropertiesProxy conf;

    @At("/")
    @Ok("jst:/index.html")
    public NutMap index(String who) throws IOException {
        NutMap re = new NutMap();
        re.put("who", Strings.sBlank(who, "world"));
        re.put("self_version", conf.get("app.build.version", "未知"));
        re.put("nutz_version", Nutz.version());
        return re;
    }

    public static void main(String[] args) throws Exception {
        new NbApp().run();
    }

}

Nutz 的实现和 Blade/ActFramwork 一样都是一个文件解决问题. Nutz 也从 pom.xml 文件中获取项目版本号.

Nutz's 用来生成主页的模板代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello ${=obj.who}</title>
</head>
<body>
<h1>Hello ${=obj.who}!</h1>
<h3>Self Version: ${obj.self_version}</h3>
<h3>Nutz Version: ${obj.nutz_version}</h3>
</body>
</html>

3.5 Play

@Singleton
class HomeController @Inject()(cc: ControllerComponents, config: Configuration) extends AbstractController(cc) {
  val version = config.get[String]("version")

  def index(who: String) = Action {
    Ok(views.html.index(who, version, PlayVersion.current))
  }

}

和其他所有实现不一样, Play 使用的是 Scala. 代码看上去非常少, 不过对我这个 Javaer 来说还是稍微有点眼生的感觉. Play 的实现也包括了展现 app 和框架版本号, play 没有使用定义在 sbt 脚步中的版本, 而是从 app 配置文件中获取版本号, 稍稍重复了一点.

Play 用来生成页面的模板有两个文件:

@(title: String)(content: Html)

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>@title</title>
    </head>
    <body>
        @content
    </body>
</html>
@(who: String, version: String, playVersion: String)

@main("Hello World - Play"){
<h1>Hello @who</h1>
<div>@version</div>
<p>
 Powered by Play @playVersion
</p>
}

Play 实现提供了自动化测试方案:

class UnitSpec extends PlaySpec with Results {

  "HomeController" should {

    "return a valid result with action" in {
      val config = Configuration(ConfigFactory.load("application"))
      val controller = new HomeController(stubControllerComponents(), config)
      val result = controller.index("world")(FakeRequest())
      status(result) mustEqual OK
      contentAsString(result) must include ("Hello World")
    }
  }
}
class FunctionalSpec extends PlaySpec with GuiceOneAppPerSuite {

  "HomeController" should {

    "render the index page" in {
      val home = route(app, FakeRequest(GET, "/?who=World")).get

      status(home) mustBe Status.OK
      contentType(home) mustBe Some("text/html")
      contentAsString(home) must include ("Hello World")
    }

  }
}
class BrowserSpec extends PlaySpec
  with OneBrowserPerTest
  with GuiceOneServerPerTest
  with HtmlUnitFactory
  with ServerProvider {

  "Application" should {

    "work from within a browser" in {

      go to ("http://localhost:" + port)

      pageSource must include ("Hello World")
    }
  }
}

3.6 Redkale

@RestService(name = " ")
public class HelloWorldService implements Service {

    private String appVersion = "1.0.0";

    @Override
    public void init(AnyValue conf) {
        String path = "/META-INF/maven/org.redkalex.htdi/htdi-hello/pom.properties";
        InputStream in = this.getClass().getResourceAsStream(path);
        if (in != null) {
            Properties props = new Properties();
            try {
                props.load(in);
                in.close();
            } catch (Exception e) {
            }
            this.appVersion = props.getProperty("version", "未知");
        }
    }

    @RestMapping(name = "index.html", auth = false)
    public HttpResult<String> index(String who) throws IOException {
        if (who == null || who.isEmpty()) who = "World";
        String body = "<!DOCTYPE html>\n"
            + "<html>\n"
            + "<head>\n"
            + "<meta charset=\"UTF-8\">\n"
            + "<title>Hello " + who + "</title>\n"
            + "</head>\n"
            + "<body>\n"
            + "<h1>Hello " + who + "!</h1>\n"
            + "<h3>Project Version: " + appVersion + "</h3>\n"
            + "<h3>Redkale Version: " + Redkale.getDotedVersion() + "</h3>\n"
            + "</body>\n"
            + "</html>";
        return new HttpResult<>("text/html;charset=utf8", body);
    }

}

Redkale 实现直接使用字串拼接输出而不是模板引擎. 对于 HelloWorld 这样的项目这样做完全没有问题. 不过稍微正式一点的项目这样会死人的吧. Redkale 的作者强调 Redkale 的应用更多偏向于数据服务, 同时也提到如果需要模板引擎应用可以很方便地集成三方产品.

Redkale 实现了展示应用和框架版本的需求.

3.7 SprintBoot

@SpringBootApplication
public class App extends SpringBootServletInitializer implements WebApplicationInitializer {

    public static void main( String[] args )
    {
        SpringApplication.run(App.class, args);
    }
}
@Controller
public class HelloController {
    @RequestMapping("/")
    public ModelAndView sayHello() {
        ModelAndView view = new ModelAndView("/sayHello.html");
        view.addObject("message", "Hello world"); 
        view.addObject("name", "Spring Boot");
        view.addObject("version", "2.x");
        return view;
    }
}

SpringBoot 的实现也很容易理解. 使用了 2 个源代码文件, 一个是启动入口, 另一个是响应处理器.

SprintBoot 使用 Beetl 引擎来生成主页:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
	<h1>${message}</h1>
	Build By ${name} ${version}
</body>
</html>

SprintBoot 硬编码了框架版本, 没有显示应用版本.

SprintBoot 实现提供了单元测试.

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class MvcTest{
    @Autowired
    private MockMvc mvc;
    @Test
    public void testHello() throws Exception {
      
        String viewName = mvc.perform(get("/")).andReturn().getModelAndView().getViewName();
        Assert.assertEquals("/sayHello.html", viewName);
    }
}

这个测试条件比较有趣, 检查是否使用了正确的 view 模板, 并没有针对真正的需求进行测试

3.8 TIO-MVC

@RequestPath(value = "/")
public class HelloController {
	@RequestPath(value = "")
	public HttpResponse index(HttpRequest request, String who) throws Exception {
		String name = StrUtil.isBlank(who) ? "World" : who;
		String html = "<title>Hello " + name + "</title><h1>Hello " + name + "</h1>";
		html += "<div>" + HowToDoTioStarter.APP_NAME + " " + P.p.getStr("app.version") + "</div>";
		html += "<div>Powered by <a href='https://t-io.org' target='_blank'>" + HttpConst.SERVER_INFO + " " + SysConst.TIO_CORE_VERSION + "</a></div>";
		return Resps.html(request, html);
	}
}
public class P {
	public static Props p = new Props("classpath:app.properties");
}
public class HowToDoTioStarter {
	public static final String APP_NAME = "how to do it : tio-mvc";
	
	public static HttpConfig httpConfig;

	public static HttpRequestHandler requestHandler;

	public static HttpServerStarter httpServerStarter;

	public static ServerGroupContext serverGroupContext;

	public static void init() throws Exception {
		httpConfig = new HttpConfig(P.p.getInt("http.port"), null, null, null);
//		httpConfig.setPageRoot(P.p.getStr("http.page"));//html/css/js等的根目录,支持classpath:,也支持绝对路径
//		httpConfig.setMaxLiveTimeOfStaticRes(P.p.getInt("http.maxLiveTimeOfStaticRes"));
//		httpConfig.setPage404(P.p.getStr("http.404"));
//		httpConfig.setPage500(P.p.getStr("http.500"));
		httpConfig.setUseSession(false);
		httpConfig.setCheckHost(false);

		String[] scanPackages = new String[] { HelloController.class.getPackage().getName() };//tio mvc需要扫描的根目录包,会递归子目录
		Routes routes = new Routes(scanPackages);
		requestHandler = new DefaultHttpRequestHandler(httpConfig, routes);

		httpServerStarter = new HttpServerStarter(httpConfig, requestHandler);
		serverGroupContext = httpServerStarter.getServerGroupContext();
		serverGroupContext.setUseQueueSend(true);
		httpServerStarter.start(); //启动http服务器
	}
	
	public static void main(String[] args) throws Exception {
		init();
	}
}

TIO-MVC 的实现代码行数和 JFinal 实现相近. 控制器本身的代码相对简单, 只是实现中有一些 boilerplate 代码, 和其他实现比较起来稍显臃肿.

和 Redkale 一样, TIO-MVC 的实现选择使用字串拼接来生成主页; 另外 TIO-MVC 的应用版本是硬编码在代码中的.

4. 启动

4.1 启动 ActFramework

ActFramework 提供了不同脚本来实现不同模式的启动:

4.1.1 开发模式

image

image

4.1.2 e2e 测试模式

使用 e2e 模式启动应用可以启动后立刻运行自动化测试脚本:

image

4.1.3 产品模式

image

image

4.2 Blade

Blade 没有提供启动脚本, 不过 README 文件提供了启动步骤

image

image

4.3 JFinal

在启动 JFinal 项目的时候我们遇到了一点麻烦, 因为 JFinal build 的包是用来部署到 servlet 容器里面的, 我们不太想去和 Tomcat 之类的东西打交道, 所以在 JFinal 项目里面添加了插件:

		<plugins>
		  <plugin>
		    <groupId>org.mortbay.jetty</groupId>
		    <artifactId>jetty-maven-plugin</artifactId>
		    <version>8.1.8.v20121106</version>
		    <configuration>
		        <stopKey>stop</stopKey>
		        <stopPort>5599</stopPort>
		        <webAppConfig>
		            <contextPath>/</contextPath>
		        </webAppConfig>
		        <scanIntervalSeconds>5</scanIntervalSeconds>
		        <connectors>
		            <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
		                <port>80</port>
		                <maxIdleTime>60000</maxIdleTime>
		            </connector>
		        </connectors>
		    </configuration>
		  </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <executable>java</executable>
                    <arguments>
                        <argument>-Xms512m</argument>
                        <argument>-Xmx512m</argument>
                        <argument>-classpath</argument>
                        <classpath/>
                        <argument>htdi.jfinal.hellworld.config.ProjectConfig</argument>
                    </arguments>
                </configuration>
            </plugin>
	</plugins>

之后我们就可以直接启动了:

image

image

4.4 Nutz

Nutz 也是通过 README 文件提供了启动步骤. Nutz 启动信息比较多, 没法截屏:

luog@luog-X510UQR:~/p/htdi/nutz-hello-world$ mvn -q clean compile nutzboot:run
[INFO ] 07:55:50.685 org.nutz.boot.banner.SimpleBannerPrinter.printBanner(SimpleBannerPrinter.java:34) - 
 _   _ ______                                      ___   
| \ | || ___ \  ______ ______ ______ ______ ______| \ \  
|  \| || |_/ / |______|______|______|______|______| |\ \ 
| . ` || ___ \  ______ ______ ______ ______ ______| | > >
| |\  || |_/ / |______|______|______|______|______| |/ / 
\_| \_/\____/                                     |_/_/  
  
:: Nutz Boot ::   (2.3-SNAPSHOT)

[INFO ] 07:55:50.736 org.nutz.ioc.loader.annotation.AnnotationIocLoader.<init>(AnnotationIocLoader.java:50) -  > scan 'htdi.nutz.helloworld'
[INFO ] 07:55:50.738 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'mainLauncher                            ' - htdi.nutz.helloworld.MainLauncher
[INFO ] 07:55:50.740 org.nutz.ioc.loader.annotation.AnnotationIocLoader.<init>(AnnotationIocLoader.java:50) -  > scan 'org.nutz.boot.starter'
[INFO ] 07:55:50.750 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'nutFilterStarter                        ' - org.nutz.boot.starter.nutz.mvc.NutFilterStarter
[INFO ] 07:55:50.753 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'whaleFilterStarter                      ' - org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter
[INFO ] 07:55:50.754 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'jettyStarter                            ' - org.nutz.boot.starter.jetty.JettyStarter
[INFO ] 07:55:50.765 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'nbServletContextListener                ' - org.nutz.boot.starter.servlet3.NbServletContextListener
[INFO ] 07:55:50.767 org.nutz.ioc.loader.annotation.AnnotationIocLoader.addClass(AnnotationIocLoader.java:98) -    > add 'jstViewStarter                          ' - org.nutz.boot.starter.jst.JstViewStarter
[INFO ] 07:55:50.777 org.nutz.boot.NbApp.prepare(NbApp.java:279) - Configure Manual:
|id  |key                                     |required  |Possible Values     |Default   |Description         |                                starters|
|----|----------------------------------------|----------|--------------------|----------|--------------------|----------------------------------------|
|0   |jetty.contextPath                       |no        |                    |/         |上下文路径               |org.nutz.boot.starter.jetty.JettyStarter|
|1   |jetty.gzip.enable                       |no        |                    |false     |是否启用gzip            |org.nutz.boot.starter.jetty.JettyStarter|
|2   |jetty.gzip.level                        |no        |                    |-1        |gzip压缩级别            |org.nutz.boot.starter.jetty.JettyStarter|
|3   |jetty.gzip.minContentSize               |no        |                    |512       |gzip压缩最小触发大小        |org.nutz.boot.starter.jetty.JettyStarter|
|4   |jetty.host                              |no        |                    |0.0.0.0   |监听的ip地址             |org.nutz.boot.starter.jetty.JettyStarter|
|5   |jetty.http.idleTimeout                  |no        |                    |300000    |空闲时间,单位毫秒           |org.nutz.boot.starter.jetty.JettyStarter|
|6   |jetty.httpConfig.blockingTimeout        |no        |                    |-1        |阻塞超时                |org.nutz.boot.starter.jetty.JettyStarter|
|7   |jetty.httpConfig.headerCacheSize        |no        |                    |8192      |头部缓冲区大小             |org.nutz.boot.starter.jetty.JettyStarter|
|8   |jetty.httpConfig.maxErrorDispatches     |no        |                    |10        |最大错误重定向次数           |org.nutz.boot.starter.jetty.JettyStarter|
|9   |jetty.httpConfig.outputAggregationSize  |no        |                    |8192      |输出聚合大小              |org.nutz.boot.starter.jetty.JettyStarter|
|10  |jetty.httpConfig.outputBufferSize       |no        |                    |32768     |输出缓冲区大小             |org.nutz.boot.starter.jetty.JettyStarter|
|11  |jetty.httpConfig.persistentConnectionsEnabled|no        |                    |true      |是否启用持久化连接           |org.nutz.boot.starter.jetty.JettyStarter|
|12  |jetty.httpConfig.requestHeaderSize      |no        |                    |8192      |请求的头部最大值            |org.nutz.boot.starter.jetty.JettyStarter|
|13  |jetty.httpConfig.responseHeaderSize     |no        |                    |8192      |响应的头部最大值            |org.nutz.boot.starter.jetty.JettyStarter|
|14  |jetty.httpConfig.securePort             |no        |                    |          |安全协议的端口,例如8443      |org.nutz.boot.starter.jetty.JettyStarter|
|15  |jetty.httpConfig.secureScheme           |no        |                    |          |安全协议,例如https        |org.nutz.boot.starter.jetty.JettyStarter|
|16  |jetty.httpConfig.sendDateHeader         |no        |                    |true      |是否发送日期信息            |org.nutz.boot.starter.jetty.JettyStarter|
|17  |jetty.httpConfig.sendServerVersion      |no        |                    |true      |是否发送jetty版本号        |org.nutz.boot.starter.jetty.JettyStarter|
|18  |jetty.maxFormContentSize                |no        |                    |1gb       |表单最大尺寸              |org.nutz.boot.starter.jetty.JettyStarter|
|19  |jetty.page.404                          |no        |                    |          |自定义404页面,同理,其他状态码也是支持的|org.nutz.boot.starter.jetty.JettyStarter|
|20  |jetty.page.java.lang.Throwable          |no        |                    |          |自定义java.lang.Throwable页面,同理,其他异常也支持|org.nutz.boot.starter.jetty.JettyStarter|
|21  |jetty.port                              |no        |                    |8080      |监听的端口               |org.nutz.boot.starter.jetty.JettyStarter|
|22  |jetty.staticPath                        |no        |                    |          |额外的静态文件路径           |org.nutz.boot.starter.jetty.JettyStarter|
|23  |jetty.staticPathLocal                   |no        |                    |          |静态文件所在的本地路径         |org.nutz.boot.starter.jetty.JettyStarter|
|24  |jetty.threadpool.idleTimeout            |no        |                    |60000     |线程池idleTimeout,单位毫秒 |org.nutz.boot.starter.jetty.JettyStarter|
|25  |jetty.threadpool.maxThreads             |no        |                    |500       |线程池最大线程数maxThreads  |org.nutz.boot.starter.jetty.JettyStarter|
|26  |jetty.threadpool.minThreads             |no        |                    |200       |线程池最小线程数minThreads  |org.nutz.boot.starter.jetty.JettyStarter|
|27  |jetty.welcome_files                     |no        |                    |index.html,index.htm,index.do|WelcomeFile列表       |org.nutz.boot.starter.jetty.JettyStarter|
|28  |nutz.mvc.whale.enc.input                |no        |                    |UTF-8     |在其他Filter之前设置input编码|org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|29  |nutz.mvc.whale.enc.output               |no        |                    |UTF-8     |在其他Filter之前设置output编码|org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|30  |nutz.mvc.whale.http.hidden_method_param |no        |                    |          |隐形http方法参数转换所对应的参数名 |org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|31  |nutz.mvc.whale.http.method_override     |no        |                    |false     |是否允许使用X-HTTP-Method-Override|org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|32  |nutz.mvc.whale.upload.enable            |no        |                    |false     |是否启用隐形Upload支持      |org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|33  |web.session.timeout                     |no        |                    |30        |Session空闲时间,单位分钟    |org.nutz.boot.starter.jetty.JettyStarter|
[INFO ] 07:55:50.782 org.nutz.ioc.impl.NutIoc.<init>(NutIoc.java:130) - ... NutIoc init complete
[INFO ] 07:55:50.825 org.eclipse.jetty.util.log.Log.initialized(Log.java:193) - Logging initialized @2378ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO ] 07:55:50.987 org.eclipse.jetty.server.Server.doStart(Server.java:374) - jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 1.8.0_171-b11
[WARN ] 07:55:51.173 org.eclipse.jetty.annotations.AnnotationParser.asmVersion(AnnotationParser.java:95) - Unknown asm implementation version, assuming version 393216
[INFO ] 07:55:51.173 org.eclipse.jetty.annotations.AnnotationConfiguration.scanForAnnotations(AnnotationConfiguration.java:489) - Scanning elapsed time=0ms
[INFO ] 07:55:51.177 org.eclipse.jetty.webapp.StandardDescriptorProcessor.visitServlet(StandardDescriptorProcessor.java:283) - NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
[INFO ] 07:55:51.185 org.eclipse.jetty.server.session.DefaultSessionIdManager.doStart(DefaultSessionIdManager.java:365) - DefaultSessionIdManager workerName=node0
[INFO ] 07:55:51.185 org.eclipse.jetty.server.session.DefaultSessionIdManager.doStart(DefaultSessionIdManager.java:370) - No SessionScavenger set, using defaults
[INFO ] 07:55:51.187 org.eclipse.jetty.server.session.HouseKeeper.startScavenging(HouseKeeper.java:149) - node0 Scavenging every 660000ms
[INFO ] 07:55:51.202 org.nutz.mvc.NutFilter._init(NutFilter.java:85) - NutFilter[nutz] starting ...
[INFO ] 07:55:51.207 org.nutz.mvc.impl.NutLoading.load(NutLoading.java:55) - Nutz Version : 1.r.66-20180614 
[INFO ] 07:55:51.207 org.nutz.mvc.impl.NutLoading.load(NutLoading.java:56) - Nutz.Mvc[nutz] is initializing ...
[INFO ] 07:55:51.207 org.nutz.mvc.config.AbstractNutConfig.getAppRoot(AbstractNutConfig.java:82) - /WEB-INF/ not Found?!
[INFO ] 07:55:51.210 org.nutz.mvc.impl.NutLoading.evalUrlMapping(NutLoading.java:159) - Build URL mapping by org.nutz.mvc.impl.UrlMappingImpl ...
[INFO ] 07:55:51.232 org.nutz.mvc.impl.NutActionChainMaker.getProcessorByName(NutActionChainMaker.java:72) - Optional processor class not found, disabled : org.nutz.integration.shiro.NutShiroProcessor
[INFO ] 07:55:51.254 org.nutz.mvc.impl.NutActionChainMaker.getProcessorByName(NutActionChainMaker.java:72) - Optional processor class not found, disabled : org.nutz.plugins.validation.ValidationProcessor
[INFO ] 07:55:51.262 org.nutz.mvc.impl.NutLoading.evalUrlMappingluog@luog-X510UQR:~/p/htdi/nutz-hello-world$ mvn -q clean compile nutzboot:run
(NutLoading.java:221) - Found 1 module methods
[INFO ] 07:55:51.263 org.nutz.mvc.impl.NutLoading.load(NutLoading.java:141) - Nutz.Mvc[nutz] is up in 56ms
[INFO ] 07:55:51.277 org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:851) - Started o.e.j.w.WebAppContext@d2b229c{/,[],AVAILABLE}
[INFO ] 07:55:51.288 org.eclipse.jetty.server.AbstractConnector.doStart(AbstractConnector.java:289) - Started ServerConnector@32a11092{HTTP/1.1,[http/1.1]}{127.0.0.1:8080}
[INFO ] 07:55:51.288 org.eclipse.jetty.server.Server.doStart(Server.java:411) - Started @2842ms
[INFO ] 07:55:51.291 org.nutz.boot.NbApp.execute(NbApp.java:213) - NB started : 702ms

image

4.5 Play

Play 的实现使用了 sbt, 第一次运行简直就是灾难, 花了至少半个小时才能启动. 不过第一次之后就都很好了:

image

image

4.6 Redkale

和大部分实现项目一样, Redkale 在 README 中提供了如何启动应用的方法. 我们可以很容易启动 Redkale 项目:

image

image

4.7 SpringBoot

启动 SpringBoot 项目没有什么问题:

image

image

4.8 TIO-MVC

TIO-MVC 实现稍微有一点不一样. 使用 mvn clean package 构建项目包之后我们需要到 /target/htdi-tio-helloworld 目录然后运行 startup.sh 启动应用:

image

image

5. 一些数据比较

image

注意

1 SpringBoot 提供了单元测试用例, 不过该测试只检查是否加载了正确的模板文件, 并没有检查输出是否满足需求 2 测试环境: 操作系统: LinuxMint 19, 硬件: i7 8550U + 16GB RAM + SSD

© 著作权归作者所有

共有 人打赏支持
罗格林

罗格林

粉丝 291
博文 43
码字总数 41912
作品 4
其他
架构师
私信 提问
加载中

评论(12)

罗格林
罗格林

引用来自“JFinal”的评论

没想到上了热门,我再不出手 jfinal 就要被玩坏了 @天蓬元帅 提交的版本里面很多东西都是没有使用的,按照现有的评比标准数据自然不对

刚刚提交了一个版本,除开配置有效代码行从 56 降到 8 行,如果 start() 启动与路由配置也算的话也就 10 行代码, 打包大小从 6.8M 降到 954KB,注意是 KB,通过 mvn package 命令打包。

此外,显示框架版本根本不需要硬编码,jfinal 从来都支持,启动时显示版本应用从 jfinal 诞生第一天开始就支持,启动时长 0.06s。

综上,除了没有自动化测试以外,其它数据应该全是第一的,还请更新下数据。上了热门还是要注意一下不能误导用户
哈哈,大 boss 出马果然不一样
JFinal
JFinal
没想到上了热门,我再不出手 jfinal 就要被玩坏了 @天蓬元帅 提交的版本里面很多东西都是没有使用的,按照现有的评比标准数据自然不对

刚刚提交了一个版本,除开配置有效代码行从 56 降到 8 行,如果 start() 启动与路由配置也算的话也就 10 行代码, 打包大小从 6.8M 降到 954KB,注意是 KB,通过 mvn package 命令打包。

此外,显示框架版本根本不需要硬编码,jfinal 从来都支持,启动时显示版本应用从 jfinal 诞生第一天开始就支持,启动时长 0.06s。

综上,除了没有自动化测试以外,其它数据应该全是第一的,还请更新下数据。上了热门还是要注意一下不能误导用户
山东-小木
山东-小木
发起者是罗总吗?
红白机
红白机

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功

引用来自“罗格林”的评论

这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊

引用来自“红白机”的评论

可能是我机器不怎么好。启动都是5S以上。不过相对以前SSH 30S,还算不错。

引用来自“红白机”的评论

热加载是不用的,我指的是第一次启动的时间。

引用来自“罗格林”的评论

那你的机器就有点尴尬了,这里头 Blade, JFinal 和 t-io 都应该是秒起才对啊. 我用的是 i7 8550U (低电压处理器), 16GB RAM, SSD
是有点残旧了~i5 4200M的,4G,500G机械盘~~~~各位轻喷!!
罗格林
罗格林

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功

引用来自“罗格林”的评论

这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊

引用来自“红白机”的评论

可能是我机器不怎么好。启动都是5S以上。不过相对以前SSH 30S,还算不错。

引用来自“红白机”的评论

热加载是不用的,我指的是第一次启动的时间。
那你的机器就有点尴尬了,这里头 Blade, JFinal 和 t-io 都应该是秒起才对啊. 我用的是 i7 8550U (低电压处理器), 16GB RAM, SSD
红白机
红白机

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功

引用来自“罗格林”的评论

这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊

引用来自“红白机”的评论

可能是我机器不怎么好。启动都是5S以上。不过相对以前SSH 30S,还算不错。
热加载是不用的,我指的是第一次启动的时间。
罗格林
罗格林

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功

引用来自“罗格林”的评论

这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊

引用来自“红白机”的评论

可能是我机器不怎么好。启动都是5S以上。不过相对以前SSH 30S,还算不错。
你是说热加载都需要 5s ?
红白机
红白机

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功

引用来自“罗格林”的评论

这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊
可能是我机器不怎么好。启动都是5S以上。不过相对以前SSH 30S,还算不错。
罗格林
罗格林

引用来自“闲大赋”的评论

springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功
这个比较只能在 spring, play 和 act 之间搞了,貌似其他的没有提供热加载支持啊
闲大赋
闲大赋
springboot 第一次启动需要时间略长,不过再热加载应该就非常短了,还是建议比较的时候加上热加载时间,比如,修改HelloWorld类,看看什么时间能重启成功
项目如何进入结算阶段

施工单位上传竣工验收报告-监理审核-建设审核-通知协审 这个时候协审上传项目总结报告-审计组长审核-中心主任审核,项目关闭。 项目关闭之后,审计局界面会看到关闭的项目,然后可以点击开启...

peter_shen
2015/04/05
5
0
web 3D 程序编写

1、利用HTML、XHTML、DIV、CSS、JavaScript、websocket等web技术实现前端3D展示功能。 2、有3D图形学知识基础(了解渲染、场景、动画、材质、灯光等) 3、three.js与webgl/canvas结合并可二次...

zb1508478742023
2017/10/20
0
0
web 3D 程序编写

1、利用HTML、XHTML、DIV、CSS、JavaScript、websocket等web技术实现前端3D展示功能。 2、有3D图形学知识基础(了解渲染、场景、动画、材质、灯光等) 3、three.js与webgl/canvas结合并可二次...

zb1508478742023
2017/10/20
0
0
次世代建模

1、3D建模(单个物品、中小型场景、大型场景)和模型优化 2、使用3D Max、Maya、Zbrush、Photoshop等软件完成建模、UV、贴图绘制等工作 3、利用次世代建模技术,诸如法线雕刻,环境遮蔽贴图等...

zb1508478742023
2017/10/20
0
0
次世代建模

1、3D建模(单个物品、中小型场景、大型场景)和模型优化 2、使用3D Max、Maya、Zbrush、Photoshop等软件完成建模、UV、贴图绘制等工作 3、利用次世代建模技术,诸如法线雕刻,环境遮蔽贴图等...

zb1508478742023
2017/10/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Httpd 整合 Tomcat 步骤

环境:Tomcat8 + Httpd2.4 工作原理:借助于Tomcat的AJP连接器实现Apache与Tomcat的通信 配置步骤: 1. 配置httpd.conf 新增: Include conf/extra/mod_jk.conf 修改:添加 index.jsp <IfM...

ZeroneLove
昨天
1
0
Docker笔记3——容器命令(未写完,明天整理接着写)

未写完,明天整理接着写 新建并启动容器 docker run docker run [OPTIONS] IMAGE [COMMEND] [ARG...] OPTIONS: --name=[容器新名字] :为容器指定一个名称 -d:后台运行容器,并返回容器ID,...

HappyBKs
昨天
1
0
2018个人年终总结

感谢领导的信任和指导,新的一年获得了很多成长和提高,改掉了很多不好的习惯。 在这一年里,我在领导的帮助下,主要完成了以下功能: 1、完成上海银行版本投资营销相关功能的开发。 2、完成车...

万山红遍
昨天
9
0
保密工作与linux系统的发展

保密工作从性质上可以分成商业方面的保密和国家安全方面的保密。由于自己从事的是IT方面的工作,工作中必然会接触涉及到计算机信息方面的相关文件。加上单位已近通过武器装备科研生产单位二级...

linux-tao
昨天
2
0
Spark共享变量

概述 Spark程序的大部分操作都是RDD操作,通过传入函数给RDD操作函数来计算。这些函数在不同的节点上并发执行,但每个内部的变量有不同的作用域,不能相互访问,所以有时会不太方便,Spark提...

仟昭
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部