首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JBoss JNDI 資料源整合 Spring IOC:完整設定與常見錯誤排查

     編輯:Java 相關知識

JBoss 環境下 JNDI 資料源整合 Spring 完整指南

專案原使用 Apache DBCP 資料源,後因引入第三方報表工具強制要求 JNDI 連線,需在 JBoss 4.2.2 GA + SQL Server 2000 環境下完成遷移。本文記錄完整設定步驟、Spring FactoryBean 實作與經典「首次連線失敗、重整即正常」問題的根因分析與解決方案。

環境規格

  • 作業系統:Windows XP
  • 應用伺服器:JBoss 4.2.2 GA
  • 資料庫:SQL Server 2000 (jTDS 驅動)
  • 架構:Spring + iBATIS + Struts

步驟 1:JBoss 資料源設定 (-ds.xml)

在 server/default/deploy 目錄建立 xxx-ds.xml,JBoss 啟動時自動掃描部署:

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>jdbc/DataSource</jndi-name>
        <connection-url>jdbc:jtds:sqlserver://localhost:1433/fnx</connection-url>
        <driver-class>net.sourceforge.jtds.jdbc.Driver</driver-class>
        <user-name>sa</user-name>
        <password>1234</password>
        <metadata>
            <type-mapping>MS SQLSERVER2000</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

步驟 2:Spring FactoryBean 實作 (支援 JNDI/非 JNDI 切換)

public class DataSourceFactoryBean implements FactoryBean, InitializingBean {

    private DataSource dataSource;
    private boolean jndi;
    private String jndiName;
    private String driverClassName;
    private String url;
    private String username;
    private String password;

    @Override
    public void afterPropertiesSet() throws Exception {
        if (isJndi()) {
            if (!StringUtils.hasText(this.jndiName)) {
                throw new IllegalArgumentException("jndiName is required");
            }
        } else {
            if (!StringUtils.hasText(this.driverClassName)) throw new IllegalArgumentException("driverClassName is required");
            if (!StringUtils.hasText(this.url)) throw new IllegalArgumentException("url is required");
            if (!StringUtils.hasText(this.username)) throw new IllegalArgumentException("username is required");
            if (!StringUtils.hasText(this.password)) throw new IllegalArgumentException("password is required");
        }
        this.createDataSource();
    }

    @Override
    public Object getObject() throws Exception {
        return this.createDataSource();
    }

    @Override
    public Class getObjectType() {
        return javax.sql.DataSource.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    protected DataSource createDataSource() {
        if (this.dataSource == null) {
            try {
                if (this.isJndi()) {
                    this.dataSource = new JndiDataSourceSupport().lookupDataSource(this.jndiName);
                } else {
                    this.dataSource = new DriverManagerDataSource(driverClassName, url, username, password);
                }
            } catch (Throwable err) {
                throw new RuntimeException(err);
            }
        }
        return this.dataSource;
    }

    private class JndiDataSourceSupport extends JndiLocatorSupport {
        public DataSource lookupDataSource(String jndiName) throws NamingException {
            return (DataSource) super.lookup(jndiName, getObjectType());
        }
    }
    // getter/setter 省略
}

步驟 3:Spring 設定檔片段

<bean id="dataSource" class="com.example.dao.DataSourceFactoryBean">
    <property name="jndi" value="${jdbc.IS_JNDI}" />
    <property name="jndiName" value="${jdbc.jndiName}" />
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list><value>classpath:config/jdbc.properties</value></list>
    </property>
</bean>

步驟 4:jdbc.properties 設定

# false=開發環境用傳統 datasource, true=正式環境用 JNDI
jdbc.IS_JNDI=true
jdbc.jndiName=java:jdbc/DataSource

# SQL Server 設定 (開發環境備用)
jdbc.driverClassName=net.sourceforge.jtds.jdbc.Driver
jdbc.url=jdbc:jtds:sqlserver://localhost:1433/fnx
jdbc.username=sa
jdbc.password=1234

關鍵問題:首次連線失敗、重整即正常

錯誤堆疊追蹤

Throwable while attempting to get a new connection: null
org.jboss.resource.JBossResourceException: Could not create connection; 
- nested throwable: (org.jboss.resource.JBossResourceException: Apparently wrong driver class specified for URL: 
class: net.sourceforge.jtds.jdbc.Driver, url: jdbc:jtds:sqlserver://localhost:1433/fnx)

根因分析

jTDS JDBC 驅動 JAR (jtds.jar) 僅放在專案 WEB-INF/lib,未放入 JBoss 容器共用類別庫 JBOSS_HOME/server/default/lib。

問題機制

  1. JBoss 啟動時讀取 -ds.xml 建立資料源,需載入驅動類別
  2. 驅動類別不在容器 Classpath,首次建立連線失敗
  3. Web 應用程式啟動時將 WEB-INF/lib 類別載入容器 Classloader
  4. 重整頁面時驅動已可用,連線成功

解決方案

將 jtds.jar 複製至 JBOSS_HOME/server/default/lib 目錄,重啟 JBoss 即可徹底解決。

最佳實踐總結

  • JNDI 資料源屬容器管理資源,驅動程式必須置於容器共用庫
  • 開發/正式環境透過屬性檔切換資料源策略,避免硬編碼
  • FactoryBean 模式封裝資料源建立邏輯,支援多種資料源類型無縫切換
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved