幾天前遇到的這個問題。由於交易是配置的,不同的交易是同一個類的不同實例,所以不可能提前將其以@autowired類似的方式注入到需要的類中
<op:transaction id="Recharge" template="TransTemplate"></op:transaction> <op:transaction id="QueryAgreementAcct" template="TransTemplate"></op:transaction>
只能寫一個工具類,實現ApplicationContextAware,取得容器,然后收到交易報文的時候,根據報文里的交易名,去取得容器中對應的transaction bean。然后根據bean的template等等信息,往下執行。
但是當我在做這件事情的時候,遇到一個奇怪的問題。這個問題我在cnblog上問過(https://q.cnblogs.com/q/95168/)。問了之后這段時間在准備一場面試,唉唉,失敗了。所以今天才回過頭來看這個問題:
public class TcpServer implements ApplicationContextAware{ private OpenbankExecutor executor; private int port; private PacketHandler packetHandler; private ServerSocket serverSocket; private Socket socket; private ApplicationContext applicationContext; private final Logger log = LoggerFactory.getLogger(getClass()); public void init() throws IOException { serverSocket = new ServerSocket(port); log.debug("TcpServer 成功啟動"); while(true) { socket = serverSocket.accept(); System.out.println("收到socket請求"); executor.execute(new Runnable() { @Override public void run() { try { Map context = packetHandler.handle(socket.getInputStream()); String tranCode = (String) context.get("tranCode"); Assert.hasText(tranCode); Transaction transaction = (Transaction) applicationContext.getBean(tranCode); Template template = (Template) applicationContext.getBean(transaction.getTemplate()); log.debug("transaction: "+transaction.getId()); log.debug("transaction: "+template.getId()); } catch (Exception e) { e.printStackTrace(); } } }); } }
這個TcpServer 類也是Spring容器管理的:
<bean id="tcpServer" class="com.openbank.portal.server.TcpServer" init-method="init"> <property name="executor" ref="openbankThreadPool"/> <property name="port" value="${tcp.port}"/> <property name="packetHandler" ref="xmlpacketHandler"/> </bean>
結果我測了一下,收到交易報文之后,走到上面代碼紅色的地方就“卡”住了。就像程序執行完了一樣,但是沒有打印出后面的debug信息,糾結了一段時間沒有搞懂為什么。
今天我把spring的源代碼下載下來了,DEBUG了一下,發現好像是鎖的問題,因為走到了一個synchronized方法就沒有后文了,可見是一直沒有獲取到鎖
然后我又寫了一些測試,把Spring啟動期間所有被實例化的Singleton全部打印出來看,最后才慢慢搞明白
原來就是線程的問題。
因為啊因為啊,我上面代碼里面可以看到,socket = serverSocket.accept();
這一段,是直接在TcpServer被初始化時就要運行的,但是這里阻塞的,一直等待報文的到來。就是這樣,導致spring加載bean的過程,加載到這個bean也就卡住了,甚至后面還有bean根本沒有機會得到實例化。
解決方式很簡單,我把socket = serverSocket.accept(); 這個放到一個新開的線程里面取處理就好了。