Spring CommandLineRunner

CommandLineRunner je funkční rozhraní s metodou void run(String... args) throws Exception. V této metodě můžeme uvést vše, co chceme aby se stalo a startu aplikace. Jednou z možností jak vytvořit CommandLineRunner je vlastní třída s anotací @Configuration a následně metoda s anotací @Bean vracející CommandLineRunner

@Configuration
class AppConfig {

    @Bean
    fun init(bookRepository: BookRepository): CommandLineRunner {
        return CommandLineRunner {
            bookRepository.save(BookEntity(title = "Cyber Leviathan"))
            bookRepository.save(BookEntity(title = "Last Hunt"))
        }
    }
}

Jiný způsob je extendovat CommandLineRunner a implementovat metodu run()

@SpringBootApplication
class SpringData2Application : CommandLineRunner {
	override fun run(vararg args: String?) {
		println("Running ...")
	}
}

fun main(args: Array<String>) {
	runApplication<SpringData2Application>(*args)
}

 

Micrometer a DistributionSummary

Pokud použijeme micrometer a objekt DistributionSummary, zde jsou ukázky výstupu na actuatoru:

Pokud máme hodnoty

500, 1500, 2500, ..., 9500

a

val summary = DistributionSummary.builder("my.summary").baseUnit("miliseconds").register(meterRegistry)

a hodnoty uložíme/nahrajeme

summary.record(hodnota)

výsledek (na endpointu /actuator/prometheus)

# HELP my_summary_miliseconds
# TYPE my_summary_miliseconds summary
my_summary_miliseconds_count 10.0
my_summary_miliseconds_sum 50000.0
# HELP my_summary_miliseconds_max
# TYPE my_summary_miliseconds_max gauge
my_summary_miliseconds_max 9500.0

Další možnosti:

Číst dálMicrometer a DistributionSummary

Projekce a Query

V předchozím příspěvku jsem ukázal základní možnost projekce ve Spring Data. Na pár věcí je ale třeba si dávat pozor. Je možné používat @Query a také nativní @Query(nativeQuery = true), ale je třeba specifikovat název výsledného sloupce pomocí klíčového slova AS.

@Query("SELECT b.title AS title, b.numberOfPages AS numberOfPages FROM BookEntity b")
fun findAllInfo(): List<BaseBookInfo>

@Query("SELECT b.title AS title, b.numberOfPages AS numberOfPages FROM t_book b", nativeQuery = true)
fun findAllInfoNative(): List<BaseBookInfo>

Pokud byste to neudělali, našel by se sice správný počet záznamů, ale všechny záznamy by měly všechny hodnoty null (nebo by došlo k vyhození výjimky v případě přístupu k primitivním datovým typům).

Spring Data a projekce

Pokud pro dotazování používáte Spring Data, v rámci výsledku dostanete root entitu (všechny její hodnoty). Pokud tedy budete mít následující entitu:

@Entity
@Table(name = "t_book")
@SequenceGenerator(initialValue = 100, name = "t_book_seq_gen", sequenceName = "t_book_seq")
class BookEntity (
        @Id
        @GeneratedValue(generator = "t_book_seq_gen", strategy = GenerationType.SEQUENCE)
        var id: Long = 0L,

        var author: String = "",

        var publisher: String = "",

        var title: String = "",

        var foreword: String = "",

        var numberOfPages: Int = 0,

        var publishingYear: Int = 0
)

vždy získáte všechny hodnoty (id, author, publisher, title, foreword, numberOfPages, …), i když je nebudete vždy potřebovat. Pokud chcete použe některé hodnoty, je zde možnost tzv. projekce. Vytvoříte si rozhraní, které bude obsahovat jen to, co potřebujete. Např. budete chtít pouze titul knihy a počet stran.

Číst dálSpring Data a projekce

Spring OAuth2

Pro vytvoření oauth autorizace nabízí spring knihovnu

org.springframework.security.oauth:spring-security-oauth

Zde najdete celkem 8 endpointů označených anotací @FrameworkEndpoint:

  • /oauth/authorize (class AuthorizationEndpoint)
  • /oauth/authorize, method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL (class AuthorizationEndpoint)
  • /oauth/check_token (class CheckTokenEndpoint)
  • /oauth/token, method=RequestMethod.GET (class TokenEndpoint)
  • /oauth/token, method=RequestMethod.POST (class TokenEndpoint)
  • /oauth/token_key, method = RequestMethod.GET (class TokenKeyEndpoint)
  • /oauth/confirm_access (class WhitelabelApprovalEndpoint)
  • /oauth/error (class WhitelabelErrorEndpoint)

Anotace @FrameworkEndpoint znamená, že tyto endpointy jsou vytvořeny frameworkem. Nemusíte je tedy vytvářet sami.

Spring, Kotlin a JSON decoding errors

Nedávno jsem narazil na zajímavý problém. Měl jsem dvě služby, kdy se přes REST rozhraní zavolá jedna, ta provolá druhou a vrátí výsledek. Původní odpověď vypadala takto:

data class GetAchievementsResponse(
        achievements: List<AchievementInfo>
)

Jakmile jsem ale objekt změnil takto:

data class GetAchievementsResponse(
        val aAchievements: List<AchievementInfo>,
        val rAchievements: List<AchievementInfo>
)

Začal jsem dostávat tyto chyby:

JSON decoding error: Instantiation of [simple type, class cz.vitfo.client.GetAchievementsResponse] value failed for JSON property aAchievements due to missing (therefore NULL) value for creator parameter aAchievements which is a non-nullable type
JSON decoding error: Cannot deserialize instance of `java.util.ArrayList` out of FIELD_NAME token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of FIELD_NAME token

Nakonec jsem přišel na to, že z nějakého důvodu com.fasterxml.jackson v tomto případě nezvládá camel case názvy proměnných. Řešením bylo toto pojmenování:

data class GetAchievementsResponse(
        val a_achievements: List<AchievementInfo>,
        val r_achievements: List<AchievementInfo>
)

Spuštění kódu při startu Springu

Pokud potřebujete spusti určitý kód po naběhnutí Springu, můžete použít CommandLineRunner. Je to rozhraní, které indikuje beanu, která má být spuštěna jakmile je ve Spring aplikaci.

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or Order @Order annotation.

Zde je jednoduchý příklad, kdy si vytvoříme konfigurační třídu s názvem SpringDataConfig.

Číst dálSpuštění kódu při startu Springu

Změna verze Javy z 8 na 11

Nedávno jsem prováděl změny verze Javy na projektu, který používá Spring Boot. Měnil jsem verzi Javy z verze 8 na verzi 11. Nebylo to až tak strašné, jak jsem čekal (spíše naopak), přesto se ale některé komplikace objevily. V tomto příspěvku budu postupovat od chyby k chybě, tak jak se u mě objevovaly.

Číst dálZměna verze Javy z 8 na 11

Spring – Java konfigurační soubor a konfigurace anotacemi

Spring má dva hlavní způsoby konfigurace (XML a Javu), ale tři hlavní přístupy ke konfiguraci a každý z těchto přístupů má několik názvů.

  • Konfigurace využívající XML (XML based configuration)
    • XML konfigurační soubor a XML konfigurace (XML config and XML-driven/based configuration)
    • XML konfigurační soubor a konfigurace anotacemi (XML config and annotation-driven/based configuration)
  • Konfigurace využívající Javu
    • Java konfigurační soubor a konfigurace anotacemi (Java config and annotation-driven/based configuration | Java based configuration | JavaConfig)

Java konfigurační soubor a konfigurace anotacemi
V tomto příspěvku se podíváme na Java konfigurační soubor a konfigurace anotacemi. V tomto případě se používá pro konfiguraci Java třída a anotace (@Autowired, @Service, …).

Naše jednoduchá aplikace bude mít dvě rozhraní MessageService a SecurityService. Rozhraní MessageService bude mít metodu String getMessage() a SecurityService String encode(String text). Funkčnost aplikace je tato: nejdříve získá řetězec (metoda getMessage()) a ten potom zašifruje (metoda encode()). Vlastní způsob šifrování, implementovaný v SecurityServiceImpl, bude triviální. Všechny písmena e v textu změníme za € a všechna písmena s za §.

Číst dálSpring – Java konfigurační soubor a konfigurace anotacemi