通过@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
相关产品推荐
相关产品推荐

