-
Notifications
You must be signed in to change notification settings - Fork 0
Spring
Ne pas rendre visibles les champs d'un @Component visibles (> private) car cela peut perturber l'injection. Observé par un TU qui passe dans l'IDE mais plus dans maven. Utiliser à la place des getters.
Pour debug les échanges HTTP :
logging.level.reactor.netty.http.client=debug
logging.level.org.apache.http=DEBUGhttps://gist.github.com/Bludwarf/5bd0acd470f426363d4006e62adc72c8
Pour les requêtes SQL :
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACERAF :
- on a que l'URL de mémoire, ajouter un exemple
- l'une des deux propriétés n'a aucun effet, la supprimer dans ce cas
- sensible à la casse ?
- quelle différence avec https://www.baeldung.com/spring-resttemplate-logging#logging-headersbody-with-apache-httpclient ?
Certains logs sont activés par la propriété spring.mvc.log-resolved-exception=true. Cette propriété est positionnée par défaut lorsqu'on utilise DevTools. Exemple de logs concernés :
2024-08-23_16:02:42.957 WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [org.modelmapper.MappingException: ModelMapper mapping errors:
1) Converter [org.modelmapper.internal.converter.BooleanConverter@6b0f62e3](mailto:org.modelmapper.internal.converter.BooleanConverter@6b0f62e3) failed to convert int to boolean.
1 error]
Méthode qui ajoute l'authent quand on utilise un WebClient : org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager#createAuthorizationContext
Pour afficher le statut de la transaction courante liée au @Transactional :
TransactionAspectSupport.currentTransactionStatus()Le nom de la transaction correspond au nom de la méthode qui l'a créée.
Spring définit un ObjectMapper par défaut qui se configure avec le fichier de propriétés mais aussi avec du code.
Exemples de propriétés :
spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSSXXX
# Déjà à false par défaut dans Spring
spring.jackson.serialization.write-dates-as-timestamps=falseExemple de code :
@Configuration
@RequiredArgsConstructor
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder
.timeZone(TimeZone.getTimeZone("UTC"))
;
}
}On peut également définir des MappingJackson2HttpMessageConverter (cf. article).
On peut aussi définir un SimpleModule de cette manière :
@Component
public class DateModule extends SimpleModule {
public DateModule(final DateFormat dateFormat) {
addSerializer(Date.class, new DateSerializer(dateFormat));
addDeserializer(Date.class, new DateDeserializer(dateFormat));
}
}On ne peut pas commenter en fin de ligne :
key=value # Comment
Dans cette exemple, key vaut "value # Comment".
Si aucune source pour décrire les properties n'est disponible, on peut en spécifier une dans le fichier META-INF/additional-spring-configuration-metadata.json. Exemple :
{
"properties": [
{
"name": "module.oauth2.check-token-uri",
"type": "java.net.URI",
"description": "ACL check_token URI."
}
],
"hints": [
{
"name": "module.oauth2.check-token-uri",
"values": [
{
"value": "http://acl:8085/oauth/check_token",
"description": "ACL through gateway/registry."
},
{
"value": "http://localhost:8085/oauth/check_token",
"description": "Local ACL."
}
]
}
]
}Gist.
Principe de nommer une méthode de Repository en suivant une convention pour en déduire la requête SQL. Exemples :
- getById
- findById
Ce post explique comment Spring implémente ce mécanisme. L'implémentation par défaut se trouve dans la classe SimpleJpaRepository.
Pile d'appel :
- repo.getById(id) : Interface perso qui renvoie T ou Optional
- simpleJpaRepository.getById(id) : Implémentation par défaut de Spring Data JPA
- entityManager.getReference(id) : Appel à Hibernate
Quand on appelle getById c'est bien Hibernate qui lance l'exception via la méthode org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.JpaEntityNotFoundDelegate#handleEntityNotFound (testé avec org.hibernate.orm:hibernate-core:6.4.10.Final dans Spring Boot 3.2.9). Cette exception n'est lancée que si on utilise réellement l'entité (cf. propriété Hibernate hibernate.jpa.compliance.proxy).
Voir cet article, en se focalisant sur le 1er cas EntityManager.getReference.
Ce qui n'est pas cohérent, c'est qu'une méthode getByCode, par exemple, renvoie null au lieu de lancer une exception.
Solution possible : définir une méthode findById dans le repo qui renvoie un objet plutôt qu'un Optional. On reste cohérent avec le principe get => exception / find => null.
Pile d'appel :
- repo.getById(id)
- org.springframework.data.jpa.repository.query.JpaQueryExecution#execute
- org.hibernate.query.internal.AbstractProducedQuery#getSingleResult : lance une exception NoResultException catchée par JpaQueryExecution
Avec Spring Boot 2 on ne renvoie pas d'exception mais simplement null.
| Version de Spring Boot | 2 | 3 |
|---|---|---|
| Implémentation Spring Data JPA | org.springframework.data.jpa.repository.query.JpaQueryExecution#execute | org.springframework.data.jpa.repository.support.SimpleJpaRepository#getById |
| Implémentation Hibernate | org.hibernate.query.internal.AbstractProducedQuery#getSingleResult | org.hibernate.proxy.AbstractLazyInitializer#initialize |
| Exception lancée par Hibernate | javax.persistence.NoResultException | jakarta.persistence.EntityNotFoundException |
| Résultat de Spring Data JPA | Renvoie null | Relaie l'exception lancée par Hibernate |
-
Solution la plus simple : On peut même plus simplement injecter un
Environment - Solution intéressante
Suivre l'article.
Attention : bien utiliser proxyMode = ScopedProxyMode.TARGET_CLASS sur l'annotation @Scope, comme indiqué dans mon commentaire. Cf. doc Spring. En effet, cela permet d'injecter un Proxy du Bean, qui peut ensuite être une instance différente déterminée par le Custom Scope. Sans cela, une instance serait directement injectée et son Scope serait donc celui du bean dans lequel il est injecté.
