Spring Boot in Three Languages

Implementing a Spring Boot Web Application in Java, Groovy and Kotlin

In this post, I’m going to demonstrate and discuss implementing the same simple CRUD application with Spring Boot using Java, Groovy and Kotlin. A few months ago, I wrote a post exploring a handful of JVM Languages.  It was so much fun, I wanted to do a follow up where I wrote an actual web application. Why those three languages?  They’re the three choices for language on the Spring Initializr.

Our example application for this post is a simple Task application where the user can list, add, edit and delete a task.  The application is developed in each language with Java being original.  The interface is Thymeleaf and the templates and stylesheet are identical across the three projects.

Let’s look at a few quick statistics between the three applications.  If you care about lines of code, and I don’t particularly, imagine the 60%-70% more lines of code in Java across a much larger application.  More interesting to me, is how much slower the Groovy application is to start up.  Since we typically don’t use bootRun in production, it’s a bit of an arbitrary metric, but I have read that Groovy is slower and this was a noticeable difference when testing the applications.

SB_3Lang_StatsChart

The Task Entity

Let’s take a look at our task entity.  It’s straightforward with an id, description and few practical task-related fields.

Java

The Java class is no surprise.  We define our fields, annotate them properly and have getters and setters.

@Entity
public class Task {
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   private Long id;
   private String description;
   private LocalDate startDate;
   private LocalDate completionDate;
   private Priorities priority;
   private Statuses status;

   //standard getters and setters
}

Groovy

The groovy entity is nearly identical to the Java entity, but there are no getters or setters and no access modifiers on the fields.

@Entity
class Task {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long id;
   String description;
   LocalDate startDate;
   LocalDate completionDate;
   Priorities priority;
   Statuses status;
}

Kotlin

The Kotlin entity is where the syntax starts to diverge.  The entity is set up using Kotlin’s data class concept and is defined entirely in it’s constructor.  Kotlin’s Null Safety is also on display.

@Entity
data class Task (
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   var id: Long,
   @Column(nullable = false)
   var description: String,
   @Column(nullable = true)
   var startedDate: LocalDate? = null,
   @Column(nullable = true)
   var completion: LocalDate? = null,
   @Column(nullable = true)
   var priority: Priorities? = null,
   @Column(nullable = true)
   var status: Statuses? = null
)

The Controllers

There are similar differences across the Dto and the enums defined for priority and statuses, so let’s get to something interesting: the controller classes.

Java

To keep things brief, we’re going to look at only a few methods of each controller to illustrate the differences.  We annotate the controller and autowire our Repository and our mapper.  Shown are the methods for listing all the tasks and for saving a new task.

@Controller
public class TaskController {
   @Autowired
   private TaskRepository taskRepository;
   @Autowired
   private ModelMapper modelMapper;
   
   @RequestMapping("/")
   public String listAll(Model model) {
      List<Task> tasks = taskRepository.findAll();
      
      List<TaskDto> taskDtos = tasks.stream().map(task -> modelMapper.map(task, TaskDto.class)).collect(Collectors.toList());
      model.addAttribute("tasks", taskDtos);
      return "task/list";
   }
   
   @RequestMapping(value="/task/add",  params={"save"}, method=RequestMethod.POST)
   public String saveNewTask(@Valid TaskDto taskDto, BindingResult bindingResult, Model model) {
      if (bindingResult.hasErrors()) {
         model.addAttribute("action", "task/add");
         return "task/entry";
      }
      
      Task task = modelMapper.map(taskDto, Task.class);
      taskRepository.save(task);
      return "redirect:/";
   }

   @RequestMapping(value="/task/add", params={"cancel"}, method=RequestMethod.POST)
   public String cancelNewTask() {
      return "redirect:/";
   }

   //...

Groovy

The Groovy controller is nearly identical aside from the missing access modifiers and a couple of other minor syntax differences.

@Controller
class TaskController {
   @Autowired
   TaskRepository taskRepository;
   @Autowired
   ModelMapper modelMapper;
   
   @RequestMapping("/")
   String listAll(Model model) {
      List<Task> tasks = taskRepository.findAll();
      
      List<TaskDto> taskDtos = tasks.stream().map{task -> modelMapper.map(task, TaskDto.class)}.collect(Collectors.toList());
      model.addAttribute("tasks", taskDtos);
      return "task/list";
   }
      
   @RequestMapping(value="/task/add",  params=("save"), method=RequestMethod.POST)
   String saveNewTask(@Valid TaskDto taskDto, BindingResult bindingResult, Model model) {
      if (bindingResult.hasErrors()) {
         model.addAttribute("action", "task/add");
         return "task/entry";
      }
      
      Task task = modelMapper.map(taskDto, Task.class);
      taskRepository.save(task);
      return "redirect:/";
   }

   @RequestMapping(value="/task/add", params=("cancel"), method=RequestMethod.POST)
   String cancelNewTask() {
      return "redirect:/";
   }

   //...

Kotlin

The Kotlin controller has the obvious syntactical differences.  This controller gives an overall impression of being more succinct.  Kotlin allows you to write an entire method on one line if all it does is return something, so the controller takes advantage of that for some of it’s more simple functionality.  The cancelNewTask method in this snippet is an example of that.

@Controller
class TaskController(
   private val taskRepository: TaskRepository,
   private val modelMapper: ModelMapper) {
	
   @GetMapping("/")
   fun listAll(model: Model): String {
      val tasks: List<Task> = taskRepository.findAll();
      val taskDtos : List<TaskDto> = tasks.map{task -> modelMapper.map(task, TaskDto::class.java)}
	
      model.addAttribute("tasks", taskDtos)
      return "task/list";
   }
	
   @PostMapping(value = arrayOf("/task/add"),  params = arrayOf("save"))
   fun saveNewTask(@Valid taskDto: TaskDto, bindingResult: BindingResult, model: Model): String {
      if (bindingResult.hasErrors()) {
         model.addAttribute("action", "task/add");
         return "task/entry";
      }
      
      var task: Task = modelMapper.map(taskDto, Task::class.java);
      taskRepository.save(task);
      return "redirect:/";
   }

   @PostMapping(value=arrayOf("/task/add"), params = arrayOf("cancel"))
   fun cancelNewTask() = "redirect:/"

   //...

Conclusion

This small example illustrates a Spring Boot web application using an in memory H2 database in all the languages available from the Spring Initializr.  I’m not sure I’m ready to switch over the Kotlin yet, but I can understand the appeal.  Given the performance hit, I don’t see an advantage to Groovy.  What are you writing your web applications in?  Leave me a comment if there’s something you think I should try next.

The complete example is available on GitHub.

References and Further Reading

Advertisements