{"id":17471182,"url":"https://github.com/vladimir-dejanovic/micronaut-vs-spring-boot","last_synced_at":"2025-03-17T20:30:51.729Z","repository":{"id":123190115,"uuid":"181223220","full_name":"vladimir-dejanovic/micronaut-vs-spring-boot","owner":"vladimir-dejanovic","description":"Code used for my conf talk \"Micronaut, Dragon-Slayer (Spring/boot) or just another framework \"","archived":false,"fork":false,"pushed_at":"2020-10-13T14:05:20.000Z","size":55,"stargazers_count":9,"open_issues_count":6,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T03:41:45.315Z","etag":null,"topics":["java","micronaut","micronautfw","spring-boot","springboot"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vladimir-dejanovic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-13T20:08:04.000Z","updated_at":"2022-01-23T05:03:55.000Z","dependencies_parsed_at":"2023-05-26T07:30:28.932Z","dependency_job_id":null,"html_url":"https://github.com/vladimir-dejanovic/micronaut-vs-spring-boot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-dejanovic%2Fmicronaut-vs-spring-boot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-dejanovic%2Fmicronaut-vs-spring-boot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-dejanovic%2Fmicronaut-vs-spring-boot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladimir-dejanovic%2Fmicronaut-vs-spring-boot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vladimir-dejanovic","download_url":"https://codeload.github.com/vladimir-dejanovic/micronaut-vs-spring-boot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243878486,"owners_count":20362433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","micronaut","micronautfw","spring-boot","springboot"],"created_at":"2024-10-18T16:29:27.837Z","updated_at":"2025-03-17T20:30:51.722Z","avatar_url":"https://github.com/vladimir-dejanovic.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Micronaut, Dragon-Slayer (Spring/boot) or just another framework\nCode used for my conf talk \"Micronaut, Dragon-Slayer (Spring/boot) or just another framework \"\n\nWe will look at simple use case, we will build web application in Spring Boot and Micronaut. Both Code \nbases need to meet this requirements\n \n- REST API\n- DB (connect to DB and be able to read and write data to it)\n- Service (way to organize code into \"Services\")\n- Template engine for HTML (some way of creating dynamic front end)\n- Security (ability to protect parts of application)\n\n## Environment on my machine :)\n\n- Apache Maven 3.5.2\n- Java version: 11.0.2, vendor: AdoptOpenJDK\n\n## Sprint Boot example\n\nFirst we need to go to start.spring.io and add this dependencies\n- Lombok\n- Spring Web Starter\n- Sping Security\n- Thymeleaf\n- H2 Database\n- Spring Data JPA\n\nAfter we setup the name and group we can download the code and start coding.\n\nResult of coding can be seen in **bookstore-springboot**.\n\n### DB\n\nFor database we will use H2 in memory database.\n\n#### Pojo\n\nLet us open class *Book*, as you can see it is simple *Entity* class, Lombok is used so \nthat we don't need to write all getters and setters. \n\n#### Repository\n\nWe also have very simple repository *BookRepository*. There is almost no code there\nsince we relay completely on Spring Data to do all for us.\n\n### Service\n\nNext let us open *BookService*. As you can see it is simple standard Spring service.\nAll that we needed to do is add annotation **@Service**, inject BookRepository and define simple methods.\n\n### REST API\n\nREST API is defined in *BookControler* as standard Spring RestController. Again \nwe relay on Spring annotations to do the heavy lifting for us.\n\nOnly thing left is to define to end points. One for retrieving all books\n- /books\nand one more for adding books\n- /admin/book?title=\u003cbook title\u003e\n\nWe use GET in both cases for simplicity, never do something like this in production.\n\n### Security\n\nSecurity is handled in *SimpleSecurityJavaConfig* which extends WebSecurityConfigurerAdapter.\nHere we define that endpoint **/books** is open to everyone, while endpoint /admin/book is accessible only by authorised users.\n\n### HTML template\n\nLast part of chalange is to have way to generate dynamic front end, we will use Thymeleaf for that.\n\nIn **/resources/template** there is **login.html**, last thing is to connect some url with this template, for that we will use\n**SimpleConfig** which implements WebMvcConfigurer, and add code to map requests to **/login** with our login.html template.\n\nAfter we compile the code and start application, spring will generate random password for us, as username use **user**. Use it when accessing protected part of application.\n```\nUsing generated security password: 1ed406bf-a80e-4763-b106-4b75ff73c6e6\n```\n\n## Micronaut\n\nLet us look now how we can solve same challenge in Micronaut.\n\nThere is no web starter like there is for Spring (start.spring.io). First we need to install [SDKMan](https://sdkman.io/).\nWith SDKMan we can install a lot of things. \n\n```bash\n$ sdk list\n```\n\nTo install Micronaut enter this command\n\n```bash\n$ sdk install micronaut\n\n```\n\nif you already have Micronaut installed, you can check which version you have by typing \n\n```bash\n\n$ sdk list micronaut\n================================================================================\nAvailable Micronaut Versions\n================================================================================\n \u003e * 1.0.4                                                                      \n     1.0.3                                                                      \n     1.0.2                                                                      \n\n================================================================================\n+ - local version\n* - installed\n\u003e - currently in use\n================================================================================\n\n\n```\n\nNow that we have micronaut installed we can start its CLI\n\n```\n$ mn\n```\n\nTo list all possible profiles of micronaut we need to enter\n\n```\nmn\u003e list-profiles\n| Available Profiles\n--------------------\n  cli                 The cli profile\n  configuration       The profile for creating the configuration\n  federation          The federation profile\n  function-aws        The function profile for AWS Lambda\n  function-aws-alexa  The function profile for AWS Alexa-Lambda\n  grpc                Profile for Creating GRPC Services\n  kafka               The Kafka messaging profile\n  profile             A profile for creating new Micronaut profiles\n  rabbitmq            The RabbitMQ messaging profile\n  service             The service profile\n\n```\n\nTo list all profiles run this command\n\n```\nmn\u003e profile-info service\n```\n\n\n### Initial skeleton application\n\nTo create init application we need to run this command\n\n```\n$ mn create-app \u003capp name\u003e\n```\n\nit will use gradel by default, to create app using maven as build tool do this \n\n```\n$ mn create-app \u003capp name\u003e --build maven\n```\n\nto add Hibernate JPA we need to add **--features=hibernate-jpa**, to also add security run this command\n\n```\n$ mn create-app \u003capp name\u003e --build maven --features=hibernate-jpa,security-session \n```\n\nfor example in our case full command wold be \n\n```\n$ mn create-app bookstore-micronaut --build maven --features=hibernate-jpa,security-session\n```\n\nLet us rename package to xyz.itshark.conf.talk.bookstore.micronaut, and let us for now delete tests.\n\nIn resources/applicatino.yml for now comment security part\n\n```yaml\n\n---\nmicronaut:\n    application:\n        name: bookstore-micronaut\n\n---\ndatasources:\n  default:\n    url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE\n    driverClassName: org.h2.Driver\n    username: sa\n    password: ''    \njpa:\n  default:\n    properties:\n      hibernate:\n        hbm2ddl:\n          auto: update\n\n\n#---\n#micronaut:\n#  security:\n#    enabled: true\n#    endpoints:\n#      login:\n#        enabled: true\n#      logout:\n#        enabled: true\n#    session:\n#      enabled: true\n#      loginSuccessTargetUrl: /\n#      loginFailureTargetUrl: /\n---\ndatasources.default: {}\n```\n\nResult of this command is in directory **bookstore-micronaut-step-01**.\n\n### Database\n\nFirst we need to add Pojo class for Book which will be Entity class.\n\n```java\n@Entity\npublic class Book {\n    @Id\n    @GeneratedValue(strategy = GenerationType.AUTO)\n    private Long id;\n\n    @NotNull\n    private String title;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n}\n```\n\nIn this case we will not use Lombok, so we will write code for getters and setters. \nLombok generate getter and setters in comple time, since Micronaut is also doing a log of work in compile time they can clash one with other. \nThey can work together but for this simple use case it is faster just not to use Lombok.\n\nNext is to add Repository, in this case we can't use Spring Data so we need to write code like this\n\n```java\nimport io.micronaut.configuration.hibernate.jpa.scope.CurrentSession;\nimport io.micronaut.runtime.ApplicationConfiguration;\nimport io.micronaut.spring.tx.annotation.Transactional;\n\nimport javax.inject.Singleton;\nimport javax.persistence.EntityManager;\nimport javax.persistence.PersistenceContext;\nimport javax.persistence.TypedQuery;\nimport java.util.List;\n\n@Singleton\npublic class BookRepository  {\n\n    @PersistenceContext\n    private EntityManager entityManager;\n    private final ApplicationConfiguration applicationConfiguration;\n\n    public BookRepositoryImpl(@CurrentSession EntityManager entityManager,\n                              ApplicationConfiguration applicationConfiguration) {\n        this.entityManager = entityManager;\n        this.applicationConfiguration = applicationConfiguration;\n    }\n\n    @Override\n    @Transactional(readOnly = true)\n    public List\u003cBook\u003e findAll() {\n        String sqlString = \"Select o from Book as o\";\n        TypedQuery\u003cBook\u003e query = entityManager.createQuery(sqlString, Book.class);\n        return query.getResultList();\n    }\n\n    @Override\n    @Transactional\n    public Book save(String title) {\n        Book book = new Book();\n        book.setTitle(title);\n        entityManager.persist(book);\n\n        return book;\n    }\n}\n```\n\nResult of this command is in directory **bookstore-micronaut-step-02**.\n\n### Service\n\nLet us create **BookService** now\n\n```java\n@Singleton\npublic class BookService {\n\n    private BookRepository bookRepository;\n\n    public BookServiceImpl(BookRepository bookRepository) {\n        this.bookRepository = bookRepository;\n    }\n\n    @Override\n    public List\u003cBook\u003e getAllBooks() {\n        return bookRepository.findAll();\n    }\n\n    @Override\n    public Book addBook(String title) {\n        return bookRepository.save(title);\n    }\n}\n```\n\nResult of this command is in directory **bookstore-micronaut-step-03**.\n\n\n### REST API\n\nTo add REST API we need to add **BookController**\n\n```java\nimport io.micronaut.http.annotation.Controller;\nimport io.micronaut.http.annotation.Get;\nimport io.micronaut.http.annotation.QueryValue;\nimport xyz.itshark.conf.talk.bookstore.micronaut.pojo.Book;\nimport xyz.itshark.conf.talk.bookstore.micronaut.service.BookService;\n\nimport java.util.List;\n\n@Controller\npublic class BookController {\n\n    private BookService bookService;\n\n    public BookController(BookService bookService) {\n        this.bookService = bookService;\n    }\n\n\n    @Get(\"/books\")\n    public List\u003cBook\u003e getAll() {\n        return bookService.getAllBooks();\n    }\n\n    @Get(\"/admin/book\")\n    public Book addBook(@QueryValue(\"title\") String title) {\n        return bookService.addBook(title);\n    }\n}\n```\n\nResult of this command is in directory **bookstore-micronaut-step-04**.\n\nAt this moment you can build the code and start applicatino and check if all works as intended.\n\n### Security \u0026 HTML Template\n\nLet us know add security. \n\nFirst uncomment security part of resources/application.yaml, then change url for failed authentication to **/login/authFailed**\n\n```yaml\n\n---\nmicronaut:\n    application:\n        name: bookstore-micronaut\n\n---\ndatasources:\n  default:\n    url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE\n    driverClassName: org.h2.Driver\n    username: sa\n    password: ''    \njpa:\n  default:\n    properties:\n      hibernate:\n        hbm2ddl:\n          auto: update\n\n\n---\nmicronaut:\n  security:\n    enabled: true\n    endpoints:\n      login:\n        enabled: true\n      logout:\n        enabled: true\n    session:\n      enabled: true\n      loginSuccessTargetUrl: /\n      loginFailureTargetUrl: /login/authFailed\n---\ndatasources.default: {}\n```\n\nThen we need to add AuthenticationProvider, let us create class **SimpleAuthenticationProvider**\nWe will define user with username **user** and password **password**\n\n\n```java\nimport io.micronaut.security.authentication.*;\nimport io.reactivex.Flowable;\nimport org.reactivestreams.Publisher;\n\nimport javax.inject.Singleton;\nimport java.util.ArrayList;\n\n@Singleton\npublic class SimpleAuthenticationProvider implements AuthenticationProvider {\n\n    @Override\n    public Publisher\u003cAuthenticationResponse\u003e authenticate(AuthenticationRequest authenticationRequest) {\n        if(authenticationRequest.getIdentity().equals(\"user\") \u0026\u0026 authenticationRequest.getSecret().equals(\"password\")) {\n            return Flowable.just(new UserDetails(\"user\",new ArrayList()));\n        } else return Flowable.just(new AuthenticationFailed());\n    }\n}\n```\n\nNext thing we need to do is to secure our end points. For that we will use annotation **@Secured**\n\n```java\n@Controller\npublic class BookController {\n\n    private BookService bookService;\n\n    public BookController(BookService bookService) {\n        this.bookService = bookService;\n    }\n\n\n    @Get(\"/books\")\n    @Secured(SecurityRule.IS_ANONYMOUS)\n    public List\u003cBook\u003e getAll() {\n        return bookService.getAllBooks();\n    }\n\n    @Get(\"/admin/book\")\n    @Secured(SecurityRule.IS_AUTHENTICATED)\n    public Book addBook(@QueryValue(\"title\") String title) {\n        return bookService.addBook(title);\n    }\n}\n```\n\nJust like with the Spring Boot example, url */books* will be open to everyone, while url */admin/book* only to authenticated users.\n\nLet us now add loging and home page to allow users to login and also to demonstrage HTML template engine. \nAgain we will use Thymeleaf, so we need to update pom.xml and add dependencies \n\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003eio.micronaut\u003c/groupId\u003e\n      \u003cartifactId\u003emicronaut-views\u003c/artifactId\u003e\n    \u003c/dependency\u003e\n    \u003cdependency\u003e\n      \u003cgroupId\u003eorg.thymeleaf\u003c/groupId\u003e\n      \u003cartifactId\u003ethymeleaf\u003c/artifactId\u003e\n      \u003cversion\u003e3.0.11.RELEASE\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\nThen create directory **views** in directory **resources** and add files auth.html and home.html.\n\nauth.html\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eAuth\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv th:if=\"${errors}\"\u003e\n    Invalid username and password.\n\u003c/div\u003e\n\u003cform action=\"/login\" method=\"POST\"\u003e\n    \u003cdiv\u003e\u003clabel\u003e User Name : \u003cinput type=\"text\" name=\"username\" id=\"username\"/\u003e \u003c/label\u003e\u003c/div\u003e\n    \u003cdiv\u003e\u003clabel\u003e Password: \u003cinput type=\"password\" name=\"password\" id=\"password\"/\u003e \u003c/label\u003e\u003c/div\u003e\n    \u003cdiv\u003e\u003cinput type=\"submit\" value=\"Login\"/\u003e\u003c/div\u003e\n\u003c/form\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nhome.html\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eHome\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003csection\u003e\n    \u003ch1 th:if=\"${loggedIn}\"\u003eusername: \u003cspan th:text=\"${username}\"\u003e\u003c/span\u003e\u003c/h1\u003e\n    \u003ch1 th:unless=\"${loggedIn}\"\u003eYou are not logged in \u003ca href=\"/login/auth\"\u003eLogin\u003c/a\u003e\u003c/h1\u003e\n\u003c/section\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nLast thing left is to connect this templates with urls. For that we will add LoginController and HomeController\n\n```java\nimport io.micronaut.http.annotation.Controller;\nimport io.micronaut.http.annotation.Get;\nimport io.micronaut.security.annotation.Secured;\nimport io.micronaut.views.View;\n\nimport javax.annotation.Nullable;\nimport java.security.Principal;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Secured(\"isAnonymous()\")\n@Controller(\"/\")\npublic class HomeController {\n\n    @Get(\"/\")\n    @View(\"home\")\n    Map\u003cString, Object\u003e index(@Nullable Principal principal) {\n        Map\u003cString, Object\u003e data = new HashMap\u003c\u003e();\n        data.put(\"loggedIn\", principal!=null);\n        if (principal!=null) {\n            data.put(\"username\", principal.getName());\n        }\n        return data;\n    }\n}\n\n```\n\n\n```java\nimport io.micronaut.http.annotation.Controller;\nimport io.micronaut.http.annotation.Get;\nimport io.micronaut.security.annotation.Secured;\nimport io.micronaut.views.View;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Secured(\"isAnonymous()\")\n@Controller(\"/login\")\npublic class LoginController {\n\n    @Get(\"/auth\")\n    @View(\"auth\")\n    public Map\u003cString, Object\u003e auth() {\n        return new HashMap\u003c\u003e();\n    }\n\n    @Get(\"/authFailed\")\n    @View(\"auth\")\n    public Map\u003cString, Object\u003e authFailed() {\n        return Collections.singletonMap(\"errors\", true);\n    }\n}\n```\n\nResult of this command is in directory **bookstore-micronaut-final**.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladimir-dejanovic%2Fmicronaut-vs-spring-boot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvladimir-dejanovic%2Fmicronaut-vs-spring-boot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladimir-dejanovic%2Fmicronaut-vs-spring-boot/lists"}