Dernière fois est passé de Spring Boot 1.5 à Spring Boot 2.0. Cette fois, dans un autre projet, j'écrirai sur ce à quoi j'étais accro quand je l'ai élevé de 1.4 à 2.0.
La principale difficulté cette fois était
--Il a fallu beaucoup de temps pour déterminer si la cause du problème était la partie 1.4-> 1.5 ou la partie 1.5-> 2.0. --Thymeleaf a une grande influence, donc les tests sont difficiles --Sérialisation / désérialisation autour de Spring Session --Contre-mesures pour le déploiement en production
C'est autour.
Spécifiez la version de Spring Boot dans pom.xml.
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath />
</parent>
J'utilisais à l'origine HikariPC, je vais donc le supprimer des dépendances.
pom.xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
Le package de SpringBootServletInitializer
a changé, alors réimportez-le.
Le paquet a changé en ʻorg.springframework.boot.web.servlet.`, alors réimportez-le
Le paquet a changé, alors réimportez-le
Remplacez ʻextends WebMvcConfigurerAdapter par ʻimplements WebMvcConfigurer
Changez pour @ EnableWebSecurity
.
https://docs.spring.io/spring-security/site/docs/current/reference/html/mvc.html
Migrez de «vitesse» à «moustache».
pom.xml
- <dependency>
- <groupId>org.apache.velocity</groupId>
- <artifactId>velocity</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-mustache</artifactId>
+ </dependency>
Remplacez le fichier de modèle de vitesse * .vm
par le fichier de modèle de moustache * .mustache
.
Selon le format du modèle de moustache
${hoge}
↓
{{hoge}}
Remplacez tout comme ça
Vous pouvez tous les remplacer à la fois par shell, mais si vous le faites avec IntelliJ refactor, le code de l'appelant sera également trouvé et indiqué, donc si le nombre est petit, IntelliJ refactor peut être bon.
Ajoutez la dépendance suivante
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
Réimporter avec le package suivant
org.springframework.boot.configurationprocessor.json.JSONObject
L'argument de SpringApplication.run a changé
Modifié comme suit
public static void main(String[] args) {
- Object[] objects = { HogeApplication.class, FugaService.class };
- SpringApplication.run(objects, args);
+ final SpringApplication application = new SpringApplication(HogeApplication.class, FugaService.class);
+ application.run(args);
}
De plus, héritez de la classe suivante
SpringBootServletInitializer
Je vais le réparer en silence
https://spring.io/blog/2017/06/20/a-preview-on-spring-data-kay#improved-naming-for-crud-repository-methods
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase;
De
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
Changer le package en
https://www.thymeleaf.org/doc/articles/thymeleaf3migration.html
Mécaniquement comme ça
find src/main/resources -type f -name "*.html" -print | xargs sed -i -e 's/th:substituteby/th:replace/g'
Mécaniquement comme ça
find src/main/resources -type f -name "*.html" -print | xargs sed -i -e 's@type=\"text/css\"@@g'
Tout en regardant le contenu, supprimez-le.
Un tel mec
<script>(window.dataLayer || (window.dataLayer = [])).push(<span th:remove="tag" th:utext="${hoge}"/>)</script>
Réparez-le comme ça
<script type="text/javascript" th:inline="javascript">/*<![CDATA[*/
(window.dataLayer || (window.dataLayer = [])).push(/*[(${hoge})]*/)
/*]]>*/</script>
SessionScope
Erreur lorsque @ SessionScope
ne peut pas être référencé lors de l'exécution de SpringSecurity ʻonAuthenticationSuccess
Error creating bean with name 'user': Scope 'session' is not active for the current thread;`
Créez le fichier de configuration suivant
WebRequestContextListener.java
package jp.hoge;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextListener;
import javax.servlet.annotation.WebListener;
@Configuration
@WebListener
public class WebRequestContextListener extends RequestContextListener {
}
java.sql.SQLSyntaxErrorException: Table 'hoge.hibernate_sequence' doesn't exist
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:536)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115)
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983)
at com.mysql.cj.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1826)
at com.mysql.cj.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1923)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
Changer GenerationType
- @GeneratedValue(strategy=GenerationType.AUTO)
+ @GeneratedValue(strategy=GenerationType.IDENTITY)
org.springframework.dao.InvalidDataAccessResourceUsageException: error performing isolated work; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: error performing
isolated work
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy127.save(Unknown Source)
at jp.hoge.service.FugaService.execute(FugaService.java:218)
at jp.hoge.controller.FugaController.execute(FugaController.java:101)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
Les mappages des générateurs d'Hibernate ont changé.
Corrigé en ajoutant les paramètres suivants à application.properties
application.properties
spring.jpa.hibernate.use-new-id-generator-mappings=false
<SpringProfile>
dans logback-spring.xml
ne parvient pas à charger correctement le profil
Pour executableJar, définissez-le comme -Dspring-boot.run.profiles = nom de l'environnement
.
Dans ce projet, nous déploierons la guerre sur Tomcat, donc
Définissez-le dans ʻapplication.properties comme
spring-boot.run.profiles = nom de l'environnement`.
En fait, le profil est divisé lorsque le fichier war est généré dans pom.xml, il est donc défini comme suit.
application.properties
spring-boot.run.profiles=${spring.profiles.active}
pom.xml
<profiles>
<profile>
<id>local</id>
<properties>
<spring.profiles.active>local</spring.profiles.active>
</properties>
</profile>
<profile>
<id>stg</id>
<properties>
<spring.profiles.active>stg</spring.profiles.active>
</properties>
</profile>
</profiles>
Profil au moment de la construction
mvn package -Pstg
Avec ce sentiment, il est intégré dans ʻapplication.properties`.
NullPointerExepotion se produit lors de la tentative d'accès à une variable membre de User Cependant, même si vous regardez le contenu de Redis, vous ne pouvez pas le lire car il est sérialisé ...
@ SessionScope
Une fois, créez un sérialiseur qui se convertit en JSON et l'enregistre dans Redis avec le sentiment suivant
HttpSessionConfig.java
@ConditionalOnProperty(name = "spring.session.store-type", havingValue = "redis", matchIfMissing = false)
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 20 * 24 * 60 * 60) //default=1800 -> 20days
@Configuration
public class HttpSessionConfig implements BeanClassLoaderAware {
@Autowired
private LettuceConnectionFactory lettuceConnectionFactory;
private ClassLoader classLoader;
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
final ObjectMapper mapper = new ObjectMapper()
.registerModules(SecurityJackson2Modules.getModules(classLoader))
.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
return new GenericJackson2JsonRedisSerializer(mapper);
}
//Pour Elasticache
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
@Override
public void setBeanClassLoader(final ClassLoader classLoader) {
this.classLoader = classLoader;
}
}
Ensuite, lors de la transition vers Controller après la connexion, il était défini sur Redis avec tous les champs nuls.
Le contrôleur après la connexion détermine si ʻid dans l'objet ʻUser
est nul, et s'il est nul, il est reconditionné.
Une fois connecté, je peux gérer la session sans aucun problème.
En fait, je voulais pouvoir avoir des informations de session au format JSON, mais comme la structure de la classe User était une structure imbriquée compliquée, j'ai abandonné en raison de contraintes de temps ... Trop d'informations dans la classe User ...
Il semble que le SerialVersionUID
géré en interne pour la sérialisation et la désérialisation a changé avec la mise à niveau de la version de SpringSession, donc si vous déployez le code mis à jour, les utilisateurs déjà connectés ne pourront pas désérialiser les informations de session.
Donc, je mettrai dans la correspondance lors de la désérialisation.
https://sdqali.in/blog/2016/11/02/handling-deserialization-errors-in-spring-redis-sessions/
L'article est un peu vieux et les bibliothèques dépendantes ont changé, je l'ai donc changé comme suit.
HttpSessionConfig.java
- public class HttpSessionConfig {
+ public class HttpSessionConfig extends RedisHttpSessionConfiguration {
+ @Autowired
+ RedisTemplate<Object, Object> redisTemplate;
+ @Bean
+ @Override
+ public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
+ return super.springSessionRepositoryFilter(new SafeDeserializationRepository<>(sessionRepository, redisTemplate));
+ }
SafeDeserializationRepository.java
public class SafeDeserializationRepository<S extends Session> implements SessionRepository<S> {
private final SessionRepository<S> delegate;
private final RedisTemplate<Object, Object> redisTemplate;
private static final String BOUNDED_HASH_KEY_PREFIX = "spring:session:sessions:";
public SafeDeserializationRepository(SessionRepository<S> delegate,
RedisTemplate<Object, Object> redisTemplate) {
this.delegate = delegate;
this.redisTemplate = redisTemplate;
}
@Override
public S createSession() {
return delegate.createSession();
}
@Override
public void save(S session) {
delegate.save(session);
}
@Override
public S findById(String id) {
try {
return delegate.findById(id);
} catch(SerializationException e) {
log.info("Deleting non-deserializable session with key {}", id);
redisTemplate.delete(BOUNDED_HASH_KEY_PREFIX + id);
return null;
}
}
@Override
public void deleteById(String id) {
delegate.deleteById(id);
}
}
Cela permettra aux utilisateurs existants de se déconnecter une fois, de se reconnecter pour enregistrer les nouvelles données de session dans Redis, puis de s'y référer.
En général, le support ci-dessus l'a fait fonctionner dans un environnement de production. (Je pense qu'il y en a d'autres)
De plus, à l'origine, Tomcat devrait être de la série 8.5 ou supérieure, mais la version 8.0 ne semble pas poser de problème, j'ai donc oublié de mettre à niveau Tomcat.
Si vous sautez certaines versions majeures de Spring Boot, ce sera très difficile, alors mettons à jour fréquemment ...! !!
Recommended Posts