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.
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.