├── .editorconfig ├── .gitignore ├── README.adoc ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ratpack-centric ├── ratpack-centric.gradle └── src │ └── main │ └── java │ ├── RatpackBlockingIntegrationApp.java │ ├── RatpackCentricApp.java │ ├── RatpackHelloWorldApp.java │ └── RatpackRegistryApp.java ├── settings.gradle ├── spring-boot-centric ├── spring-boot-centric.gradle └── src │ └── main │ └── java │ ├── demo │ └── SpringBootHelloWorld.java │ ├── demo2 │ └── SpringBootCentricHelloWorld.java │ └── demo3 │ └── SpringBootRatpackCommunicationApp.java ├── spring-config ├── spring-config.gradle └── src │ └── main │ └── java │ ├── AcmeConfig.java │ ├── ImportantBusinessService.java │ └── MessageService.java ├── spring-ratpack.gradle └── why-would-you-do-that.jpg /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] # for all files 4 | indent_style = space 5 | indent_size = 2 6 | 7 | # We recommend you to keep these unchanged 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipr 2 | *.iml 3 | *.iws 4 | build/ 5 | target/ 6 | out/ 7 | .idea 8 | .gradle 9 | *.log 10 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Spring Boot and Ratpack 2 | Dan Hyun 3 | 4 | With the advent of Ratpack and Spring Boot, many believe that you can only choose one. 5 | That couldn't be further from the truth. 6 | The only area where they potentially overlap is in serving web requests. 7 | The Spring Framework is a very rich ecosystem including but not limited to DI, web mvc, data, security, etc. 8 | Ratpack focuses on rapid web app prototyping and iteration - balancing low resource utilization, high performance and developer friendliness. 9 | We'll explore the ways in which Ratpack and Spring Boot work in harmony. 10 | 11 | 12 | == http://projects.spring.io/spring-boot/[Spring Boot] 13 | 14 | === Background 15 | 16 | * Builds on 10+ Years of experience around Servlet based Web MVC applications 17 | ** `spring:spring-webmvc:1.0.2` published 2005 18 | * Single self-contained artifact -- standalone jar/war or war that can deploy to Servlet Container 19 | * `@Configuration` encouraged although you can certainly define beans in XML 20 | 21 | .But why would you use XML over `@Configuration` 22 | image::why-would-you-do-that.jpg[] 23 | 24 | * Annotation-based framework integrations 25 | ** spring-cloud projects 26 | *** `@EnableDiscoveryClient` 27 | *** `@RibbonClient` 28 | 29 | === Value 30 | 31 | * Immediate impact for newcomers 32 | * Industry standard 33 | * Let's user focus on writing business logic 34 | 35 | 36 | === Hello World 37 | 38 | [source,gradle] 39 | .build.gradle 40 | ---- 41 | buildscript { 42 | ext { 43 | springBootVersion = '1.3.3.RELEASE' 44 | } 45 | repositories { 46 | mavenCentral() 47 | } 48 | dependencies { 49 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 50 | } 51 | } 52 | 53 | apply plugin: 'idea' 54 | apply plugin: 'eclipse' 55 | apply plugin: 'spring-boot' 56 | 57 | repositories { 58 | jcenter() 59 | mavenCentral() 60 | } 61 | 62 | dependencies { 63 | compile('org.springframework.boot:spring-boot-starter-web') 64 | } 65 | ---- 66 | 67 | [source,java,linenums] 68 | .SpringBootApp.java 69 | ---- 70 | package demo; // <1> 71 | 72 | import org.springframework.boot.SpringApplication; 73 | import org.springframework.boot.autoconfigure.SpringBootApplication; 74 | import org.springframework.stereotype.Controller; 75 | import org.springframework.web.bind.annotation.RequestMapping; 76 | import org.springframework.web.bind.annotation.ResponseBody; 77 | 78 | @SpringBootApplication // <2> 79 | @Controller // <3> 80 | public class SpringBootApp { 81 | 82 | @RequestMapping("/") 83 | @ResponseBody String home() { 84 | return "Hello, World!"; 85 | } 86 | 87 | public static void main(String[] args) { 88 | SpringApplication.run(SpringBootApp.class, args); 89 | } 90 | } 91 | ---- 92 | <1> Doesn't work if class is in default package 93 | <2> Activate Spring Boot web app 94 | <3> Spring MVC Controller Stereotype 95 | 96 | [source,bash] 97 | ---- 98 | $ ./gradlew bootRun 99 | 100 | $ curl -v localhost:8080/ 101 | * timeout on name lookup is not supported 102 | * Trying ::1... 103 | * Connected to localhost (::1) port 8080 (#0) 104 | > GET / HTTP/1.1 105 | > Host: localhost:8080 106 | > User-Agent: curl/7.45.0 107 | > Accept: */* 108 | > 109 | < HTTP/1.1 200 OK 110 | < Server: Apache-Coyote/1.1 111 | < Content-Type: text/plain;charset=UTF-8 112 | < Content-Length: 13 113 | < Date: Thu, 31 Mar 2016 03:27:03 GMT 114 | < 115 | Hello, World!* Connection #0 to host localhost left intact 116 | ---- 117 | 118 | Very compelling demo: 119 | 120 | * No application-context.xml 121 | * No web.xml 122 | * No Tomcat configuration 123 | 124 | == https://ratpack.io/[Ratpack] 125 | 126 | === Background 127 | 128 | * Java 8+ 129 | * Builds on battle proven async/NIO networking framework -- Netty 130 | * Lightly opinionated (async/lazy/functional/immutable/composable/reactive) 131 | * Lightweight && Low resource overhead -> _fast_ 132 | * Collection of jars -- no framework SDK / no codegen 133 | * Not MVC 134 | ** no controllers 135 | ** no table routing 136 | * Currently at 1.2.0 (Stable API) 137 | 138 | === Value 139 | 140 | * WYSIWYG -- no "magic" (no classpath scanning, no reflection) 141 | * Start writing business logic immediately 142 | * Designed for developer friendliness/productivity 143 | * First class testing support 144 | * Great for rapid prototyping and evolving to larger code base 145 | * Reduce that monthly AWS bill $ $ $ 146 | 147 | 148 | === Hello World Groovy 149 | [source,groovy] 150 | .hello.groovy 151 | ---- 152 | @Grab('io.ratpack:ratpack-groovy:1.2.0') 153 | import static ratpack.groovy.Groovy.ratpack 154 | 155 | ratpack { 156 | handlers { // <1> 157 | get { // <2> 158 | render 'Hello, Groovy!' 159 | } 160 | } 161 | } 162 | ---- 163 | <1> Define request handling aspect of your application 164 | <2> Register a handler for `GET /` sends plain text response 165 | 166 | 167 | [source,bash] 168 | ---- 169 | $ groovy hello.groovy 170 | $ curl -v localhost:5050/ 171 | * timeout on name lookup is not supported 172 | * Trying ::1... 173 | * Connected to localhost (::1) port 5050 (#0) 174 | > GET / HTTP/1.1 175 | > Host: localhost:5050 176 | > User-Agent: curl/7.45.0 177 | > Accept: */* 178 | > 179 | < HTTP/1.1 200 OK 180 | < content-type: text/plain;charset=UTF-8 181 | < content-length: 14 182 | < connection: keep-alive 183 | < 184 | Hello, Groovy!* Connection #0 to host localhost left intact 185 | ---- 186 | 187 | Handles "live reloads" 188 | 189 | === Hello World Java 190 | 191 | [source, java] 192 | .build.gradle 193 | ---- 194 | plugins { 195 | id 'io.ratpack.ratpack-java' version '1.2.0' 196 | id 'com.github.johnrengelman.shadow' version '1.2.3' 197 | } 198 | 199 | mainClassName = 'RatpackApp' 200 | 201 | repositories { 202 | jcenter() 203 | } 204 | ---- 205 | 206 | [source,java,linenums] 207 | .RatpackApp.java 208 | ---- 209 | import ratpack.server.RatpackServer; 210 | 211 | public class RatpackApp { 212 | public static void main(String[] args) throws Exception { 213 | RatpackServer.start(serverSpec -> serverSpec // <1> 214 | .handlers(chain -> chain // <2> 215 | .get(ctx -> ctx.getResponse().send("Hello, World!")) // <3> 216 | ) 217 | ); 218 | } 219 | } 220 | ---- 221 | <1> Supply specification on how to build the Ratpack application 222 | <2> Supply description of Ratpack application 223 | <3> Add handler for `GET /` endpoint 224 | 225 | [source,bash] 226 | ---- 227 | ./gradlew run -t //<1> 228 | ---- 229 | <1> Compile and run application in continuous mode (recompiles app as source changes) 230 | 231 | [source,bash] 232 | ---- 233 | $ curl -v localhost:5050 234 | * Rebuilt URL to: localhost:5050/ 235 | * timeout on name lookup is not supported 236 | * Trying ::1... 237 | * Connected to localhost (::1) port 5050 (#0) 238 | > GET / HTTP/1.1 239 | > Host: localhost:5050 240 | > User-Agent: curl/7.45.0 241 | > Accept: */* 242 | > 243 | < HTTP/1.1 200 OK 244 | < content-type: text/plain;charset=UTF-8 245 | < content-length: 13 246 | < connection: keep-alive 247 | < 248 | Hello, World!* Connection #0 to host localhost left intact 249 | ---- 250 | 251 | == Meet the API 252 | 253 | === https://ratpack.io/manual/current/api/ratpack/handling/Handler.html[The Handler] 254 | 255 | * SAM type 256 | * Primary means of request processing 257 | 258 | [source, java] 259 | ---- 260 | public interface Handler { 261 | void handle(Context context); 262 | } 263 | ---- 264 | 265 | === https://ratpack.io/manual/current/api/ratpack/handling/Context.html[The Context] 266 | 267 | * Primary means of reading request and response object 268 | * Primary means of inter Handler communciation 269 | * Registry of application level and request level objects 270 | * Provides easy access to features of Ratpack 271 | ** Parsing 272 | ** Rendering 273 | ** HTTP objects 274 | ** Execution 275 | 276 | 277 | === https://ratpack.io/manual/current/api/ratpack/registry/Registry.html[The Registry] 278 | 279 | A registry of objects of which Ratpack is aware; primarily used to communicate between `Handler` s. 280 | 281 | [source,java] 282 | .RatpackRegistryApp.java 283 | ---- 284 | import ratpack.registry.Registry; 285 | import ratpack.server.RatpackServer; 286 | 287 | public class RatpackRegistryApp { 288 | public static void main(String[] args) throws Exception { 289 | RatpackServer.start(serverSpec -> serverSpec 290 | .registry(Registry.of(registrySpec -> registrySpec // <1> 291 | .add(MessageService.class, () -> "My message service") // <2> 292 | )) 293 | .handlers(chain -> chain 294 | .get(ctx -> { 295 | MessageService messageService = ctx.get(MessageService.class); // <3> 296 | ctx.render(messageService.send()); 297 | }) 298 | ) 299 | ); 300 | } 301 | interface MessageService { 302 | String send(); 303 | } 304 | } 305 | ---- 306 | <1> Use Registry builder to build and add Registry to Ratpack server definition 307 | <2> Add an instance of `MessageService` to the Registry 308 | <3> Retrieve `MessageService` instance from Context Registry 309 | 310 | [source,bash] 311 | ---- 312 | $ curl -v localhost:5050/ 313 | * timeout on name lookup is not supported 314 | * Trying ::1... 315 | * Connected to localhost (::1) port 5050 (#0) 316 | > GET / HTTP/1.1 317 | > Host: localhost:5050 318 | > User-Agent: curl/7.45.0 319 | > Accept: */* 320 | > 321 | < HTTP/1.1 200 OK 322 | < content-type: text/plain;charset=UTF-8 323 | < content-length: 18 324 | < connection: keep-alive 325 | < 326 | My message service* Connection #0 to host localhost left intact 327 | ---- 328 | 329 | Communicating between handlers 330 | 331 | [source,java] 332 | .RatpackRegistryApp.java 333 | ---- 334 | import ratpack.registry.Registry; 335 | import ratpack.server.RatpackServer; 336 | 337 | import java.time.Instant; 338 | 339 | public class RatpackRegistryApp { 340 | public static void main(String[] args) throws Exception { 341 | RatpackServer.start(serverSpec -> serverSpec 342 | .registry(Registry.of(registrySpec -> registrySpec 343 | .add(MessageService.class, () -> "My message service") 344 | )) 345 | .handlers(chain -> chain 346 | .all(ctx -> // <1> 347 | ctx.next(Registry.single(Instant.now())) // <2> 348 | ) 349 | .get(ctx -> { 350 | Instant requestStart = ctx.get(Instant.class); // <3> 351 | MessageService messageService = ctx.get(MessageService.class); 352 | ctx.render(requestStart + " " + messageService.send()); 353 | }) 354 | ) 355 | ); 356 | } 357 | interface MessageService { 358 | String send(); 359 | } 360 | } 361 | ---- 362 | <1> Add handler that applies to every incoming request 363 | <2> Use `Registry#single` factory to create Registry of single item and pass to next qualifying handler 364 | <3> Retrieve `Instant` from created from upstream handler 365 | 366 | 367 | [source, bash] 368 | ---- 369 | $ curl -v localhost:5050 370 | * Rebuilt URL to: localhost:5050/ 371 | * timeout on name lookup is not supported 372 | * Trying ::1... 373 | * Connected to localhost (::1) port 5050 (#0) 374 | > GET / HTTP/1.1 375 | > Host: localhost:5050 376 | > User-Agent: curl/7.45.0 377 | > Accept: */* 378 | > 379 | < HTTP/1.1 200 OK 380 | < content-type: text/plain;charset=UTF-8 381 | < content-length: 43 382 | < connection: keep-alive 383 | < 384 | 2016-03-31T06:16:45.632Z My message service* Connection #0 to host localhost left intact 385 | ---- 386 | 387 | === Integrating existing blocking libraries 388 | 389 | [source, java] 390 | .RatpackBlockingIntegrationApp.java 391 | ---- 392 | import ratpack.exec.Blocking; 393 | import ratpack.exec.Promise; 394 | import ratpack.registry.Registry; 395 | import ratpack.server.RatpackServer; 396 | 397 | public class RatpackBlockingIntegrationApp { 398 | public static void main(String[] args) throws Exception { 399 | RatpackServer.start(serverSpec -> serverSpec 400 | .registry(Registry.of(registrySpec -> registrySpec 401 | .add(BlockingMessageService.class, () -> { 402 | try { 403 | Thread.sleep(1000); // <1> 404 | } catch (Exception e) { 405 | // uh oh 406 | } 407 | return "My blocking message service"; 408 | }) 409 | )) 410 | .handlers(chain -> chain 411 | .get(ctx -> { 412 | BlockingMessageService messageService = ctx.get(BlockingMessageService.class); 413 | Promise promise = Blocking.get(messageService::send); <2> 414 | promise.then(ctx::render); <3> 415 | }) 416 | ) 417 | ); 418 | } 419 | interface BlockingMessageService { 420 | String send(); 421 | } 422 | } 423 | ---- 424 | <1> Simulate blocking behavior 425 | <2> Use https://ratpack.io/manual/current/api/ratpack/exec/Blocking.html#get-ratpack.func.Factory-[`Blocking#get(Factory)`] method to hook into Ratpack's blocking executor 426 | <3> When promise is resolved send response to client 427 | 428 | [source,bash] 429 | ---- 430 | $ curl -v localhost:5050 431 | * Rebuilt URL to: localhost:5050/ 432 | * timeout on name lookup is not supported 433 | * Trying ::1... 434 | * Connected to localhost (::1) port 5050 (#0) 435 | > GET / HTTP/1.1 436 | > Host: localhost:5050 437 | > User-Agent: curl/7.45.0 438 | > Accept: */* 439 | > 440 | < HTTP/1.1 200 OK 441 | < content-type: text/plain;charset=UTF-8 442 | < content-length: 18 443 | < connection: keep-alive 444 | < 445 | My blocking message service* Connection #0 to host localhost left intact 446 | ---- 447 | 448 | == Spring and Ratpack Integration 449 | 450 | Huge thanks to http://github.com/dsyer[@dsyer (Dave Syer)] 451 | 452 | === Ratpack Centric approach 453 | [source,gradle] 454 | .build.gradle 455 | ---- 456 | plugins { 457 | id 'io.ratpack.ratpack-java' version '1.2.0' 458 | id 'com.github.johnrengelman.shadow' version '1.2.3' // <1> 459 | } 460 | 461 | mainClassName = 'RatpackApp' // <2> 462 | 463 | repositories { 464 | jcenter() 465 | } 466 | 467 | dependencies { 468 | compile ratpack.dependency('spring-boot') // <3> 469 | } 470 | ---- 471 | <1> Shadow plugin to create fatjar 472 | <2> Specify main class 473 | <3> Add `io.ratpack:ratpack-spring-boot:1.2.0` to compile time dependencies 474 | 475 | [source,java] 476 | .RatpackApp.java 477 | ---- 478 | import org.springframework.context.annotation.Bean; 479 | import org.springframework.context.annotation.Configuration; 480 | import ratpack.server.RatpackServer; 481 | import ratpack.spring.Spring; 482 | 483 | public class RatpackApp { 484 | public static void main(String[] args) throws Exception { 485 | RatpackServer.start(serverSpec -> serverSpec 486 | .registry(Spring.spring(MySpringConfig.class)) // <1> 487 | .handlers(chain -> chain 488 | .get(ctx -> { 489 | String hello = ctx.get(String.class); // <2> 490 | ctx.getResponse().send(hello); 491 | }) 492 | ) 493 | ); 494 | } 495 | 496 | @Configuration 497 | public static class MySpringConfig { 498 | @Bean 499 | public String hello() { 500 | return "Hello from Spring!"; 501 | } 502 | } 503 | } 504 | ---- 505 | <1> Add base Spring config class to Ratpack's registry 506 | <2> Retrieves register Spring bean from Ratpack's registry 507 | 508 | Issuing a get request after starting the Ratpack application. 509 | [source] 510 | ---- 511 | $ curl -v localhost:5050/ 512 | * timeout on name lookup is not supported 513 | * Trying ::1... 514 | * Connected to localhost (::1) port 5050 (#0) 515 | > GET / HTTP/1.1 516 | > Host: localhost:5050 517 | > User-Agent: curl/7.45.0 518 | > Accept: */* 519 | > 520 | < HTTP/1.1 200 OK 521 | < content-type: text/plain;charset=UTF-8 522 | < content-length: 18 523 | < connection: keep-alive 524 | < 525 | Hello from Spring!* Connection #0 to host localhost left intact 526 | ---- 527 | 528 | === Spring Boot Centric approach 529 | 530 | http://start.spring.io/[Spring Boot Initializr: Bootstrap Spring Boot] 531 | 532 | [source, bash] 533 | ---- 534 | $ curl -o demo.zip "http://start.spring.io/starter.zip?type=gradle-project&bootVersion=1.3.3.RELEASE &baseDir=demo&groupId=com.example&artifactId=demo&name=demo&description=Demo+project+for+Spring+Boot &packageName=com.example&packaging=jar&javaVersion=1.8&language=java&autocomplete=&generate-project= &style=ratpack" 535 | 536 | % Total % Received % Xferd Average Speed Time Time Time Current 537 | Dload Upload Total Spent Left Speed 538 | 100 54752 100 54752 0 0 122k 0 --:--:-- --:--:-- --:--:-- 126k 539 | 540 | $ unzip -l demo.zip 541 | Archive: demo.zip 542 | Length Date Time Name 543 | --------- ---------- ----- ---- 544 | 0 2016-03-31 02:41 demo/ 545 | 4957 2016-03-31 02:41 demo/gradlew 546 | 0 2016-03-31 02:41 demo/gradle/ 547 | 0 2016-03-31 02:41 demo/gradle/wrapper/ 548 | 0 2016-03-31 02:41 demo/src/ 549 | 0 2016-03-31 02:41 demo/src/main/ 550 | 0 2016-03-31 02:41 demo/src/main/java/ 551 | 0 2016-03-31 02:41 demo/src/main/java/com/ 552 | 0 2016-03-31 02:41 demo/src/main/java/com/example/ 553 | 0 2016-03-31 02:41 demo/src/main/resources/ 554 | 0 2016-03-31 02:41 demo/src/test/ 555 | 0 2016-03-31 02:41 demo/src/test/java/ 556 | 0 2016-03-31 02:41 demo/src/test/java/com/ 557 | 0 2016-03-31 02:41 demo/src/test/java/com/example/ 558 | 891 2016-03-31 02:41 demo/build.gradle 559 | 53638 2016-03-31 02:41 demo/gradle/wrapper/gradle-wrapper.jar 560 | 200 2016-03-31 02:41 demo/gradle/wrapper/gradle-wrapper.properties 561 | 2314 2016-03-31 02:41 demo/gradlew.bat 562 | 299 2016-03-31 02:41 demo/src/main/java/com/example/DemoApplication.java 563 | 0 2016-03-31 02:41 demo/src/main/resources/application.properties 564 | 405 2016-03-31 02:41 demo/src/test/java/com/example/DemoApplicationTests.java 565 | --------- ------- 566 | 62704 21 files 567 | ---- 568 | 569 | [source, gradle] 570 | .build.gradle 571 | ---- 572 | buildscript { 573 | ext { 574 | springBootVersion = '1.3.3.RELEASE' 575 | } 576 | repositories { 577 | mavenCentral() 578 | } 579 | dependencies { 580 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 581 | } 582 | } 583 | 584 | apply plugin: 'spring-boot' 585 | 586 | repositories { 587 | jcenter() 588 | mavenCentral() 589 | } 590 | 591 | dependencies { 592 | compile project(':spring-config') 593 | compile('io.ratpack:ratpack-spring-boot:1.2.0') 594 | compile('org.springframework.boot:spring-boot-starter-web') 595 | } 596 | 597 | ---- 598 | 599 | [source, java] 600 | .SpringBootCentricHelloWorld.java 601 | ---- 602 | package demo2; 603 | 604 | import org.springframework.boot.SpringApplication; 605 | import org.springframework.boot.autoconfigure.SpringBootApplication; 606 | import org.springframework.context.annotation.Bean; 607 | import ratpack.func.Action; 608 | import ratpack.handling.Chain; 609 | import ratpack.spring.config.EnableRatpack; 610 | 611 | @SpringBootApplication 612 | @EnableRatpack // <1> 613 | public class SpringBootCentricHelloWorld { 614 | 615 | @Bean 616 | Action chain() { // <2> 617 | return chain -> chain 618 | .get(ctx -> ctx.render("Hello from Spring Boot!")); 619 | } 620 | 621 | public static void main(String[] args) { 622 | SpringApplication.run(SpringBootCentricHelloWorld.class, args); 623 | } 624 | } 625 | ---- 626 | <1> Configures Spring Boot to be Ratpack-aware 627 | <2> Provides the `Action` as a Component, Spring takes care of building and starting Ratpack server 628 | 629 | [NOTE] 630 | Spring Boot (Spring MVC) and Ratpack will run side by side, binding to both 8080 (Tomcat default) and 5050 (Ratpack default). If you wish to disable Spring MVC autoconfig there are http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-non-web-application[many ways to do so]. 631 | 632 | === Defining controller and Ratpack handler chain side by side 633 | 634 | [source, java] 635 | .SpringBootRatpackCommunicationApp.java 636 | ---- 637 | package demo3; 638 | 639 | import org.springframework.boot.SpringApplication; 640 | import org.springframework.boot.autoconfigure.SpringBootApplication; 641 | import org.springframework.context.annotation.Bean; 642 | import org.springframework.stereotype.Controller; 643 | import org.springframework.web.bind.annotation.RequestMapping; 644 | import org.springframework.web.bind.annotation.ResponseBody; 645 | import org.springframework.web.client.RestTemplate; 646 | import ratpack.func.Action; 647 | import ratpack.handling.Chain; 648 | import ratpack.http.client.HttpClient; 649 | import ratpack.http.client.ReceivedResponse; 650 | import ratpack.jackson.Jackson; 651 | import ratpack.spring.config.EnableRatpack; 652 | 653 | import java.net.URI; 654 | import java.time.Instant; 655 | import java.util.HashMap; 656 | import java.util.Map; 657 | 658 | @SpringBootApplication 659 | @EnableRatpack 660 | @Controller 661 | public class SpringBootRatpackCommunicationApp { 662 | 663 | @Bean 664 | Action chain() { 665 | return chain -> chain 666 | .get(ctx -> ctx.render("Hello from Ratpack in Spring Boot!")) 667 | .get("json", ctx -> { // <1> 668 | Map map = new HashMap<>(); 669 | map.put("date", Instant.now().toString()); 670 | ctx.render(Jackson.json(map)); // <2> 671 | }) 672 | .get("boot", ctx -> { // <3> 673 | HttpClient client = ctx.get(HttpClient.class); // <4> 674 | client.get(new URI("http://localhost:8080")) 675 | .map(ReceivedResponse::getBody) 676 | .map(body -> "Received from Spring Boot: " + body.getText()) 677 | .then(ctx::render); // <5> 678 | }); 679 | } 680 | 681 | @RequestMapping("/") 682 | @ResponseBody String bootRoot() { 683 | return "This is Spring Boot"; 684 | } 685 | 686 | @RequestMapping("/ratpack") 687 | @ResponseBody Map bootRatpack() { // <6> 688 | RestTemplate restTemplate = new RestTemplate(); 689 | return restTemplate.getForObject("http://localhost:5050/json", Map.class); 690 | } 691 | 692 | public static void main(String[] args) { 693 | SpringApplication.run(SpringBootRatpackCommunicationApp.class, args); 694 | } 695 | } 696 | ---- 697 | <1> Create `GET /json` endpoint in Ratpack chain that returns simple JSON object containing time of request 698 | <2> Utilize `Jackson.json(Object)` to create a renderable that knows how to send JSON to client. 699 | <3> Create `GET /boot` endpoint in Ratpack chain that sends a response based on response from Spring Boot app 700 | <4> Retrieve non-blocking/async `HttpClient` from Ratpack registry 701 | <5> Create `GET /` request against Spring Boot app, extract the response body and send to the user 702 | <6> Create `GET /ratpack` endpoing in Spring Boot app that issues a REST call against the previously defined `/json` endpoint in he Ratpack app and render the result to the user 703 | 704 | 705 | [source, bash] 706 | ---- 707 | $ curl localhost:8080 708 | This is Spring Boot 709 | 710 | $ curl localhost:5050 711 | Hello from Ratpack in Spring Boot! 712 | 713 | $ curl localhost:5050/boot 714 | Received from Spring Boot: This is Spring Boot 715 | 716 | $ curl localhost:8080/ratpack 717 | {"date":"2016-03-31T07:11:14.178Z"} 718 | ---- 719 | 720 | `compile 'io.ratpack:ratpack-spring-boot-starter:1.2.0'` 721 | 722 | 723 | == Resources 724 | 725 | * http://start.spring.io/[Spring Initializr] 726 | * https://ratpack.io/[Ratpack Homepage] 727 | * https://github.com/ratpack/ratpack[Ratpack source code] 728 | * https://github.com/ratpack[Ratpack examples] 729 | * https://ratpack.io/manual/current/all.html[Ratpack user guide] 730 | * https://slack-signup.ratpack.io/[Ratpack Slack Registration] 731 | * https://forum.ratpack.io/[Ratpack Forum] 732 | * http://shop.oreilly.com/product/0636920037545.do[Book: Learning Ratpack (O'Reilly)] 733 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danhyun/spring-ratpack-2016/1e37c40caf44b778bfcc19e17228e64d5a73b698/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Mar 30 22:05:18 EDT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /ratpack-centric/ratpack-centric.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'io.ratpack.ratpack-java' version '1.2.0' 3 | id 'com.github.johnrengelman.shadow' version '1.2.3' 4 | } 5 | 6 | dependencies { 7 | compile project(':spring-config') 8 | compile ratpack.dependency('spring-boot') 9 | } 10 | -------------------------------------------------------------------------------- /ratpack-centric/src/main/java/RatpackBlockingIntegrationApp.java: -------------------------------------------------------------------------------- 1 | import ratpack.exec.Blocking; 2 | import ratpack.exec.Promise; 3 | import ratpack.registry.Registry; 4 | import ratpack.server.RatpackServer; 5 | 6 | public class RatpackBlockingIntegrationApp { 7 | public static void main(String[] args) throws Exception { 8 | RatpackServer.start(serverSpec -> serverSpec 9 | .registry(Registry.of(registrySpec -> registrySpec 10 | .add(BlockingMessageService.class, () -> { 11 | try { 12 | Thread.sleep(1000); 13 | } catch (Exception e) { 14 | // uh oh 15 | } 16 | return "My blocking message service"; 17 | }) 18 | )) 19 | .handlers(chain -> chain 20 | .get(ctx -> { 21 | BlockingMessageService messageService = ctx.get(BlockingMessageService.class); 22 | Promise promise = Blocking.get(messageService::send); 23 | promise.then(ctx::render); 24 | }) 25 | ) 26 | ); 27 | } 28 | interface BlockingMessageService { 29 | String send(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ratpack-centric/src/main/java/RatpackCentricApp.java: -------------------------------------------------------------------------------- 1 | import ratpack.server.RatpackServer; 2 | import ratpack.spring.Spring; 3 | 4 | public class RatpackCentricApp { 5 | public static void main(String[] args) throws Exception { 6 | RatpackServer.start(serverSpec -> serverSpec 7 | .registry(Spring.spring(AcmeConfig.class)) 8 | .handlers(chain -> chain 9 | .get(ctx -> ctx.render("Hello, World!")) 10 | ) 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ratpack-centric/src/main/java/RatpackHelloWorldApp.java: -------------------------------------------------------------------------------- 1 | import ratpack.server.RatpackServer; 2 | 3 | public class RatpackHelloWorldApp { 4 | public static void main(String[] args) throws Exception { 5 | RatpackServer.start(serverSpec -> serverSpec 6 | .handlers(chain -> chain 7 | .get(ctx -> ctx.render("Hello, World!")) 8 | ) 9 | ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ratpack-centric/src/main/java/RatpackRegistryApp.java: -------------------------------------------------------------------------------- 1 | import ratpack.registry.Registry; 2 | import ratpack.server.RatpackServer; 3 | 4 | import java.time.Instant; 5 | 6 | public class RatpackRegistryApp { 7 | public static void main(String[] args) throws Exception { 8 | RatpackServer.start(serverSpec -> serverSpec 9 | .registry(Registry.of(registrySpec -> registrySpec 10 | .add(MessageService.class, () -> "My message service") 11 | )) 12 | .handlers(chain -> chain 13 | .all(ctx -> 14 | ctx.next(Registry.single(Instant.now())) 15 | ) 16 | .get(ctx -> { 17 | Instant requestStart = ctx.get(Instant.class); 18 | MessageService messageService = ctx.get(MessageService.class); 19 | ctx.render(requestStart + " " + messageService.send()); 20 | }) 21 | ) 22 | ); 23 | } 24 | interface MessageService { 25 | String send(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include \ 2 | 'spring-config', 3 | 'ratpack-centric', 4 | 'spring-boot-centric' 5 | 6 | rootProject.name = 'spring-ratpack' 7 | 8 | def setBuildFile(project) { 9 | project.buildFileName = "${project.name}.gradle" 10 | project.children.each(this.&setBuildFile) 11 | } 12 | 13 | setBuildFile(rootProject) 14 | -------------------------------------------------------------------------------- /spring-boot-centric/spring-boot-centric.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.3.3.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'spring-boot' 14 | 15 | dependencies { 16 | compile project(':spring-config') 17 | compile('io.ratpack:ratpack-spring-boot:1.2.0') 18 | compile('org.springframework.boot:spring-boot-starter-web') 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-centric/src/main/java/demo/SpringBootHelloWorld.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | 9 | @SpringBootApplication 10 | @Controller 11 | public class SpringBootHelloWorld { 12 | 13 | @RequestMapping("/") 14 | @ResponseBody String home() { 15 | return "Hello, World!"; 16 | } 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(SpringBootHelloWorld.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spring-boot-centric/src/main/java/demo2/SpringBootCentricHelloWorld.java: -------------------------------------------------------------------------------- 1 | package demo2; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import ratpack.func.Action; 7 | import ratpack.handling.Chain; 8 | import ratpack.spring.config.EnableRatpack; 9 | 10 | @SpringBootApplication 11 | @EnableRatpack 12 | public class SpringBootCentricHelloWorld { 13 | 14 | @Bean 15 | Action chain() { 16 | return chain -> chain 17 | .get(ctx -> ctx.render("Hello from Spring Boot!")); 18 | } 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(SpringBootCentricHelloWorld.class, args); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spring-boot-centric/src/main/java/demo3/SpringBootRatpackCommunicationApp.java: -------------------------------------------------------------------------------- 1 | package demo3; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | import org.springframework.web.client.RestTemplate; 10 | import ratpack.func.Action; 11 | import ratpack.handling.Chain; 12 | import ratpack.http.client.HttpClient; 13 | import ratpack.http.client.ReceivedResponse; 14 | import ratpack.jackson.Jackson; 15 | import ratpack.spring.config.EnableRatpack; 16 | 17 | import java.net.URI; 18 | import java.time.Instant; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | @SpringBootApplication 23 | @EnableRatpack 24 | @Controller 25 | public class SpringBootRatpackCommunicationApp { 26 | 27 | @Bean 28 | Action chain() { 29 | return chain -> chain 30 | .get(ctx -> ctx.render("Hello from Ratpack in Spring Boot!")) 31 | .get("json", ctx -> { 32 | Map map = new HashMap<>(); 33 | map.put("date", Instant.now().toString()); 34 | ctx.render(Jackson.json(map)); 35 | }) 36 | .get("boot", ctx -> { 37 | HttpClient client = ctx.get(HttpClient.class); 38 | client.get(new URI("http://localhost:8080")) 39 | .map(ReceivedResponse::getBody) 40 | .map(body -> "Received from Spring Boot: " + body.getText()) 41 | .then(ctx::render); 42 | }); 43 | } 44 | 45 | @RequestMapping("/") 46 | @ResponseBody String bootRoot() { 47 | return "This is Spring Boot"; 48 | } 49 | 50 | @RequestMapping("/ratpack") 51 | @ResponseBody Map bootRatpack() { 52 | RestTemplate restTemplate = new RestTemplate(); 53 | return restTemplate.getForObject("http://localhost:5050/json", Map.class); 54 | } 55 | 56 | public static void main(String[] args) { 57 | SpringApplication.run(SpringBootRatpackCommunicationApp.class, args); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spring-config/spring-config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | springVersion = '4.2.5.RELEASE' 3 | } 4 | dependencies { 5 | compile "org.springframework:spring-core:$springVersion" 6 | compile "org.springframework:spring-context:$springVersion" 7 | } 8 | -------------------------------------------------------------------------------- /spring-config/src/main/java/AcmeConfig.java: -------------------------------------------------------------------------------- 1 | import org.springframework.context.annotation.Bean; 2 | import org.springframework.context.annotation.Configuration; 3 | 4 | @Configuration 5 | public class AcmeConfig { 6 | 7 | @Bean 8 | public String secretMessage() { 9 | return "Important Message"; 10 | } 11 | 12 | @Bean 13 | public ImportantBusinessService lucrativeService() { 14 | return investment -> investment * 10; 15 | } 16 | 17 | @Bean 18 | public MessageService secretMessageService(String secreteMessage) { 19 | return () -> secreteMessage; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /spring-config/src/main/java/ImportantBusinessService.java: -------------------------------------------------------------------------------- 1 | 2 | interface ImportantBusinessService { 3 | double makeMoney(double investment); 4 | } 5 | -------------------------------------------------------------------------------- /spring-config/src/main/java/MessageService.java: -------------------------------------------------------------------------------- 1 | 2 | public interface MessageService { 3 | String send(); 4 | } 5 | -------------------------------------------------------------------------------- /spring-ratpack.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | apply plugin: 'idea' 3 | apply plugin: 'eclipse' 4 | apply plugin: 'java' 5 | repositories { 6 | jcenter() 7 | mavenCentral() 8 | } 9 | } 10 | 11 | idea { 12 | project { 13 | jdkName '1.8' 14 | languageLevel '1.8' 15 | vcs 'Git' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /why-would-you-do-that.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danhyun/spring-ratpack-2016/1e37c40caf44b778bfcc19e17228e64d5a73b698/why-would-you-do-that.jpg --------------------------------------------------------------------------------