如何在运行时更新Spring Bean以连接多台SAP服务器?
实现Spring环境下Hibersap多SAP服务器动态切换连接
问题背景
我需要通过Session Manager连接SAP服务器,当前使用Spring Bean创建Session Manager,配置信息来自hibersap.xml。此前仅连接单台服务器,现在需要连接名为BHD和AHD的两台及以上服务器,但不清楚如何在运行时动态切换配置,实现连接不同服务器的需求。
现有配置文件与代码
hibersap.xml配置
<session-manager name="BHD"> <context>org.hibersap.execution.jco.JCoContext</context> <properties> <property name="jco.client.client" value="111" /> <property name="jco.client.user" value="AAA" /> <property name="jco.client.passwd" value="11Q1Q1Q1Q" /> <property name="jco.client.ashost" value="11.11.11.11" /> <property name="jco.client.sysnr" value="00" /> </properties> <annotated-classes> <annotated-class>com.ldap.connection.sap.bapi.RfcLdapParameters</annotated-class> <annotated-class>com.ldap.connection.sap.bapi.RfcSapUserDetails</annotated-class> <annotated-class>com.ldap.connection.sap.bapi.UsernameAndMailModel</annotated-class> </annotated-classes> </session-manager> <session-manager name="AHD"> <context>org.hibersap.execution.jco.JCoContext</context> <properties> <property name="jco.client.client" value="222" /> <property name="jco.client.user" value="BBB" /> <property name="jco.client.passwd" value="2Q2Q2Q2Q22" /> <property name="jco.client.ashost" value="22.22.22.22" /> <property name="jco.client.sysnr" value="00" /> </properties> <annotated-classes> <annotated-class>com.ldap.connection.sap.bapi.RfcLdapParameters</annotated-class> <annotated-class>com.ldap.connection.sap.bapi.RfcSapUserDetails</annotated-class> <annotated-class>com.ldap.connection.sap.bapi.UsernameAndMailModel</annotated-class> </annotated-classes> </session-manager>
当前Spring配置类
@Configuration @Slf4j public class HibersapConfiguration { @Bean public SessionManager sessionManager() { String sname = System.getProperty("java.systemname"); AnnotationConfiguration configuration = null; if(sname != null) { if(BtcConstants.LIVE_SYSTEM_NAME.equals(sname)) { log.info("SAP üretim canlı sisteme bağlanılıyor..."); } if(BtcConstants.DEV_SYSTEM_NAME.equals(sname)){ configuration = new AnnotationConfiguration("BHD"); log.info("SAP üretim dev sisteme bağlanılıyor..."); } }else { configuration = new AnnotationConfiguration("BHD"); log.info("Local'den SAP üretim dev sisteme bağlanılıyor..."); } return configuration.buildSessionManager(); } }
仓库类代码
@Slf4j @Repository public class SapUserRepository implements SapUserRepositoryI { private final SessionManager sessionManager; public SapUserRepository(SessionManager sessionManager) { this.sessionManager = sessionManager; } @Override public List<SapUser> findPersonalDetailFromSap(Date currentDate, String pernr) { List<SapUser> sapUserList = new ArrayList<SapUser>(); RfcSapUserDetails details = new RfcSapUserDetails(); details.setCurrentDate(currentDate); details.setPernr(pernr); Session session = null; if(sessionManager != null) { log.info("'findPersonalDetailFromSap' için session açıldı"); session = sessionManager.openSession(); try { log.info("Personel verileri çekiliyor..."); session.execute(details); sapUserList = details.getSapUserList(); } finally { session.close(); log.info("session kapatıldı"); } }else { log.error("sessionManager null geldi !"); } return sapUserList; }
解决方案
方案一:注册多个SessionManager Bean,按名称动态获取
这种方式适合服务器数量固定的场景,直接为每个SAP服务器创建独立的SessionManager Bean,使用时通过名称获取对应实例。
修改Spring配置类
@Configuration @Slf4j public class HibersapConfiguration { @Bean(name = "bhdSessionManager") public SessionManager bhdSessionManager() { AnnotationConfiguration configuration = new AnnotationConfiguration("BHD"); log.info("初始化BHD SAP服务器连接..."); return configuration.buildSessionManager(); } @Bean(name = "ahdSessionManager") public SessionManager ahdSessionManager() { AnnotationConfiguration configuration = new AnnotationConfiguration("AHD"); log.info("初始化AHD SAP服务器连接..."); return configuration.buildSessionManager(); } }
修改仓库类,动态获取对应SessionManager
通过注入ApplicationContext,根据业务传入的服务器名称获取对应Bean:
@Slf4j @Repository public class SapUserRepository implements SapUserRepositoryI { private final ApplicationContext applicationContext; public SapUserRepository(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public List<SapUser> findPersonalDetailFromSap(Date currentDate, String pernr, String serverName) { // 拼接Bean名称,比如传入"BHD"则获取"bhdSessionManager" String beanName = serverName.toLowerCase() + "SessionManager"; SessionManager sessionManager = applicationContext.getBean(beanName, SessionManager.class); List<SapUser> sapUserList = new ArrayList<SapUser>(); RfcSapUserDetails details = new RfcSapUserDetails(); details.setCurrentDate(currentDate); details.setPernr(pernr); Session session = null; if(sessionManager != null) { log.info("'findPersonalDetailFromSap' için session açıldı"); session = sessionManager.openSession(); try { log.info("Personel verileri çekiliyor..."); session.execute(details); sapUserList = details.getSapUserList(); } finally { session.close(); log.info("session kapatıldı"); } }else { log.error("sessionManager null geldi !"); } return sapUserList; } }
方案二:实现SessionManager工厂类,按需创建缓存实例
这种方式更灵活,适合服务器数量不固定或需要动态加载配置的场景,通过工厂类统一管理SessionManager实例,避免重复创建。
创建SessionManager工厂类
@Component public class SessionManagerFactory { // 缓存已创建的SessionManager,避免重复初始化 private final Map<String, SessionManager> sessionManagerCache = new ConcurrentHashMap<>(); public SessionManager getSessionManager(String serverName) { // 不存在则创建,存在则直接返回缓存实例 return sessionManagerCache.computeIfAbsent(serverName, this::buildSessionManager); } private SessionManager buildSessionManager(String serverName) { AnnotationConfiguration configuration = new AnnotationConfiguration(serverName); log.info("初始化{} SAP服务器连接...", serverName); return configuration.buildSessionManager(); } // 可选:添加刷新缓存的方法,用于配置更新时重新创建实例 public void refreshSessionManager(String serverName) { sessionManagerCache.remove(serverName); getSessionManager(serverName); } }
修改仓库类,注入工厂类
@Slf4j @Repository public class SapUserRepository implements SapUserRepositoryI { private final SessionManagerFactory sessionManagerFactory; public SapUserRepository(SessionManagerFactory sessionManagerFactory) { this.sessionManagerFactory = sessionManagerFactory; } @Override public List<SapUser> findPersonalDetailFromSap(Date currentDate, String pernr, String serverName) { SessionManager sessionManager = sessionManagerFactory.getSessionManager(serverName); List<SapUser> sapUserList = new ArrayList<SapUser>(); RfcSapUserDetails details = new RfcSapUserDetails(); details.setCurrentDate(currentDate); details.setPernr(pernr); Session session = null; if(sessionManager != null) { log.info("'findPersonalDetailFromSap' için session açıldı"); session = sessionManager.openSession(); try { log.info("Personel verileri çekiliyor..."); session.execute(details); sapUserList = details.getSapUserList(); } finally { session.close(); log.info("session kapatıldı"); } }else { log.error("sessionManager null geldi !"); } return sapUserList; } }
注意事项
- SessionManager实例是线程安全的,因此可以安全缓存复用,无需每次请求都创建新实例。
- 如果SAP服务器配置可能动态变更,可在工厂类中添加缓存刷新方法,触发重新初始化SessionManager。
- 根据业务场景选择合适的方案:固定服务器数量用方案一,动态扩展用方案二。
内容的提问来源于stack exchange,提问作者Cemal
相关产品推荐
相关产品推荐

