P6Spy源码分析,理解跟踪SQL的工作原理

来源:互联网 发布:matlab2013a mac下载 编辑:程序博客网 时间:2024/05/28 15:22

P6Spy是记录JDBC调用日志信息的一个工具,既然记录了JDBC调用,当然就可以监听到SQL,是开发人员必备的开发利器.可以让开发人员非常方便的知道当前应用程序执行了那些sql

P6Spy官方网站http://www.p6spy.com/index.html

在介绍P6Spy工作原理之前先回忆下传统jdbc的取得连接的方法
Class.forName("oracle.jdbc.driver.OracleDriver");//动态加载oracle.jdbc.driver.OracleDriver类,有关类加载这里就不做介绍了.
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);//加载驱动后就可以取得连接了

多么经典的一段代码啊,通俗的说sun定义了一套jdbc接口,由各个数据库厂商去实现,其中java.sql.Driver是一个核心接口,该接 口核心方法 boolean acceptsURL(String url) ,以OracleDriver为例,oracle.jdbc.driver.OracleDriver类实现了Driver接口,当显示的调用 Class.forName("oracle.jdbc.driver.OracleDriver");时,OracleDriver会被加载,执行 static{}块内的代码,做些初始化工作.当然一个应用往往有时会连接多个数据库,所以衍生了DriverManager类,这个类负责管理多个数据 库驱动,也就是多个driver对象,这也是为什么Driver接口有个方法叫acceptsURL的原因,因为当DriverManager管理多个 driver对象的时候,调用DriverManager.getConnection(URL, USERNAME, PASSWORD);时,DriverManager必须知道当前Url请求的是那个数据库,最直接的实现想法就是循环遍历每个驱动调用驱动的 acceptsURL方法,如果返回true的话,则返回该驱动的连接,所以每个数据库驱动定义的URL格式都不同,因为必须每个驱动都能识别多自己的 URL. 同时DriverManager还提供registerDriver和deregisterDriver方法,前者将驱动实例添加到 DriverManager类的容器中,后者将驱动从容器中删除,就是所谓的注册驱动和反注册驱动.


上面介绍了JDBC的基本工作原理,下面介绍下P6Spy的配置,首先要改驱动,例如把应用中写 com.microsoft.jdbc.sqlserver.SQLServerDriver的地方改为 com.p6spy.engine.spy.P6SpyDriver,无论是连接池还是经典的jdbc.然后在spy.properties驱动中配置 realDriver,这里是真正的驱动com.microsoft.jdbc.sqlserver.SQLServerDriver,然后将 p6spy.jar放到classpath中即可.至于其他输出形式的配置就不介绍了,例如sqlprofiler等.

了解了P6Spy的配置,下面来看P6Spy的源码,首先声明源码已经被我改过了,只保留了最最核心的东西,所以下面的源码和真实的P6Spy源码 有些不同,但核心的方法和思想都是一样的,原来的方法中大量的辅助功能和配置参数判断,代码过长不便于贴出来分析. 由于本人文采不佳所以决定通过在代码上面添加注释的方式来讲解代码.

package com.p6spy.engine.spy;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.ResourceBundle;

public class P6SpyDriver extends P6SpyDriverCore {

    
static {// Class.forName("com.p6spy.engine.spy.P6SpyDriver");当加载这个类时会触发此程序块
        init();
    }

    
/**
     * 初始化过程,本方法原名initMethod,由于父类也有此名称static方法不能重载为了避免引起奇异改为init
     
*/
    
public static void init() {
        List driverNames 
= new ArrayList();// 存放真实驱动的驱动名容器
        try {
            loadDriverNames(driverNames);
// 从配置文件加载真实驱动的驱动名
            if (driverNames == null || driverNames.size() == 0)
                
return;
            P6SpyDriverCore.initMethod(driverNames);
// 核心方法
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    
private static void loadDriverNames(List driverNames) {
        
try {
            ResourceBundle resources 
= ResourceBundle.getBundle("spy-drivers");
            Enumeration keys 
= resources.getKeys();
            
while (keys.hasMoreElements()) {
                driverNames.add(resources.getString(keys.nextElement()
                        .toString().trim()));
            }
        } 
catch (Throwable e) {
            e.printStackTrace();
            driverNames.add(
"oracle.jdbc.driver.OracleDriver");
            driverNames.add(
"com.microsoft.jdbc.sqlserver.SQLServerDriver");
            driverNames.add(
"com.mysql.jdbc.Driver");
            driverNames.add(
"COM.ibm.db2.jdbc.net.DB2Driver");
            driverNames.add(
"com.informix.jdbc.IfxDriver");
            driverNames.add(
"org.hsqldb.jdbcDriver");
        }
    }

 
}
 
package com.p6spy.engine.spy;

import java.sql.*;
import java.util.*;

import com.p6spy.engine.common.*;
import com.p6spy.engine.logging.P6LogFactory;

public abstract class P6SpyDriverCore implements Driver {
    
/**
     * 真正的驱动对象实例
     
*/
    
protected Driver passthru = null;

    
/**
     * P6核心工厂
     
*/
    
protected static P6Factory p6Factory;

    
/**
     * 初始化工作执行完毕的标识
     
*/
    
protected static boolean initialized = false;

    
/**
     * 存放被P6托管的数据库驱动容器
     
*/
    
protected static ArrayList realDrivers = new ArrayList();

    
/**
     * 保证初始化工作的线程安全
     * 
     * 
@param driverNames
     
*/
    
public synchronized static void initMethod(List driverNames) {
        
if (initialized)// 标识初始化工作只执行一次
            return;
        String className 
= null;
        
try {
            
for (int i = 0; i < driverNames.size(); i++) {
                P6SpyDriver spy 
= new P6SpyDriver();// 创建一个P6驱动实例
                DriverManager.registerDriver(spy);// 注册P6驱动实例
                className = (String) driverNames.get(i);
                deregister(className);
// 处理真正的驱动抢在p6前注册的情况,进行反注册
                try {
                    Driver realDriver 
= (Driver) forName(className)
                            .newInstance();
// 加载真正的驱动
                    spy.setPassthru(realDriver);// 将真正的驱动注入到P6驱动中,做为P6驱动对象的一个属性
                    realDrivers.add(realDriver);// 将真正的驱动添加到P6驱动实例共享的容器中
                } catch (ClassNotFoundException e) {
                    DriverManager.deregisterDriver(spy);
// 找不到真正的驱动类时,反注册掉P6驱动实例,因为这个实例不具有存在的价值了.
                    continue;
                }
            }
            p6Factory 
= new P6LogFactory();// 监听到的SQL输出工厂,原来这里是根据配置文件动态加载的,有两种输出模式
            registeredDrivers();// 输出已经注册的驱动信息到控制台
        } catch (Exception e) {
            String err 
= "Error registering  [" + className + "] Caused By: "
                    
+ e.toString();
            P6LogQuery.logError(err);
            
throw new P6DriverNotFoundError(err);
        } 
finally {
            initialized 
= true;
        }

    }

    
/**
     * 打印当前已经注册的驱动
     
*/
    
static void registeredDrivers() {
        
for (Enumeration e = DriverManager.getDrivers(); e.hasMoreElements();) {
            Object dr 
= e.nextElement();
            String msg 
= dr.toString();
            
if (dr instanceof P6SpyDriver) {
                msg 
= " P6SpyDriver =>"
                        
+ ((P6SpyDriver) dr).getPassthru().getClass().getName()
                                .toString();
            }
            P6LogQuery.logDebug(
"Driver manager reporting driver registered: "
                    
+ msg);
        }
    }

    
/**
     * 使用当前的类加载器加载目标类
     * 
     * 
@param name
     * 
@return
     * 
@throws ClassNotFoundException
     
*/
    
static Class forName(String name) throws ClassNotFoundException {
        ClassLoader ctxLoader 
= null;
        
try {
            ctxLoader 
= Thread.currentThread().getContextClassLoader();
            
return Class.forName(name, true, ctxLoader);

        } 
catch (ClassNotFoundException ex) {
            System.out.println(
"警告: ClassNotFound " + name);
        } 
catch (SecurityException ex) {
            ex.printStackTrace();
        }
        
return Class.forName(name);
    }

    
/**
     * 查找抢在p6前注册的驱动进行反注册
     * 
     * 
@param className
     * 
@throws SQLException
     
*/
    
static void deregister(String className) throws SQLException {
        ArrayList dereg 
= new ArrayList();
        
for (Enumeration e = DriverManager.getDrivers(); e.hasMoreElements();) {
            Driver driver 
= (Driver) e.nextElement();

            
if (driver instanceof P6SpyDriver) {
                
break;
            }
            
// now you have to be careful of concurrent update
            
// exceptions here, so save the drivers for later
            
// deregistration
            if (driver.getClass().getName().equals(className)) {
                dereg.add(driver);
            }
        }

        
// if you found any drivers let's dereg them now
        int size = dereg.size();
        
if (size > 0) {
            
for (int i = 0; i < size; i++) {
                Driver driver 
= (Driver) dereg.get(i);
                DriverManager.deregisterDriver(driver);
            }
        }

    }

    
/**
     * 从这里可以明显看到连接被工厂包装了
     * 
     * 
@param realConnection
     * 
@return
     * 
@throws SQLException
     
*/
    
public static Connection wrapConnection(Connection realConnection)
            
throws SQLException {
        
return p6Factory.getConnection(realConnection);
    }

    
public Driver getPassthru() {
        
return passthru;
    }

    
public void setPassthru(Driver inVar) {
        passthru 
= inVar;
    }

    
// the remaining methods are for the Driver interface
    public Connection connect(String realUrl, java.util.Properties p1)
            
throws SQLException {
        
// if there is no url, we have problems
        if (realUrl == null) {
            
throw new SQLException("realURL is null, needs the p6spy prefix: "
                    
+ realUrl);
        }

        
// lets try to find the driver from the multiple divers in
        
// spy.properties
        findPassthru(realUrl);
        
// if we can't find one, it may not be defined
        if (passthru == null) {
            
throw new SQLException("Unable to find a driver that accepts "
                    
+ realUrl);
        }

        P6LogQuery.logDebug(
"this is " + this + " and passthru is " + passthru);
        
if (passthru == null) {
            findPassthru(realUrl);
        }

        Connection conn 
= passthru.connect(realUrl, p1);

        
if (conn != null) {
            conn 
= wrapConnection(conn);
        }
        
return conn;
    }

    
/**
     * 根据实际URL寻找真实驱动
     * 
     * 
@param url
     
*/
    
protected void findPassthru(String url) {
        Iterator i 
= realDrivers.iterator();
        
while (i.hasNext()) {
            Driver driver 
= (Driver) i.next();
            
try {
                
if (driver.acceptsURL(url)) {
                    passthru 
= driver;
                    P6LogQuery.logDebug(
"found new driver " + driver);
                    
break;
                }
            } 
catch (SQLException e) {
            }
        }
    }

    
/**
     * for some reason the passthru is null, go create one
     
*/
    
public boolean acceptsURL(String realUrl) throws SQLException {
        
boolean accepts = false;

        
// somehow we get initilized but no driver is created,
        
// lets try findPassthru
        if (passthru == null && initialized) {
            
// we should have some drivers
            if (realDrivers.size() == 0) {
                
throw new SQLException("P6 has no drivers registered");
            } 
else {
                findPassthru(realUrl);
                
// if we are still null, we have issues
                if (passthru == null)
                    
throw new SQLException(
                            
"P6 can't find a driver to accept url ("
                                    
+ realUrl
                                    
+ ") from the "
                                    
+ realDrivers.size()
                                    
+ " drivers P6 knows about. The current driver is null");
            }
        }

        
if (realUrl != null) {
            accepts 
= passthru.acceptsURL(realUrl);
        }
        
return accepts;
    }

    
public DriverPropertyInfo[] getPropertyInfo(String p0,
            java.util.Properties p1) 
throws SQLException {
        
return (passthru.getPropertyInfo(p0, p1));
    }

    
public int getMajorVersion() {
        
return (passthru.getMajorVersion());
    }

    
public int getMinorVersion() {
        
return (passthru.getMinorVersion());
    }

    
public boolean jdbcCompliant() {
        
return (passthru.jdbcCompliant());
    }

}
其他代码就不一一贴出了,可见p6spy实际上和其他数据库驱动一样实现了jdbc接口,不过p6spy的每个具体实现都是调用了真正的驱动对象的方法, 这部分代码实际上非常简单不贴出来,大家可以自己去官方下源码,如果觉得官方源码复杂的话可以在我的发布的资源里面去下载我自己修改的简版p6spy
原创粉丝点击