关于java static变量初始化的疑问

java中,类的static变量作为类变量,只需要被一次初始化,就可使用,但是,我在程序中遇到一个问题:

在web.xml中配置listener,服务器启动时,ServletContextListen初始化数据源(DataSource),第一次初始化成功,但是在Test类中,调用ConnectionManager.getConnection()时,DataSource却为null,意味着ServletContextListen初始化数据源失败了,大家帮忙解答下,到底什么原因造成的,谢谢!!!

以下是代码:
[code="java"]
package c3p0.connection;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class ConnectionManager {
private static DataSource data_source=null;;
private static Properties props=null;
private static String porps_name=null;

/**
 * 创建数据库连接池
 * @return
 */
private static DataSource createDataSource(){
    load();
    data_source=initDB();
    return data_source;
}

/**
 * 加载配置文件
 */
private static void load(){
    try {
        props =  new Properties();
        InputStream in=Thread.currentThread().getContextClassLoader().getResourceAsStream(porps_name);
        props.load(in);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * 初始化数据库
 */
private static  DataSource initDB(){
    System.out.println("开始初始化数据源...");
    ComboPooledDataSource pool_ds=new ComboPooledDataSource();
    try {
        pool_ds.setDriverClass(getProperty("c3p0.connection.driverClass"));
    } catch (PropertyVetoException e) {
        e.printStackTrace();
        throw new RuntimeException("数据库驱动加载失败");
    }
    pool_ds.setJdbcUrl(getProperty("c3p0.connection.url"));
    pool_ds.setUser(getProperty("c3p0.connection.user"));
    pool_ds.setPassword(getProperty("c3p0.connection.password"));
    pool_ds.setInitialPoolSize(Integer.parseInt(getProperty("c3p0.connection.initialPoolSize")));
    pool_ds.setMaxPoolSize(Integer.parseInt(getProperty("c3p0.connection.maxPoolSize")));
    pool_ds.setMinPoolSize(Integer.parseInt(getProperty("c3p0.connection.minPoolSize")));
    pool_ds.setMaxIdleTime(Integer.parseInt(getProperty("c3p0.connection.maxIdleTime")));
    pool_ds.setAcquireIncrement(Integer.parseInt(getProperty("c3p0.connection.acquireIncrement")));
    pool_ds.setAcquireRetryAttempts(Integer.parseInt(getProperty("c3p0.connection.acquireRetryAttempts")));
    pool_ds.setAcquireRetryDelay(Integer.parseInt(getProperty("c3p0.connection.acquireRetryDelay")));
    pool_ds.setTestConnectionOnCheckout(Boolean.parseBoolean(getProperty("c3p0.connection.testConnectionOnCheckout")));
    pool_ds.setTestConnectionOnCheckin(Boolean.parseBoolean(getProperty("c3p0.connection.testConnectionOnCheckin")));
    pool_ds.setIdleConnectionTestPeriod(Integer.parseInt(getProperty("c3p0.connection.idleConnectionTestPeriod")));
    pool_ds.setCheckoutTimeout(Integer.parseInt(getProperty("c3p0.connection.checkoutTimeout")));
    pool_ds.setAutomaticTestTable(getProperty("c3p0.connection.automaticTestTable"));

    System.out.println("数据源初始化完毕...");
    return pool_ds;
}

/**
 * 获取c3p0连接池文件配置信息
 * @param key
 * @return
 */
private static String getProperty(String key){
    if(key==null||"".equals(key)){
        throw new NullPointerException("key不能为空!!!请检查key的赋值!!!");
    }
    return props.getProperty(key);
}

public static Connection getConnection() throws SQLException{
    data_source=(data_source==null) ? createDataSource() : data_source;
    return data_source.getConnection();
}

public static void start(String porps_name) throws SQLException{
    ConnectionManager.porps_name=porps_name;
    Connection conn=getConnection();
    conn.close();
}

public static void stop(){
    data_source=null;
}

public static void release(Connection conn,PreparedStatement pstmt,ResultSet res){
        try {
            if(conn!=null){
                conn.close();
            }
            if(pstmt!=null){
                pstmt.close();
            }
            if(res!=null){
                res.close();
            }
        } catch (SQLException e) {

            e.printStackTrace();
        }

}

}

[/code]
[code="java"]
package mystruts.listener;

import java.sql.SQLException;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import c3p0.connection.ConnectionManager;

/**

  • ServletContext 上下文监听器
  • 应用服务器一启动就产生该对象,服务器关闭即销毁
  • 作用于全局,所有Servlet ,相当于静态变量
  • @author tianly
    *
    /
    public class ServletContextListen implements ServletContextListener {
    /
    *

    • web服务器关闭时执行 / @Override public void contextDestroyed(ServletContextEvent event) { /*
      • 关闭数据源 */ ConnectionManager.stop(); }

    /**

    • web服务器启动时执行
      */
      @Override
      public void contextInitialized(ServletContextEvent event) {

      /**

      • 初始化数据源 */ String db_url=event.getServletContext().getInitParameter("DataBaseConfig"); try { ConnectionManager.start(db_url); } catch (SQLException e) { e.printStackTrace(); } }

}

[/code]
[code="java"]
package test.c3p0;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import c3p0.connection.ConnectionManager;
import c3p0.util.DateUtil;

public class Test {

/**
 * @param args
 */
public static void main(String[] args) {
    String currdate=DateUtil.getDateTime();
    Connection conn=null;
    PreparedStatement pstmt=null;
    ResultSet res=null;
    try {
        String sql="insert into userinfo(username,password,sex,regdate) values(?,?,?,?)";
        String sql4ID="select LAST_INSERT_ID()";
        conn=ConnectionManager.getConnection();
        conn.setAutoCommit(false);
        pstmt=conn.prepareStatement(sql);
        pstmt.setString(1, "tly");
        pstmt.setString(2, "tly");
        pstmt.setInt(3, 0);
        pstmt.setString(4, currdate);   //mysql中无需进行日期格式转换

        pstmt.executeUpdate();
        pstmt=conn.prepareStatement(sql4ID);
        res=pstmt.executeQuery();
        if(res.next()){
            int ID=res.getInt(1);
            System.out.println("ID:"+ID);
        }
        conn.commit();
    } catch (SQLException e) {
        e.printStackTrace();
        try {
            conn.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }finally{
        ConnectionManager.release(conn, pstmt, res);
    }

}

}

[/code]
[code="xml"]
<?xml version="1.0" encoding="UTF-8"?>

mysystem

index.html
index.htm
index.jsp
default.html
default.htm
default.jsp

<!-- 登录Servlet配置

LoginServlet
login.LoginServlet


LoginServlet
/login
-->


ActionDispatcher
mystruts.actionfilter.ActionDispatcher


ActionDispatcher
*.action

<!-- 数据库信息配置 -->

DataBaseConfig
c3p0/c3p0.properties


mystruts.listener.ServletContextListen


[/code]

在Test类中,调用ConnectionManager.getConnection()时,DataSource却为null,但我在debug时,跟踪查看,发现DataSource是被初始化了,不知什么原因造成初始化失败,请大家帮忙查看下,谢谢

7个回答

没必要放到ServletContextListener。

直接放在ConnectionManager中的static块中,默认加载classpath下的配置文件。

如 static {
load();

data_source=initDB();

return data_source;

}
读取classpath下的资源可以通过
Thread.currentThread().getContextClassLoader().getResourceAsStream(name)

public static void stop(){  
    data_source=null;   //容器销毁了 自动垃圾回收了,不需要
} 

报的什么异常,能否全部贴出来

jinnianshilongnian
jinnianshilongnian
7 年多之前 回复
iteye_6428
iteye_6428 嗯,理解你的意思了,tks...
7 年多之前 回复
jinnianshilongnian
jinnianshilongnian 你通过 main方法是启动了一个新的JVM,,跟web容器的JVM 是两个不同的进程。因此不能互用 你只要确保你是在web容器中使用 就能拿到。 比如在jsp/servlet/action中等都能拿到。 只要是在web容器中就能拿到 现在你是用Test main 启动了一个新进程
7 年多之前 回复
iteye_6428
iteye_6428 那怎么做到我在web容器中初始化的数据源,而在普通的javabean中也能获取到呢?
7 年多之前 回复
jinnianshilongnian
jinnianshilongnian 我的想法是在web服务器启动时,初始化数据源,所以必然要在servletcontext中初始化数据源,而不用静态代码块: 这样也行 因为你配置文件是动态传的
7 年多之前 回复
jinnianshilongnian
jinnianshilongnian 在web容器中肯定成功 因为你是通过 ConnectionManager.start(String porps_name) 启动的 传入 那个配置文件了 而在客户端 你直接getConnection() 是拿不到web容器的东西 是开启了两个JVM 因此你在客户端测试的时候 也需要先通过 ConnectionManager.start(String porps_name) 告诉ConnectionManager 配置文件
7 年多之前 回复
iteye_6428
iteye_6428 我的想法是在web服务器启动时,初始化数据源,所以必然要在servletcontext中初始化数据源,而不用静态代码块,异常就是空指针异常,即在web服务器中初始化数据源成功了,但是在Test中测试时,datasource却为空
7 年多之前 回复
jinnianshilongnian
jinnianshilongnian 你这个可以在web环境/非web环境都能使用 因为你在 public static Connection getConnection() throws SQLException{ data_source=(data_source==null) ? createDataSource() : data_source; return data_source.getConnection(); } 已经判断了
7 年多之前 回复
jinnianshilongnian
jinnianshilongnian 你在java应用程序里边测试 应该调用 ConnectionManager.start(String porps_name) 来说明配置文件是那个 否则报空指针是配置文件造成的
7 年多之前 回复

那是应为他们没有运行在同一个环境中,,服务器启动是在tomcat容器中的,而你自己写的test类 是在外面JDK里面跑的 和 tomcat没有关系,当然是空了

xx753277
远方夕阳 那只能也让这个test类运行在tomcat中你可以 通过浏览器访问一个地址,去调用测试方法
7 年多之前 回复
iteye_6428
iteye_6428 你意思是Test类获取connection时,是没有在tomcat容器中运行,所以获取datasource时,为空么?如果是这样,怎么实现在普通的javabean中获取datasource呢?
7 年多之前 回复
iteye_6428
iteye_6428 那怎么解决才好?
7 年多之前 回复

其它的 我就不多说了 ,开源中国 红薯大哥也是用的 c3p0,同时 他把它的数据库 工具类 已经贡献出来了

[code="java"]package my.db;

import java.sql.*;
import java.util.*;
import java.lang.reflect.*;

import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**

  • 数据库管理
  • @author Winter Lau
  • @date 2010-2-2 下午10:18:50
    */
    public class DBManager {

    private final static Log log = LogFactory.getLog(DBManager.class);
    private final static ThreadLocal conns = new ThreadLocal();
    private static DataSource dataSource;
    private static boolean show_sql = false;

    static {
    initDataSource(null);
    }

    /**

    • 初始化连接池
    • @param props
    • @param show_sql
      */
      private final static void initDataSource(Properties dbProperties) {
      try {
      if(dbProperties == null){
      dbProperties = new Properties();
      dbProperties.load(DBManager.class.getResourceAsStream("db.properties"));
      }
      Properties cp_props = new Properties();
      for(Object key : dbProperties.keySet()) {
      String skey = (String)key;
      if(skey.startsWith("jdbc.")){
      String name = skey.substring(5);
      cp_props.put(name, dbProperties.getProperty(skey));
      if("show_sql".equalsIgnoreCase(name)){
      show_sql = "true".equalsIgnoreCase(dbProperties.getProperty(skey));
      }
      }
      }
      dataSource = (DataSource)Class.forName(cp_props.getProperty("datasource")).newInstance();
      if(dataSource.getClass().getName().indexOf("c3p0")>0){
      //Disable JMX in C3P0
      System.setProperty("com.mchange.v2.c3p0.management.ManagementCoordinator",
      "com.mchange.v2.c3p0.management.NullManagementCoordinator");
      }
      log.info("Using DataSource : " + dataSource.getClass().getName());
      BeanUtils.populate(dataSource, cp_props);

      Connection conn = getConnection();
      DatabaseMetaData mdm = conn.getMetaData();
      log.info("Connected to " + mdm.getDatabaseProductName() + 
                        " " + mdm.getDatabaseProductVersion());
      closeConnection();
      

      } catch (Exception e) {
      throw new DBException(e);
      }
      }

    /**

    • 断开连接池 */ public final static void closeDataSource(){ try { dataSource.getClass().getMethod("close").invoke(dataSource); } catch (NoSuchMethodException e){ } catch (Exception e) { log.error("Unabled to destroy DataSource!!! ", e); } }

    public final static Connection getConnection() throws SQLException {
    Connection conn = conns.get();
    if(conn ==null || conn.isClosed()){
    conn = dataSource.getConnection();
    conns.set(conn);
    }
    return (show_sql && !Proxy.isProxyClass(conn.getClass()))?
    new _DebugConnection(conn).getConnection():conn;
    }

    /**

    • 关闭连接 */ public final static void closeConnection() { Connection conn = conns.get(); try { if(conn != null && !conn.isClosed()){ conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { log.error("Unabled to close connection!!! ", e); } conns.set(null); }

    /**

    • 用于跟踪执行的SQL语句
    • @author Winter Lau
      */
      static class _DebugConnection implements InvocationHandler {

      private final static Log log = LogFactory.getLog(_DebugConnection.class);

      private Connection conn = null;

      public _DebugConnection(Connection conn) {
      this.conn = conn;
      }

      /**

      • Returns the conn.
      • @return Connection */ public Connection getConnection() { return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), this); }

      public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
      try {
      String method = m.getName();
      if("prepareStatement".equals(method) || "createStatement".equals(method))
      log.info("[SQL] >>> " + args[0]);

      return m.invoke(conn, args);
      } catch (InvocationTargetException e) {
      throw e.getTargetException();
      }
      }

    }

}
[/code]

[url]http://www.oschina.net/code/snippet_12_8[/url]

navy0168
navy0168 你是 java application 运行的 代码 估计 连接池都没有初始化吧 ,你试试 放在 servlet 运行这段代码 测试一下 ! 我到建议 ,你改用 红薯的 这个 !
7 年多之前 回复

要看你的tomcat启动了没的,如果没有启动,你的test又没连接数据库,如何会拿到值啊。
你的初始化是tomcat启动后才会去初始化的。

前面的几个哥们也都说了,如果配置在xml中那么你肯定得去启动这个xml才能生效。
如果你想测试你的代码用测试类中mian方法来测试,你首先要去执行xml文件。

楼上说得对。

你建一个Servlet,把那段测试代码放到Servlet里去,再用浏览器调Servlet.

兄弟你太幽默了。。。监听器 是运行在web容器里的。是一个jvm实例。

main方法是另外一个jvm实例。

本质是两个main方法。。能互相调用彼此的运行时变量么。。。

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问