Spring 引导 2 - 连接两个 LDAP 模板

Spring Boot 2 - Wire Two LDAP Templates

我需要在我的 Spring Boot 2 应用程序中配置多个 LDAP 数据源/LdapTemplates。第一个 LdapTemplate 将用于大部分工作,而第二个将用于偶尔的数据子集(位于其他地方)。

我已经阅读了这些关于这样做的 Whosebug 问题,但它们似乎是针对 Spring Boot 1 的。
Can a spring ldap repository project access two different ldap directories?

据我所知,其中大部分 configuration/setup 无论如何都必须完成,即使只有一个 LDAP 数据源,回到 Spring Boot 1。使用 Spring Boot 2,我只是像这样把属性放在我的配置文件中

ldap.url=ldap://server.domain.com:389
ldap.base:DC=domain,DC=com
ldap.username:domain\ldap.svc.acct
ldap.password:secret

并像这样在我的存储库中自动装配模板

@Autowired
private final LdapTemplate ldapTemplate;

我准备好了。 (参见:)

对于第二个 LDAP 数据源,我是否可以只为“ldap2”添加属性和配置元素并完成(参见链接的问题)?或者添加此配置是否会导致 Spring Boot 2 的自动配置认为我正在覆盖它,所以现在我失去了我的第一个 LdapTemplate,这意味着我现在也需要明确配置它?
如果是这样,我是否需要配置所有内容,还是只需要部分配置?例如,如果我添加上下文源配置并将其标记为 @Primary(这适用于 LDAP 数据源吗?),我是否可以跳过将其显式分配给第一个 LdapTemplate?在相关说明中,我是否仍需要添加 @EnableLdapRepositories 注释,否则由 Spring Boot 2 自动配置?

TLDR:我需要在 Spring Boot 2 中添加的最低配置是什么以连接第二个 LdapTemplate?

这采用了我在周末学到的知识,并将其用作我自己问题的答案。我仍然不是这方面的专家,所以我欢迎更有经验的回答或评论。

解释

首先,我仍然不确定是否需要 @EnableLdapRepositories 注释。我还没有使用这些功能,所以我不能说它是否重要,或者 Spring Boot 2 是否仍在自动处理。我怀疑 Spring Boot 2 是,但我不确定。

其次,Spring Boot 的自动配置都发生在任何用户配置之后,例如我的代码配置第二个 LDAP 数据源。根据上下文源或 LdapTemplate 的存在,自动配置使用几个条件注释来判断它是否 运行s。
这意味着它看到了我的“第二个”LDAP 上下文源(条件是上下文源 bean 存在,不管它的名称是什么或它使用的是什么属性)并跳过创建它自己,这意味着我不再配置我的那部分主要数据源。
它还会看到我的“第二个”LdapTemplate(同样,条件只是一个 LdapTemplate bean 存在,不管它的名称是什么或者它使用的是什么上下文源或属性)并跳过创建它自己,所以我再次不再配置我的主要数据源。
不幸的是,这些条件意味着在这种情况下也没有中间(例如,我可以手动配置上下文源,然后允许 LdapTemplate 的自动配置仍然发生)。所以解决方案是要么在自动配置之后进行我的配置 运行,要么根本不利用自动配置并自己设置它们。

至于在自动配置之后进行配置 运行:唯一的方法是使我的配置本身成为自动配置,并指定其顺序在 Spring 的内置之后自动配置(参见:)。这不适合我的用例,所以对于我的情况(因为 Spring Boot 的设置对于标准的单源情况确实有意义)我坚持放弃自动配置并自己设置它们。

代码

如我的问题中所链接,以下两个答案很好地涵盖了设置两个数据源(尽管部分是出于其他原因),但我也会在这里详细说明我的设置。
Can a spring ldap repository project access two different ldap directories?

首先,需要创建配置 class,因为以前 Spring Boot 2 根本不需要配置。同样,我部分省略了 @EnableLdapRepositories 注释因为我还没有使用它,部分原因是我认为 Spring Boot 2 仍然会为我解决这个问题。 (注意:所有这些代码都是在 Stack Overflow 答案框中输入的,因为我没有编写此代码的开发环境,因此跳过了导入,并且代码可能无法完美编译和正常运行,尽管我希望一切顺利。)

@Configuration
public class LdapConfiguration {
}

其次是手动配置主数据源;曾经自动配置但不再自动配置的那个。这里有一个 Spring Boot 的自动配置可以利用,那就是它读入标准的 spring.ldap.* 属性(到属性对象中),但是因为它没有被赋予名称,您必须通过其完全限定的 class 名称来引用它。这意味着您可以直接跳到为主数据源设置上下文源。此代码不如实际的自动配置代码功能齐全(参见:Spring Code) 我将此 LdapTemplate 标记为 @Primary,因为对于我的使用而言,这是主要数据源,因此所有其他自动装配调用都应默认为它。这也意味着您不需要 @Qualifier 来自动连接此源(稍后会看到)。

@Configuration
public class LdapConfiguration {
    @Bean(name="contextSource")
    public LdapContextSource ldapContextSource(@Qualifier("spring.ldap-org.springframework.boot.autoconfigure.ldap.LdapProperties") LdapProperties properties) {
        LdapContextSource source = new LdapContextSource();
        source.setUrls(properties.getUrls());
        source.setUserDn(properties.getUsername());
        source.setPassword(properties.getPassword());
        source.setBaseEnvironmentProperties(Collections.unmodifiableMap(properties.getBaseEnvironment()));
        
        return source;
    }
    
    @Bean(name="ldapTemplate")
    @Primary
    public LdapTemplate ldapTemplate(@Qualifier("contextSource") LdapContextSource source) {
        return new LdapTemplate(source);
    }
}

第三种是手动配置辅助数据源,即导致所有这一切的原因。对于这个,您确实需要配置将属性读取到 LdapProperties 对象中。这段代码建立在前面的代码之上,因此您可以看到完整的 class 上下文。

@Configuration
public class LdapConfiguration {
    @Bean(name="contextSource")
    public LdapContextSource ldapContextSource(@Qualifier("spring.ldap-org.springframework.boot.autoconfigure.ldap.LdapProperties") LdapProperties properties) {
        LdapContextSource source = new LdapContextSource();
        source.setUrls(properties.getUrls());
        source.setUserDn(properties.getUsername());
        source.setPassword(properties.getPassword());
        source.setBaseEnvironmentProperties(Collections.unmodifiableMap(properties.getBaseEnvironment()));
        
        return source;
    }
    
    @Bean(name="ldapTemplate")
    @Primary
    public LdapTemplate ldapTemplate(@Qualifier("contextSource") LdapContextSource source) {
        return new LdapTemplate(source);
    }
    
    
    
    @Bean(name="ldapProperties2")
    @ConfigurationProperties("app.ldap2")
    public LdapProperties ldapProperties2() {
        return new LdapProperties();
    }
    
    @Bean(name="contextSource2")
    public LdapContextSource ldapContextSource2(@Qualifier("ldapProperties2") LdapProperties properties) {
        LdapContextSource source = new LdapContextSource();
        source.setUrls(properties.getUrls());
        source.setUserDn(properties.getUsername());
        source.setPassword(properties.getPassword());
        source.setBaseEnvironmentProperties(Collections.unmodifiableMap(properties.getBaseEnvironment()));
        
        return source;
    }
    
    @Bean(name="ldapTemplate2")
    public LdapTemplate ldapTemplate2(@Qualifier("contextSource2") LdapContextSource source) {
        return new LdapTemplate(source);
    }
}

最后,在使用这些 LdapTemplate 的 class 中,您可以正常自动装配它们。这使用构造函数自动装配而不是使用其他两个答案的字段自动装配。尽管建议使用构造函数自动装配,但两者在技术上都是有效的。

@Component
public class LdapProcessing {
    protected LdapTemplate ldapTemplate;
    protected LdapTemplate ldapTemplate2;
    
    @Autowired
    public LdapProcessing(LdapTemplate ldapTemplate, @Qualifier("ldapTemplate2") LdapTemplate ldapTemplate2) {
        this.ldapTemplate = ldapTemplate;
        this.ldapTemplate2 = ldapTemplate2;
    }
}

TLDR:定义“第二个”LDAP 数据源会停止第一个 LDAP 数据源的自动配置,因此如果使用多个,则必须(几乎完全)手动配置两者; Spring 的自动配置甚至不能用于第一个 LDAP 数据源。