... os quatro dele estão depurando.
Inspirado em um relatório de Vladimir Plizgi ( Spring Boot 2: o que eles não escrevem nas notas de versão ), decidi falar sobre minha experiência com o Spring Booth, seus recursos e armadilhas que encontrei no caminho.
Jpa de dados do Spring
O relatório de Vladimir é dedicado à migração para o Spring Booth 2 e às dificuldades que surgem. Na primeira parte, ele descreve erros de compilação, então também começarei com eles.
Eu acho que muitos estão familiarizados com o framework Spring Data e seus derivados para vários data warehouses. Eu trabalhei com apenas um deles - Spring Data JPA. A interface principal usada nesta estrutura - JpaRepository
, sofreu alterações significativas nas versões 2. *.
Considere os métodos mais usados:
De fato, há mais alterações e, depois de passar para a versão 2. * todas elas se transformarão em erros de compilação.
Quando um de nossos projetos levantou a questão da migração para o Spring Booth 2 e as primeiras etapas foram tomadas (substituindo a versão no pom.xml), todo o projeto ficou literalmente vermelho: dezenas de repositórios e centenas de chamadas para seus métodos levaram à migração "(usando novos métodos) conectaria cada segundo arquivo. Imagine o código mais simples:
SomeEntity something = someRepository.findOne(someId); if (something == null) { something = someRepository.save(new SomeEntity()); } useSomething(something);
Para que o projeto se reúna, você precisa
- chamar outro método
- altere o tipo da variável para
something
como Optional<SomeEntity>
- substitua a verificação da referência vazia
Optional::orElseGet
Optional::isPresent
ou Optional::orElse
/ Optional::orElseGet
- corrigir testes
Felizmente, existe uma maneira mais simples, já que o Spring Date oferece aos usuários oportunidades sem precedentes para adaptar os repositórios às suas necessidades. Tudo o que precisamos é definir (se ainda não foram definidos) a interface e a classe que formarão a base de todos os repositórios do nosso projeto. Isso é feito assim:
Todos os nossos repositórios serão herdados dessa interface. Agora implementação:
public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> { private final JpaEntityInformation<T, ?> entityInfo; private final EntityManager entityManager; public BaseJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityManager) { super(entityInfo, entityManager); this.entityInfo = entityInfo; this.entityManager = entityManager; } @Override public T findOne(ID id) { return findById(id).orElse(null);
O resto está na imagem e semelhança. Todos os erros de compilação desaparecem, o código funciona como antes e apenas duas classes são alteradas, e não 200. Agora você pode substituir lentamente a API obsoleta por uma nova à medida que o desenvolvimento avança, pois o Idea colorirá cuidadosamente todas as chamadas dos métodos @ Deprecated em amarelo.
Último golpe - informamos ao Spring que a partir de agora os repositórios devem ser construídos sobre a nossa classe:
@Configuration @EnableJpaRepositories(repositoryBaseClass = BaseJpaRepositoryImpl.class) public class SomeConfig{ }
Em uma piscina calma, caixas são encontradas
O Spring Booth é baseado em dois conceitos - configuração inicial e automática. Na vida, a conseqüência disso é uma propriedade importante - a biblioteca que entrou no caminho de classe do aplicativo é examinada pelo SB e, se for encontrada uma classe que inclua uma determinada configuração, ela será ativada sem o seu conhecimento e instruções explícitas. Mostramos isso com um exemplo simples.
Muitos usam a biblioteca gson para transformar objetos em strings JSON e vice-versa. Muitas vezes você pode ver este código:
@Component public class Serializer { public <T> String serialize(T obj) { return new Gson().toJson(obj); } }
Com acessos frequentes, esse código carrega adicionalmente o coletor de lixo, o que não queremos. Pessoas particularmente inteligentes criam um objeto e o usam:
@Configuration public class SomeConfig { @Bean public Gson gson() { return new Gson(); } } @Component @RequiredArgsConstructor public class Serializer { private final Gson gson; public String serialize(T obj) { return gson.toJson(obj); } }
... sem nem perceber que o Spring Booth pode fazer tudo sozinho. Por padrão, o SB vem com a org.springframework.boot:spring-boot-autoconfigure
, que inclui muitas classes denominadas *AutoConfiguration
, por exemplo, isto:
@Configuration @ConditionalOnClass(Gson.class) @EnableConfigurationProperties(GsonProperties.class) public class GsonAutoConfiguration { @Bean @ConditionalOnMissingBean public GsonBuilder gsonBuilder(List<GsonBuilderCustomizer> customizers) { GsonBuilder builder = new GsonBuilder(); customizers.forEach((c) -> c.customize(builder)); return builder; } @Bean @ConditionalOnMissingBean public Gson gson(GsonBuilder gsonBuilder) { return gsonBuilder.create(); } }
A configuração é simples como um trilho: se o aplicativo tiver uma classe Gson
e não houver um bean definido manualmente desse tipo, uma implementação padrão será criada. Esse é um poder tremendo, permitindo que uma dependência e algumas anotações aumentem uma configuração completa, cuja descrição costumava usar folhas XML e várias caixas manuscritas.
Mas isso é uma grande insidiosidade do Conselho de Segurança. A insidiosidade está em segredo, porque muito trabalho acontece oculto de acordo com os scripts pré-escritos. Isso é bom para uma aplicação típica, mas sair da trilha batida pode causar problemas - o comboio dispara sem aviso prévio. Muitos beans são criados sem nosso conhecimento, o exemplo com o Gson mostra isso bem. Tenha cuidado com a classe e suas dependências, pois ...
Qualquer coisa que entrar no seu caminho de classe pode ser usada contra você.
Um caso da vida. Existem dois aplicativos: um usa LdapTemplate
, enquanto o outro o usa uma vez. Ambos os aplicativos dependem do projeto core
, onde algumas classes comuns são retiradas, e as bibliotecas comuns para ambos os aplicativos foram cuidadosamente dobradas em pom.xml.
Depois de algum tempo, a partir do segundo projeto, todos os usos do LdapTemplate
foram LdapTemplate
como desnecessários. Mas a org.springramework:spring-ldap
permaneceu no core
. Então o SB atacou e org.springramework.ldap:ldap-core
transformou em org.springramework.boot:spring-boot-starter-data-ldap
.
Por esse motivo, mensagens interessantes apareceram nos logs:
Multiple Spring Data modules found, entering strict repository configuration mode! Spring Data LDAP - Could not safely identify store assignment for repository candidate interface ....
Dependência do org.springramework.boot:spring-boot-starter-data-ldap
led org.springramework.boot:spring-boot-starter-data-ldap
caber em um projeto no qual o LDAP não é usado. O que significa hesitar? Bata no caminho da classe :). Além disso, com todas as paradas:
- O Spring Boot percebe
org.springramework.boot:spring-boot-starter-data-ldap
no caminho de classe - agora cada repositório é verificado para ver se é adequado para uso com o Spring Data JPA ou Spring Data LDAP
- reorganização de dependências e
org.springramework.boot:spring-boot-starter-data-ldap
desnecessário de org.springramework.boot:spring-boot-starter-data-ldap
reduz o tempo de inicialização do aplicativo em uma média de 20 (!) segundos em um total de 40-50
Um leitor atento e crítico provavelmente perguntará: por que você precisou alterar org.springramework.ldap:ldap-core
para org.springramework.boot:spring-boot-starter-data-ldap
se o Spring Data LDAP não for usado, mas apenas um for usado classe org.springframework.ldap.LdapTempate
?
Resposta: foi um erro. O fato é que, antes da versão 2.1.0.M1, o autoajuste para LDAP era algo como isto
@Configuration @ConditionalOnClass({ContextSource.class}) @EnableConfigurationProperties({LdapProperties.class}) public class LdapAutoConfiguration { private final LdapProperties properties; private final Environment environment; public LdapAutoConfiguration(LdapProperties properties, Environment environment) { this.properties = properties; this.environment = environment; } @Bean @ConditionalOnMissingBean public ContextSource ldapContextSource() { LdapContextSource source = new LdapContextSource(); source.setUserDn(this.properties.getUsername()); source.setPassword(this.properties.getPassword()); source.setBase(this.properties.getBase()); source.setUrls(this.properties.determineUrls(this.environment)); source.setBaseEnvironmentProperties(Collections.unmodifiableMap(this.properties.getBaseEnvironment())); return source; } }
Onde está o LdapTemplate
? Mas ele não é :). Mais precisamente, é, mas está em outro lugar:
@Configuration @ConditionalOnClass({LdapContext.class, LdapRepository.class}) @AutoConfigureAfter({LdapAutoConfiguration.class}) public class LdapDataAutoConfiguration { @Bean @ConditionalOnMissingBean({LdapOperations.class}) public LdapTemplate ldapTemplate(ContextSource contextSource) { return new LdapTemplate(contextSource); } }
Assim, assumiu-se que você LdapTemplate
obter o LdapTemplate
no seu aplicativo satisfazendo a condição @ConditionalOnClass({LdapContext.class, LdapRepository.class})
, possível quando a dependência de spring-boot-starter-data-ldap
é adicionada ao caminho de classe.
Outra possibilidade: determinar esse feijão com as mãos, o que você não deseja (porque então precisamos do SB). org.springramework.boot:spring-boot-starter-data-ldap
criaram isso depois de substituir org.springramework.boot:spring-boot-starter-data-ldap
por org.springramework.ldap:ldap-core
.
O problema foi resolvido aqui: https://github.com/spring-projects/spring-boot/pull/13136 . Alteração principal: a LdapTemplate
bean LdapTemplate
movida para LdapAutoConfiguration
. Agora você pode usar o LDAP sem vincular ao spring-boot-starter-data-ldap
e definir manualmente a bandeja LdapTemplate
.
Barulho de capuz
Se você usa o Hibernate, provavelmente conhece o antipadrão aberto na tela . Não vamos nos debruçar sobre sua descrição; por referência, ela é descrita em detalhes suficientes e, em outras fontes, seus efeitos nocivos são descritos em grandes detalhes.
Um sinalizador especial é responsável por ativar / desativar este modo:
spring: jpa: open-in-view: true
No SB versão 1. *, ele era ativado por padrão, enquanto o usuário não era informado sobre isso. Nas versões 2. * ele ainda está ativado, mas agora um aviso é gravado no log:
WARN spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning.
Inicialmente, houve uma solicitação para desativar esse modo, o épico srach no tópico contém várias dezenas (!) De comentários detalhados, incluindo de Oliver Görke (desenvolvedor do Spring Date), Vlad Michalce (desenvolvedor do Hibernate), Phil Web e Vedran Pavic (desenvolvedores do Spring) com prós e contras.
Eles concordaram que o comportamento não mudará, mas um aviso será exibido (o que é observado). Há também uma dica bastante comum para desativar esse modo:
spring: jpa: open-in-view: false
Isso é tudo, escreva sobre o seu rake e os recursos interessantes do Conselho de Segurança - este é realmente um tópico inesgotável.