...他的四个正在调试。
受Vladimir Plizgi的一份报告( Spring Boot 2:他们在发行说明中未写的内容 )的启发,我决定谈论一下我在Spring Booth上的经历,其功能和遇到的陷阱。
春季数据JPA
弗拉基米尔(Vladimir)的报告致力于迁移到Spring Booth 2以及由此带来的困难。 在第一部分中,他描述了编译错误,因此我也将从它们开始。
我认为许多人都熟悉Spring Data框架及其用于各种数据仓库的派生工具。 我只使用其中之一-Spring Data JPA。 此框架中使用的主要接口JpaRepository
在版本2中进行了重大更改。
考虑最常用的方法:
实际上,还有更多更改,并且移至版本2之后。*所有这些都将变成编译错误。
当我们的一个项目提出了迁移到Spring Booth 2的问题并采取了第一步(替换pom.xml中的版本)时,整个项目实际上变成了红色:数十个存储库和对其方法的数百次调用导致了迁移“(使用新方法)将每隔一个钩文件。 想象一下最简单的代码:
SomeEntity something = someRepository.findOne(someId); if (something == null) { something = someRepository.save(new SomeEntity()); } useSomething(something);
为了使项目顺利进行,您需要:
- 调用另一个方法
- 将变量的类型更改为
Optional<SomeEntity>
- 用
Optional::isPresent
或Optional::orElse
/ Optional::orElseGet
替换检查空引用 - 修复测试
幸运的是,有一种简单的方法,因为Spring Date为用户提供了前所未有的机会,可以根据自己的需求定制存储库。 我们需要定义(如果尚未定义)接口和类,这些接口和类将构成我们项目的所有存储库的基础。 这样做是这样的:
我们所有的存储库都将从该接口继承。 现在执行:
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);
剩下的就是形象和肖像。 所有的编译错误都消失了,代码像以前一样工作,只更改了2个类,而不是200个类。现在,随着开发的进行,您可以慢慢地用新的API替换过时的API,因为Idea会小心地将@Deprecated方法的所有调用染成黄色。
最后一招-我们通知Spring从现在开始,存储库应该建立在我们的类之上:
@Configuration @EnableJpaRepositories(repositoryBaseClass = BaseJpaRepositoryImpl.class) public class SomeConfig{ }
在一个平静的游泳池中,可以找到垃圾箱
Spring Booth基于两个概念-启动器和自动配置。 在生活中,这的结果是一个重要的属性-进入应用程序类路径的库将由SB扫描,如果在其中找到包含某个设置的类,则该类将在您不知情和明确指示的情况下打开。 我们用一个简单的例子说明这一点。
许多人使用gson库将对象转换为JSON字符串,反之亦然。 您通常可以看到以下代码:
@Component public class Serializer { public <T> String serialize(T obj) { return new Gson().toJson(obj); } }
频繁访问时,此代码将另外加载垃圾回收器,这是我们不希望的。 特别聪明的人会创建一个对象并使用它:
@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); } }
...甚至没有意识到Spring Booth可以自己做所有事情。 缺省情况下,SB带有org.springframework.boot:spring-boot-autoconfigure
依赖项,其中包括许多名为*AutoConfiguration
类,例如:
@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(); } }
设置很简单:如果应用程序中有Gson
类,并且没有这种类型的手动定义的bean,那么将创建一个默认实现。 这是一种巨大的功能,它允许一个依赖项和几个注释来引发一个完整的配置,该配置的描述过去通常采用XML工作表和许多手写的bin。
但这是安全理事会的极大阴险。 阴险在于保密,因为根据预先编写的脚本,很多工作在后台进行。 这对于典型的应用程序是很好的,但是离开人迹罕至的轨道可能会导致问题-车队射击时没有警告。 在我们不了解的情况下创建了许多bean,Gson的示例很好地说明了这一点。 小心类和您的依赖项,因为...
任何进入您的类路径的东西都可以用来对付您。
人生案例。 有两个应用程序:一个使用LdapTemplate
,另一个使用一次。 这两个应用程序都依赖于core
项目,在该项目中取出了一些通用的类,并且将这两个应用程序的通用库仔细折叠在pom.xml中。
一段时间后,在第二个项目中,不必要地LdapTemplate
对LdapTemplate
所有使用。 但是org.springramework:spring-ldap
库仍然处于core
。 然后SB罢工了, org.springramework.ldap:ldap-core
变成了org.springramework.boot:spring-boot-starter-data-ldap
。
因此,有趣的消息出现在日志中:
Multiple Spring Data modules found, entering strict repository configuration mode! Spring Data LDAP - Could not safely identify store assignment for repository candidate interface ....
对core
领导的org.springramework.boot:spring-boot-starter-data-ldap
依赖org.springramework.boot:spring-boot-starter-data-ldap
适用于完全不使用LDAP的项目。 犹豫是什么意思? 打classpath :)。 进一步的停止:
- Spring Boot注意到
org.springramework.boot:spring-boot-starter-data-ldap
classpath中的org.springramework.boot:spring-boot-starter-data-ldap
- 现在检查每个存储库,以查看它是否适合与Spring Data JPA或Spring Data LDAP一起使用
- 重组依赖关系并
org.springramework.boot:spring-boot-starter-data-ldap
不必要的org.springramework.boot:spring-boot-starter-data-ldap
将应用程序启动时间平均缩短了20(!)秒,总共40-50秒
细心且挑剔的读者可能会问:如果不使用Spring Data LDAP,但仅使用一个,为什么需要将org.springramework.ldap:ldap-core
更改为org.springramework.boot:spring-boot-starter-data-ldap
类org.springframework.ldap.LdapTempate
吗?
答:这是一个错误。 事实是,在2.1.0.M1版本之前,LDAP的自动调整看起来像这样
@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; } }
LdapTemplate
在哪里? 但是他不是:)。 更确切地说,它在但位于其他地方:
@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); } }
因此,假设您LdapTemplate
通过满足@ConditionalOnClass({LdapContext.class, LdapRepository.class})
LdapTemplate
在应用程序中获取LdapTemplate
,这是在将spring-boot-starter-data-ldap
依赖项添加到类路径中时实现的。
另一种可能性:用您的双手确定这个bean,这是您不想要的(因为为什么我们那时需要SB)。 org.springramework.boot:spring-boot-starter-data-ldap
在用org.springramework.ldap:ldap-core
替换了org.springramework.boot:spring-boot-starter-data-ldap
之后提出了这个建议。
问题已在此处解决: https : //github.com/spring-projects/spring-boot/pull/13136 。 主要更改: LdapTemplate
bean LdapTemplate
移至LdapAutoConfiguration
。 现在,您可以使用LDAP,而无需绑定到spring-boot-starter-data-ldap
并手动定义LdapTemplate
bin。
胡德大惊小怪
如果您使用Hibernate,则您可能熟悉“ 打开视图”反模式。 我们将不去赘述它的描述,通过引用对其进行足够详细的描述,并且在其他来源中将对其有害作用进行非常详细的描述。
特殊标志负责打开/关闭此模式:
spring: jpa: open-in-view: true
在SB版本1中。*默认情况下启用此功能,而用户未获悉。 在版本2中。*仍处于启用状态,但是现在将警告写入日志:
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.
最初,有一个禁用此模式的请求, 有关该主题的史诗级内容包含数十个(!)详细注释,包括 来自OliverGörke(Spring Date开发人员),Vlad Michalce(休眠开发人员),Phil Web和Vedran Pavic(Spring开发人员)的优缺点。
他们同意行为不会改变,但是会显示警告(已观察到)。 还有一个相当普遍的技巧来禁用此模式:
spring: jpa: open-in-view: false
仅此而已,请写下您的举止和安全理事会的有趣特征-这确实是一个取之不尽的话题。