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

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

你可能感兴趣的:(P6Spy源码分析,理解跟踪SQL的工作原理)