Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Spring Boot + Spring MVC + Ratpack possible?

We're a Spring Boot shop and rely heavily on Spring MVC for our REST endpoints. We use Boot and embedded Tomcat to create a self-hosting JAR. Is it possible to replace Tomcat with Ratback while still keeping all my Spring MVC code in place? I am afraid that Spring MVC is tied into the servlet specification somehow and will not run without a servlet container. I am aware of dsyer/spring-boot-ratpack work but after skimming the code couldn't decide if Spring MVC would play well using the bridge. Is anyone aware of any work that will allow us to retain our investment in Spring MVC and have Spring Boot use Ratpack to manage HTTP traffic?

like image 712
user1836542 Avatar asked Feb 18 '15 14:02

user1836542


People also ask

Can we use spring boot and Spring MVC together?

Yes, you can use Spring MVC with Spring Boot. To create and run a Spring MVC web application in spring boot, you need to add the spring-boot-starter dependency in your pom. xml file.

Can spring boot replace Spring MVC?

Spring Boot is not a replacement of Spring MVC. Spring Boot eases the development of Spring-based apps, including ones based on Spring MVC. A Spring MVC-only app needs to be packaged into a WAR file which is then deployed on a Servlet container such as Apache Tomcat.

Is Spring Web and Spring MVC same?

Spring MVC is the framework to build web sites/applications using Spring. There's not a project called just "Spring Web" - you may be thinking of "Spring Web Flow" which is a project that works within Spring MVC.


2 Answers

I suspect the crux of your question can be distilled to: "can we put our Spring controllers on top of Ratpack's non-blocking HTTP layer?" and the simplest answer to that question is no, for reason that the MVC programming model doesn't fit well into the reactive/NIO model very well.

However, if your application has followed some common model-view-controller-(and service) patterns, then your controllers should really just be performing data binding and parsing and delegating out to a service layer. If that's the case, then likely the code in your controller is already non-blocking, and you could easily translate it to Ratpack code.

As an example, consider the following @RestController in a Spring Boot app:

@RestController
@RequestMapping("/user")
class UserController {

  @Autowired
  UserService userService

  @RequestMapping(method = RequestMethod.POST)
  Long create(@RequestBody @Valid User user) {
    User savedUser = userService.save(user)
    return savedUser.id
  }
}

Spring's data binding aspect is a computation process (ie isn't I/O bound), so we can easily translate this into a Ratpack handler:

import app.SpringConfig
import app.User
import app.UserService
import org.springframework.boot.SpringApplication
import org.springframework.context.ApplicationContext
import ratpack.jackson.JacksonModule

import static ratpack.groovy.Groovy.ratpack
import static ratpack.jackson.Jackson.fromJson
import static ratpack.jackson.Jackson.json
import static ratpack.spring.Spring.spring

ratpack {
  bindings {
    add(new JacksonModule())
    bindInstance(ApplicationContext, SpringApplication.run(SpringConfig))
  }
  handlers { ApplicationContext ctx ->
    register(spring(ctx))

    prefix("user") {
      handler { UserService userService ->
        byMethod {
          post {
            def user = parse(fromJson(User))
            blocking {
              userService.save(user)
            } then { User savedUser ->
              render(json(savedUser))
            }
          }
        }
      }
    }
  }
}

Where SpringConfig looks like this:

package app

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class SpringConfig {
  @Bean
  UserService userService() {
    new UserService()
  }
}

And here's a functional test to prove it:

package app

import com.fasterxml.jackson.databind.ObjectMapper
import ratpack.groovy.test.GroovyRatpackMainApplicationUnderTest
import ratpack.test.ApplicationUnderTest
import ratpack.test.http.TestHttpClient
import spock.lang.Shared
import spock.lang.Specification

import static groovy.json.JsonOutput.toJson

class FuncSpec extends Specification {
  @Shared ApplicationUnderTest aut = new GroovyRatpackMainApplicationUnderTest()
  @Shared ObjectMapper mapper = new ObjectMapper()
  @Delegate TestHttpClient client = aut.httpClient
  def "should parse and save user"() {
    given:
    def user = new User(username: "dan", email: "[email protected]")

    when:
    requestSpec { spec ->
      spec.body { b ->
        b.type("application/json")
        b.text(toJson(user))
      }
    }
    post('user')

    then:
    def savedUser = mapper.readValue(response.body.text, User)

    and:
    savedUser.id
  }
}

Hope this helps!

like image 188
Daniel Woods Avatar answered Nov 14 '22 23:11

Daniel Woods


The Spring MVC programming model is not very heavily dependent on Servlet APIs, but it's not supported in any other containers (i.e. not in Ratpack). There is some async stuff there now and Servlet 3.1 enhances it some more, so if that's the part of Ratpack that attracts you, maybe just using that would be a better approach. You won't get all the way to reactive and non-blocking IO that way though.

like image 40
Dave Syer Avatar answered Nov 15 '22 00:11

Dave Syer