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源码 有些不同,但核心的方法和思想都是一样的,原来的方法中大量的辅助功能和配置参数判断,代码过长不便于贴出来分析. 由于本人文采不佳所以决定通过在代码上面添加注释的方式来讲解代码.
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");
}
}
}
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源码分析,理解跟踪SQL的工作原理
- P6Spy源码分析,理解跟踪SQL的工作原理
- P6spy sql跟踪使用
- 为你的 项目安装Sql跟踪工具-P6Spy
- sql跟踪工具——p6spy
- Struts2源码粗略分析四:理解xwork工作原理
- hadoop的mapred工作原理---源码分析
- Struts2的工作原理(源码分析)
- java源码分析--HashMap的工作原理
- 基于ListView的源码分析工作原理
- 我的SQL在哪里--SQL跟踪工具P6Spy介绍(s00n原作)
- HashMap 工作原理源码分析
- caffe源码解析—caffe layer的工作原理理解
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- Android源码分析—属性动画的工作原理
- 蔡军生先生第二人生的源码分析(四十二)实现消息处理的线程类
- html控件透明与背景透明
- 蔡军生先生第二人生的源码分析(四十三)虚拟文件系统线程
- volatile的作用
- 常用数据库的链接方法
- P6Spy源码分析,理解跟踪SQL的工作原理
- 如何让iframe的背景透明
- Tomcat中使用数据源连接mysql数据库
- Web程序中对资源文件的缓存
- 写文件
- webloic resin tomcat 的远程调试的参数
- Jsp下载文件 一段代码
- 蔡军生先生第二人生的源码分析(四十四)虚拟文件系统的请求处理
- 蔡军生先生第二人生的源码分析(四十五)图像解压线程