How To Do It

阅读: 评论:0

How To Do It

How To Do It

2019独角兽企业重金招聘Python工程师标准>>>

1. 介绍

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

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

2. 实现

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

  • ActframeWork
    • 作者: 老码农 - ActFramework 框架作者
  • Blade
    • 作者: 魔王不造反 - Blade 框架作者
  • JFinal
    • 作者: 天蓬元帅 - JFinal 资深用户
  • Nutz
    • 作者: Wendal - nutz 资深用户
  • Play
    • 作者: joymufeng - PlayFramework 资深用户
  • Redkale
    • 作者: Redkale - Redkale 框架作者
  • SpringBoot
    • 作者: 闲.大赋 - SpringBoot 畅销书作者, beetl 模板引擎和 beetlsql 数据库访问库作者
  • TIO-MVC
    • 作者: talent-tan - tio 作者,码云封面人物

3. 源码一览

3.1 ActFramework

public class AppEntry {/*** The home (`/`) endpoint.** @param who*      request query parameter to specify the hello target.*      default value is `World`.*/@GetActionpublic 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.Version()</p>
</body>
</html>

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

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

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

Scenario(Hello World):description: a web page says Hellointeractions:- description: send request to the app without parameterrequest:method: GETurl: /response:html:h1: Hello Worldhead title:- contains: Hello World- contains: ActFrameworkp:<any>:- contains: Powered by ActFramework- description: send request to the app with [who = ActFramework]request:method: GETurl: /?who=ActFrameworkresponse:html:h1: Hello ActFramework

3.2 Blade

public class Application {private static void index(RouteContext ctx) {String message = "Hello " + quest().query("who", "World");ctx.attribute("message", message);ctx.attribute("appVersion", WebContext.blade().env("app.version", "default version"));ctx.attribute("bladeVersion", Const.VERSION);der("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 没有从 文件中获取应用版本信息, 而是在额外的配置文件中定义应用版本.

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#(Const::JFINAL_VERSION)</div>
</html>

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

3.4 Nutz

@IocBean
public class MainLauncher {@Injectprotected 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", ("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 也从 文件中获取项目版本号.

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 = [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 OKcontentAsString(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")).getstatus(home) mustBe Status.OKcontentType(home) mustBe Some("text/html")contentAsString(home) must include ("Hello World")}}
}
class BrowserSpec extends PlaySpecwith OneBrowserPerTestwith GuiceOneServerPerTestwith HtmlUnitFactorywith ServerProvider {"Application" should {"work from within a browser" in {go to ("localhost:" + port)pageSource must include ("Hello World")}}
}

3.6 Redkale

@RestService(name = " ")
public class HelloWorldService implements Service {private String appVersion = "1.0.0";@Overridepublic void init(AnyValue conf) {String path = "/META-INF/dkalex.htdi/htdi-hello/pom.properties";InputStream in = Class().getResourceAsStream(path);if (in != null) {Properties props = new Properties();try {props.load(in);in.close();} catch (Exception e) {}this.appVersion = Property("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: " + DotedVersion() + "</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{@Autowiredprivate MockMvc mvc;@Testpublic 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 + " " + Str("app.version") + "</div>";html += "<div>Powered by <a href='' 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(Int("http.port"), null, null, null);
//		httpConfig.setPageRoot(Str("http.page"));//html/css/js等的根目录,支持classpath:,也支持绝对路径
//		httpConfig.setMaxLiveTimeOfStaticRes(Int("http.maxLiveTimeOfStaticRes"));
//		httpConfig.setPage404(Str("http.404"));
//		httpConfig.setPage500(Str("http.500"));httpConfig.setUseSession(false);httpConfig.setCheckHost(false);String[] scanPackages = new String[] { Package().getName() };//tio mvc需要扫描的根目录包,会递归子目录Routes routes = new Routes(scanPackages);requestHandler = new DefaultHttpRequestHandler(httpConfig, routes);httpServerStarter = new HttpServerStarter(httpConfig, requestHandler);serverGroupContext = ServerGroupContext();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 开发模式

4.1.2 e2e 测试模式

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

4.1.3 产品模式

4.2 Blade

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

4.3 JFinal

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

		<plugins><plugin><groupId&bay.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=&#lipse.jetty.server.nio.SelectChannelConnector"><port>80</port><maxIdleTime>60000</maxIdleTime></connector></connectors></configuration></plugin><plugin><groupId&jo</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.fig.ProjectConfig</argument></arguments></configuration></plugin></plugins>

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

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   |tPath                       |no        |                    |/         |上下文路径               |org.nutz.boot.starter.jetty.JettyStarter|
|1   |able                       |no        |                    |false     |是否启用gzip            |org.nutz.boot.starter.jetty.JettyStarter|
|2   |ip.level                        |no        |                    |-1        |gzip压缩级别            |org.nutz.boot.starter.jetty.JettyStarter|
|3   |ip.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  |questHeaderSize      |no        |                    |8192      |请求的头部最大值            |org.nutz.boot.starter.jetty.JettyStarter|
|13  |sponseHeaderSize     |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.input                |no        |                    |UTF-8     |在其他Filter之前设置input编码|org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|29  |nutz.utput               |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.hod_override     |no        |                    |false     |是否允许使用X-HTTP-Method-Override|org.nutz.boot.starter.nutz.mvc.WhaleFilterStarter|
|32  |nutz.mvc.able            |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.lipse.jetty.util.log.Log.initialized(Log.java:193) - Logging initialized @2378ms lipse.jetty.util.log.Slf4jLog
[INFO ] 07:55:50.lipse.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.lipse.jetty.annotations.AnnotationParser.asmVersion(AnnotationParser.java:95) - Unknown asm implementation version, assuming version 393216
[INFO ] 07:55:51.lipse.jetty.annotations.AnnotationConfiguration.scanForAnnotations(AnnotationConfiguration.java:489) - Scanning elapsed time=0ms
[INFO ] 07:55:51.lipse.jetty.webapp.StandardDescriptorProcessor.visitServlet(StandardDescriptorProcessor.java:283) - NO JSP Support for /, did not lipse.jetty.jsp.JettyJspServlet
[INFO ] 07:55:51.lipse.jetty.server.session.DefaultSessionIdManager.doStart(DefaultSessionIdManager.java:365) - DefaultSessionIdManager workerName=node0
[INFO ] 07:55:51.lipse.jetty.server.session.DefaultSessionIdManager.doStart(DefaultSessionIdManager.java:370) - No SessionScavenger set, using defaults
[INFO ] 07:55:51.lipse.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.AppRoot(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.ProcessorByName(NutActionChainMaker.java:72) - Optional processor class not found, disabled : org.nutz.integration.shiro.NutShiroProcessor
[INFO ] 07:55:51.254 org.nutz.mvc.ProcessorByName(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.lipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:851) - j.w.WebAppContext@d2b229c{/,[],AVAILABLE}
[INFO ] 07:55:51.lipse.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.lipse.jetty.server.Server.doStart(Server.java:411) - Started @2842ms
[INFO ] 07:55:51.291 org.nutz.ute(NbApp.java:213) - NB started : 702ms

4.5 Play

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

4.6 Redkale

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

4.7 SpringBoot

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

4.8 TIO-MVC

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

5. 一些数据比较

注意

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

转载于:

本文发布于:2024-01-28 06:46:03,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17063955675558.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23