You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过@Autowired方法自定义@ConstructorBinding修饰的@ConfigurationProperties实例

现有实现存在的问题
  • 配置硬编码:你在构造LdapProperties时写死了所有配置参数,原本application.properties中jmix.ldap前缀的配置会完全失效,后续调整ldap地址、密码等参数必须修改代码重新打包,灵活性极差。
  • 初始化时机错误:standardAuthenticationUsers在类属性初始化阶段就调用getUserList()查询数据库,此时EntityManagerFactory大概率还未完成初始化,极易触发空指针或数据库连接未就绪异常;且列表仅在类初始化时加载一次,后续数据库中用户数据更新后列表无法同步,不满足动态需求。
  • 配置不一致:仅修改了当前Mapper实例持有的LdapProperties对象,Spring容器中其他注入LdapProperties的 Bean 依然会使用配置文件加载的原始实例,会出现全局配置不一致的问题。
  • 资源未释放:查询数据库的EntityManager用完未主动关闭,可能造成连接泄漏。
更规范的实现方案

方案1:全局覆盖LdapProperties Bean(推荐)

直接注册自定义的LdapProperties Bean覆盖框架默认生成的实例,全容器所有注入点共享同一份配置,同时保留原有配置文件的所有配置项,仅替换需要动态生成的standardAuthenticationUsers属性。
代码示例:

import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.ConfigurationPropertiesBinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import java.util.List;

@Configuration
public class CustomLdapConfig {

    private final EntityManagerFactory emf;

    public CustomLdapConfig(EntityManagerFactory emf) {
        this.emf = emf;
    }

    @Bean
    @Primary
    public LdapProperties ldapProperties(ConfigurationPropertiesBinder binder) {
        // 绑定配置文件中原有jmix.ldap前缀的所有配置
        BindResult<LdapProperties> bindResult = binder.bind("jmix.ldap", Bindable.of(LdapProperties.class));
        LdapProperties originalProps = bindResult.get();

        // 查询数据库获取动态用户列表
        List<String> dynamicUsers;
        try (EntityManager em = emf.createEntityManager()) {
            TypedQuery<String> query = em.createQuery(
                    "select u.username from User u where u.password is not null", 
                    String.class
            );
            dynamicUsers = query.getResultList();
        }

        // 复用原有配置,仅替换用户列表字段
        return new LdapProperties(
                originalProps.isEnabled(),
                originalProps.getUserDetailsSource(),
                originalProps.getUrls(),
                originalProps.getBaseDn(),
                originalProps.getManagerDn(),
                originalProps.getManagerPassword(),
                originalProps.getUserSearchBase(),
                originalProps.getUserSearchFilter(),
                originalProps.getUsernameAttribute(),
                originalProps.getMemberAttribute(),
                originalProps.getGroupRoleAttribute(),
                originalProps.getGroupSearchBase(),
                originalProps.isGroupSearchSubtree(),
                originalProps.getGroupSearchFilter(),
                originalProps.getUseActiveDirectoryConfiguration(),
                originalProps.getActiveDirectoryDomain(),
                originalProps.getGroupForSynchronization(),
                originalProps.getSynchronizeRoleAssignments(),
                originalProps.getSynchronizeUserOnLogin(),
                originalProps.getDefaultRoles(),
                dynamicUsers
        );
    }
}

如果需要支持运行时动态刷新用户列表,可给该Bean加上@RefreshScope注解,配合Spring刷新机制可实现无需重启更新配置。

方案2:仅修改当前Mapper持有的配置(适合不需要全局生效的场景)

如果不需要全局修改配置,仅需要在当前Mapper中使用自定义的用户列表,可修改原有代码复用传入的原始配置,同时调整数据库查询时机:

import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import java.util.stream.Collectors;

@Component("sample_MyUserSynchronizationStrategy")
public class UserDetailsServiceLdapUserDetailsMapperImpl extends UserDetailsServiceLdapUserDetailsMapper {
    private final EntityManagerFactory emf;

    public UserDetailsServiceLdapUserDetailsMapperImpl(EntityManagerFactory emf) {
        this.emf = emf;
    }

    @Override
    public void setLdapProperties(LdapProperties ldapProperties) {
        // 执行数据库查询获取动态用户列表
        List<String> standardAuthenticationUsers;
        try (EntityManager em = emf.createEntityManager()) {
            TypedQuery<User> query = em.createQuery(
                    "select u from User u where u.password is not null", 
                    User.class
            );
            standardAuthenticationUsers = query.getResultList()
                    .stream()
                    .map(User::getUsername)
                    .collect(Collectors.toList());
        }

        // 复用原始配置的所有参数,仅替换用户列表
        super.setLdapProperties(new LdapProperties(
                ldapProperties.isEnabled(),
                ldapProperties.getUserDetailsSource(),
                ldapProperties.getUrls(),
                ldapProperties.getBaseDn(),
                ldapProperties.getManagerDn(),
                ldapProperties.getManagerPassword(),
                ldapProperties.getUserSearchBase(),
                ldapProperties.getUserSearchFilter(),
                ldapProperties.getUsernameAttribute(),
                ldapProperties.getMemberAttribute(),
                ldapProperties.getGroupRoleAttribute(),
                ldapProperties.getGroupSearchBase(),
                ldapProperties.isGroupSearchSubtree(),
                ldapProperties.getGroupSearchFilter(),
                ldapProperties.getUseActiveDirectoryConfiguration(),
                ldapProperties.getActiveDirectoryDomain(),
                ldapProperties.getGroupForSynchronization(),
                ldapProperties.getSynchronizeRoleAssignments(),
                ldapProperties.getSynchronizeUserOnLogin(),
                ldapProperties.getDefaultRoles(),
                standardAuthenticationUsers
        ));
    }
}

内容的提问来源于stack exchange,提问作者Marcin Böhm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:36:03