本文链接
<?php // id: ecffe70d3af54df9bad97b61918ace7d
global $ct_path, $ct_log_path;
$log_path = "test_php.txt";
// 是否先log到buffer,再通过CT_flush()一次性写入文件
$ct_log_buffer = true;
$CT_off = true;
$request_num = uniqid();
$CT_format = ""; if ($ct_path) {
$dir = dirname($ct_path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
$file = fopen($ct_path, "a+");
$file_ct_log = $file;
}
if ($ct_log_path) {
$dir = dirname($ct_log_path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
$file_ct_log = fopen($ct_log_path, "a+");
} $ct_buffer = []; $path_my = __DIR__ . "/common.my.php";
if (is_file($path_my)) {
require $path_my;
} function clear_log() {
Global $log_path;
unlink($log_path);
} function clog($content, $with_lf = true) {
Global $log_path;
$file = fopen($log_path,"a+");
fwrite($file, $content);
CT_log($content);
if($with_lf) {
fwrite($file, "\n");
CT_log("\n");
}
fclose($file); } function get_safe($obj, $key, $def = NULL) {
if(isset($obj[$key])) {
return $obj[$key];
}
return $def;
} function get_stack_trace($title = "") {
$html = "=================stack trace:".$title."\n";
$array =debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($array as $row) {
$html .= sprintf("file:%s, line:%d, class:%s, function:%s\n",
get_safe($row, 'file'), get_safe($row, 'line'), get_safe($row, 'class'), get_safe($row, 'function'));
}
return $html;
} function log_stack_trace($title = "") {
clog(get_stack_trace($title));
} // error handler function with stack trace.
// use like this:
// $old_error_handler = set_error_handler("err_handler");
function err_handler($errno, $errstr, $errfile, $errline)
{
$errno_map = array(1 => "E_ERROR", 2 => "E_WARNING", 4 => "E_PARSE", 8 => "E_NOTICE",
16 => "E_CORE_ERROR", 32 => "E_CORE_WARNING", 64 => "E_COMPILE_ERROR",
128 => "E_COMPILE_WARNING", 256 => "E_USER_ERROR", 512 => "E_USER_WARNING",
1024 => "E_USER_NOTICE", 2048 => "E_STRICT", 4096 => "E_RECOVERABLE_ERROR",
8192 => "E_DEPRECATED", 16384 => "E_USER_DEPRECATED", 32767 => "E_ALL");
clog(sprintf("------------ %s(%d), msg:%s", $errno_map[$errno], $errno, $errstr));
log_stack_trace("");
/* Don't execute PHP internal error handler */
return true;
} // 获取当前系统时间,返回float格式,单位:秒
function get_time() {
date_default_timezone_set('Asia/Shanghai');
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
} function get_prefix() {
return "";
Global $ip, $pid;
if(!isset($ip)) {
$pid = getmypid();
$ip = $_SERVER['REMOTE_ADDR'];
}
return $pid.' '.$ip.' '.date("m-d H:i:s ");
} function CT($content) {
Global $CT_off, $file;
if($CT_off || !$file)
return; Global $last_time, $first_time, $is_first, $ct_log_buffer, $ct_buffer, $request_num, $CT_format;
if ($CT_format == "raw") {
$all_out = $content . "\n";
} else {
// 通过stack trace计算缩进
$array =debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$ignore_count = 0;
$count = count($array);
$ignore_names = ["call_user_func_array", "call_user_func", "spl_autoload_call"];
$ignore_classes = ["ReflectionClass"];
for ($i = 2; $i < $count; $i++) {
$frame = $array[$i];
if (in_array($frame["function"], $ignore_names) || isset($frame["class"]) && in_array($frame["class"], $ignore_classes)) {
$ignore_count++;
}
}
$all_out = get_prefix() . str_pad("", $count - 2 - $ignore_count, " ") . $request_num . " " . $content;
$cur_time=get_time();
if(!$is_first) {
$is_first = true;
$last_time = $first_time = $cur_time;
}
$total_time=$cur_time-$first_time;
$delta_time=$cur_time-$last_time;
$overtime_flag = "";
// 添加超时标记
if($delta_time * 1000 > 10)
$overtime_flag = "----overtime"; $all_out = $all_out." cur_time: $cur_time, total_time: $total_time, delta_time: $delta_time $overtime_flag\n";
$last_time=$cur_time;
} if ($ct_log_buffer === true) {
$ct_buffer[] = $all_out;
} else {
fwrite($file, $all_out);
}
} /**
* 将buffer的CT内容写入文件
*
@param boolean turn_off_buffer, 完成后是否关闭buffer,以保证通过register_shutdown_function等调用的函数能够被输出
*/
function CT_flush($turn_off_buffer)
{
global $file, $ct_buffer, $ct_log_buffer;
if (!$file) {
return;
}
fwrite($file, join("", $ct_buffer));
$ct_buffer = [];
$ct_log_buffer = !$turn_off_buffer;
} function CT_log($content = "", $path = NULL) {
Global $file_ct_log;
if (!$file_ct_log && !$path) {
return;
}
$content = get_prefix().print_r($content, true)."\n";
if($path) {
file_create_path($path);
$file = fopen($path, "a+");
if($file) {
fwrite($file, $content);
fclose($file);
}
}
else {
fwrite($file_ct_log, $content);
}
} /**
* 日志输出,使用info level
*
@param $content
*
@param array $params
*
@param string $logger
*/
function slog($content, $params = [], $logger = "default")
{
SeasLog::info($content, $params, $logger);
} /**
* 日志输出,使用debug level
*
@param $content
*
@param array $params
*
@param string $logger
*/
function slog_debug($content, $params = [], $logger = "default")
{
SeasLog::debug($content, $params, $logger);
} /**
* 日志输出,使用error level
*
@param $content
*
@param array $params
*
@param string $logger
*/
function slog_error($content, $params = [], $logger = "default")
{
SeasLog::error($content, $params, $logger);
} /**
* 日志输出,使用warning level
*
@param $content
*
@param array $params
*
@param string $logger
*/
function slog_warning($content, $params = [], $logger = "default")
{
SeasLog::warning($content, $params, $logger);
} /*
获取指定的http response header 值。
eg:
HTTP/1.1 200 OK
Server: Tengine/2.1.2
Date: Sun, 02 Apr 2017 02:49:34 GMT
Content-Type: text/html; charset=gb2312
Content-Length: 124378
Connection: keep-alive
Cache-Control: private
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
*/
function curl_get_header($ch, $response, $key)
{
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
// header processing
$header_arr = explode("\r\n", $header); $value = "";
$key .= ":";
foreach ($header_arr as $entry) {
if (!strncmp($entry, $key, strlen($key))) {
$value = trim(substr($entry, strlen($key)));
break;
}
}
return $value;
} // 获取response状态码
function curl_get_status($ch, $response)
{
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$header_arr = explode("\r\n", $header);
return explode(" ", $header_arr[0])[1];
} function startsWith($haystack, $needle)
{
// search backwards starting from haystack length characters from the end
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
} // http 301 is handled.
function http_get_core($url, &$status = null)
{
CT_log("-----------http_get:" . $url);
$ch = curl_init();
$status = -1; // 301 最多嵌套3次。
for ($i = 0; $i < 3; $i++) {
$options = array(
CURLOPT_HEADER => 1,
CURLOPT_POST => 0, // 请求方式为POST
CURLOPT_URL => $url, // 请求URL
CURLOPT_RETURNTRANSFER => 1, // 获取请求结果
CURLOPT_TIMEOUT_MS => 30000, // 超时时间(ms)
CURLOPT_POSTFIELDS => http_build_query(array()), // 注入接口参数
CURLOPT_SSL_VERIFYPEER => 0, // 不验证证书
);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate"); // 百度不支持
curl_setopt
($ch, CURLOPT_CUSTOMREQUEST, 'GET'); if (($response = curl_exec($ch))) {
// 有的网站header、<head>指定的编码不一致,会导致乱码。因此如果有编码信息,将其转送到client。
$content_type = curl_get_header($ch, $response, "Content-Type");
if($content_type) {
header("Content-Type: " . $content_type);
} $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$status = $code = curl_get_status($ch, $response);
if ($code == 301 || $code == 302) {
$redirect_url = curl_get_header($ch, $response, "Location");
$parsed_re = parse_url($redirect_url);
if(isset($parsed_re["host"])) {
$url = $redirect_url;
}
else {
$parsed = parse_url($url);
if(startsWith($redirect_url, "/")) {
$url = $parsed["schema"]. "://" . $parsed["host"] . $redirect_url;
}
else {
// TODO 相对路径拼接
$content = "relative path TODO";
curl_close($ch);
return $content;
}
}
continue;
} else if ($code == 404) {
header("Status: 404 Not Found");
$msg = array(
"status" => 404
);
$content = "<p id='http_util_message_block' style='display: none'>" . json_encode($msg) . "</p>";
$content .= "404, not found!";
} else if ($code == 200) {
$content = substr($response, $header_size);
} else {
$content = "http error, code=" . $code . "\n" . substr($response, $header_size);
}
} else {
$msg = array(
"status" => -1
);
$content = "<p id='http_util_message_block' style='display: none'>" . json_encode($msg) . "</p>";
$content .= "invoke error[" . curl_error($ch) . "]";
}
curl_close($ch);
return $content;
}
} // 递归为path创建必要的路径
function file_create_path($path)
{
$dir = dirname($path);
if ($dir && !file_exists($dir)) {
mkdir($dir, 0755, true);
}
} // $save_path 需要gbk编码
function file_save($content, $save_path, $append)
{
file_create_path($save_path);
if ($append) {
$file = fopen($save_path, "a+");
if ($file) {
fwrite($file, $content);
fclose($file);
}
} else {
file_put_contents($save_path, $content);
}
} // 默认的curl封装
function curl_do($url, $close_after_use = true)
{
CT_log("curl_do: " . $url);
$ch = curl_init(); $options = array(
CURLOPT_HEADER => 0,
CURLOPT_POST => 0, // 请求方式为POST
CURLOPT_URL => $url, // 请求URL
CURLOPT_RETURNTRANSFER => 1, // 获取请求结果
CURLOPT_TIMEOUT_MS => 30000, // 超时时间(ms)
CURLOPT_POSTFIELDS => http_build_query(array()), // 注入接口参数
CURLOPT_SSL_VERIFYPEER => 0, // 不验证证书
); curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
$response = curl_exec($ch);
$err = curl_error($ch);
if ($err) {
CT_log("curl error: " . $err);
}
if ($close_after_use) {
curl_close($ch);
}
return array("handle" => $ch, "response" => $response, "err" => $err);
} /**
* url拼接,没有处理user,pass两个components
* 详细定义参见单元测试
*
@param $url
*
@param $base
*
@return string
*/
function get_absolute_url($url, $base)
{
// 两种情况直接返回$url:
if (!$base)
return $url;
$url_host = parse_url($url, PHP_URL_HOST);
if ($url_host) {
return $url;
} $base_parsed = parse_url($base);
$base_scheme = get_safe($base_parsed, "scheme", "");
$base_host = get_safe($base_parsed, "host", "");
$base_port = isset($base_parsed["port"]) ? ":" . $base_parsed["port"] : "";
$base_path = get_safe($base_parsed, "path"); if ($base_host) {
$base_calc = $base_scheme . "://" . $base_host . $base_port;
if (startsWith($url, "/")) {
return $base_calc . $url;
} else if ($base_path) {
$pos = strrpos($base_path, "/");
if ($pos !== false) {
$dir = substr($base_path, 0, $pos + 1); // with last "/"
return $base_calc . $dir . $url;
} else {
return $base_calc . "/" . $url;
}
} else {
return $base_calc . "/" . $url;
}
} else {
if (startsWith($url, "/")) {
return $url;
} else if ($base_path) {
$pos = strrpos($base_path, "/");
if ($pos !== false) {
$dir = substr($base_path, 0, $pos + 1); // with last "/"
return $dir . $url;
} else {
return $url;
}
} else {
return $url;
}
}
} function get_absolute_url_tests()
{
// empty tests
$tests[] = [null, null, null];
$tests[] = ["", null, ""];
$tests[] = [null, "", null];
$tests[] = ["", "", ""]; $tests[] = [null, "/", "/"];
$tests[] = [null, "/a", "/"];
$tests[] = [null, "/a/", "/a/"]; $tests[] = ["a.html", "b", "a.html"];
$tests[] = ["a.html", "", "a.html"];
$tests[] = ["a.html", "/", "/a.html"];
$tests[] = ["a/b/c/a.html", "http://1.1.1.1", "http://1.1.1.1/a/b/c/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/a/b", "http://1.1.1.1:83/a/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/?", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83?", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83?a=b", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "https://1.1.1.1:83?a=b#1", "https://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "www.baidu.com?a=b#1", "a.html"]; // www被认为是path // starts with "/"
$tests[] = ["/a.html", "b", "/a.html"];
$tests[] = ["/a.html", "", "/a.html"];
$tests[] = ["/a.html", "/", "/a.html"];
$tests[] = ["/a/b/c/a.html", "http://1.1.1.1", "http://1.1.1.1/a/b/c/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/a/b", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/?", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83?", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83?a=b", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "https://1.1.1.1:83?a=b#1", "https://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "www.baidu.com?a=b#1", "/a.html"]; // www被认为是path $r = true;
foreach ($tests as $test) {
$abs = get_absolute_url($test[0], $test[1]);
echo $abs . "\n";
if ($abs !== $test[2]) {
$r = false;
break;
}
}
echo "pass: " . $r . "\n"; // return self tests
$tests_self = [];
$tests_self[] = ["http://test.com/a.html", "b", ""];
$tests_self[] = ["http://test.com/a.html", "", ""];
$tests_self[] = ["http://test.com/a.html", "/", ""];
$tests_self[] = ["http://test.com/a/b/c/a.html", "http://1.1.1.1", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/a/b", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/?", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83?", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83?a=b", ""];
$tests_self[] = ["http://test.com/a.html", "https://1.1.1.1:83?a=b#1", ""];
$tests_self[] = ["http://test.com/a.html", "www.baidu.com?a=b#1", ""]; // www被认为是path echo "-------------------return self test--------:\n";
$r = true;
foreach ($tests_self as $test) {
$abs = get_absolute_url($test[0], $test[1]);
echo $abs . "\n";
if ($abs !== $test[0]) {
$r = false;
break;
}
}
echo "pass: " . $r . "\n";
} /**
* 路径规范化。
* eg:
* a/../b => b
* ../a/../../b => ../b
* ./a/../b => ./b
*
@param $path
*
@return mixed|string
*/
function normalize_path($path)
{
if (!$path) {
return $path;
}
$path = str_replace("\\", "/", $path); $dotdotCount = 0; // 以".."开头的处理
$arr = explode("/", $path);
$pathStack = [];
foreach ($arr as $ele) {
if ($ele !== "..") {
array_push($pathStack, $ele);
} else {
if (count($pathStack) === 0) {
$dotdotCount++;
} else {
array_pop($pathStack);
}
}
}
unset($ele); $r = str_pad("", $dotdotCount, "../");
foreach ($pathStack as $path) {
if ($r === "") {
$r = $path;
} else {
$r .= "/" . $path;
}
}
return $r;
} // 尝试gbk、utf-8两种编码;优先尝试传入编码
function is_file_ex($path)
{
if (is_file($path)) {
return true;
}
$enc = mb_detect_encoding($path, "gb2312", true);
if ($enc === 'EUC-CN') {
$path2 = iconv("gbk", "utf-8", $path);
} else {
$path2 = iconv("utf-8", "gbk", $path);
}
return is_file($path2);
} // 尝试gbk、utf-8两种编码;优先尝试传入编码
function file_get_contents_ex($path)
{
if (is_file($path)) {
return file_get_contents($path);
}
$enc = mb_detect_encoding($path, "gb2312", true);
if ($enc === 'EUC-CN') {
$path2 = iconv("gbk", "utf-8", $path);
} else {
$path2 = iconv("utf-8", "gbk", $path);
}
if (is_file($path2)) {
return file_get_contents($path2);
}
return false;
} // 创建文件lock,如果路径不存在则创建之
function create_file_lock($path, &$output = null)
{
file_create_path($path);
$f = null;
// 不使用"@",这样忽略文件存在的报错,其他异常返回(如权限问题)
try {
$f = fopen($path, "x");
} catch (Exception $e) {
$msg = $e->getMessage();
if (strpos($msg, "File exists") === false && strpos($msg, "文件已存在") === false) {
$output = $e->getMessage() . "\n" . $e->getTraceAsString();
}
}
return $f;
} // 关闭、释放文件lock
function release_file_lock($f, $path)
{
if ($f) {
fclose($f);
unlink($path);
}
} /**
* echo udate('Y-m-d H:i:s.u T');
*
@param string $format
*
@param null $utimestamp
*
@return false|string
*/
function udate ($format = 'u', $utimestamp = null)
{
if (is_null($utimestamp))
$utimestamp = microtime(true); $timestamp = floor($utimestamp);
$milliseconds = round(($utimestamp - $timestamp) * 1000000); return date(preg_replace('`(?<!\\\\)u`', $milliseconds, $format), $timestamp);
}
?>

一些php常用函数积累的更多相关文章

  1. oracle常用函数积累

    --oracle常用函数积累-- --1.字符串长度:LENGTH ,语法: CONCAT(string) --示例 select LENGTH('AA_BB') from dual;--结果:5 - ...

  2. MySql常用函数积累

    --MySql查看表结构 select column_name,data_type,CHARACTER_MAXIMUM_LENGTH,column_comment from information_s ...

  3. Oracle 常用函数积累

    ①length 函数说明:计算字符串长度的函数 返回结果:数字 使用图解: ②lengthb 函数说明:计算字符串字节长度.在学习过程中,了解到还有一个 lengthb 函数.字节和字符的区别 返回结 ...

  4. 【转】JNI学习积累之一 ---- 常用函数大全

    原文网址:http://blog.csdn.net/qinjuning/article/details/7595104 本文原创,转载请注明出处:http://blog.csdn.net/qinjun ...

  5. JNI学习积累之一 ---- 常用函数大全

    主要资料来源: 百度文库的<JNI常用函数> . 同时对其加以了补充 . 要素  :1. 该函数大全是基于C语言方式的,对于C++方式可以直接转换 ,例如,对于生成一个jstring类型的 ...

  6. 我自己的Javascript 库,封装了一些常用函数 Kingwell.js

    我自己的Javascript 库,封装了一些常用函数 Kingwell.js 博客分类: Javascript javascript 库javascript库  现在Javascript库海量,流行的 ...

  7. jquery常用函数与方法汇总

    1.delay(duration,[queueName]) 设置一个延时来推迟执行队列中之后的项目. jQuery1.4新增.用于将队列中的函数延时执行.他既可以推迟动画队列的执行,也可以用于自定义队 ...

  8. SQLServer 之 常用函数及查看

    一.查看 (1)应用程序名称              SELECT APP_NAME() (2)获取登录者名字           SELECT SUSER_NAME() (3)获取字段定义的长度  ...

  9. oracle常用函数及示例

    学习oracle也有一段时间了,发现oracle中的函数好多,对于做后台的程序猿来说,大把大把的时间还要学习很多其他的新东西,再把这些函数也都记住是不太现实的,所以总结了一下oracle中的一些常用函 ...

随机推荐

  1. HDU - 5557 Matching Compressed String (自动机+倍增+表达式计算)

    题意是给你一个自动机和一个字符串的括号表达式,问自动机能否接受这个字符串. 我一想,这不就是个模拟栈计算表达式+倍增么? 再一想,复杂度200*1000*10000*log(1e9),不对啊! 交上去 ...

  2. MySQL分组查询,查询出某一个字段的最新记录

    直接上案例...... 案例: 同一个表中,只想需要A.B.C的最新记录 第一种方案: 应该还很多方法......(暂时先这样.....) 

  3. Codeforces Round #589 (Div. 2) C - Primes and Multiplication(数学, 质数)

    链接: https://codeforces.com/contest/1228/problem/C 题意: Let's introduce some definitions that will be ...

  4. 关于单片机特殊功能寄存器(SFR)和内存(RAM)公用地址:80-FF 如何区分

    RAM 的 80-FF 需要间接寻址进行访问 如:  MOV R0,#80H;    MOV A,@R0 ;  (内存 80H地址内的数据放到A中) SFR的80-FF需要直接寻址进行访问如: MOV ...

  5. 015_linuxC++之_覆写

    34.类成员函数的重载.覆盖和隐藏区别?答案:a.成员函数被重载的特征:(1)相同的范围(在同一个类中):(2)函数名字相同:(3)参数不同:(4)virtual 关键字可有可无.b.覆盖是指派生类函 ...

  6. shell中命令代换$()与`` 、 变量代换${} 、 整数运算$(( )) 的区别

    命令代换$()与`` . 变量代换${} . 整数运算$(( )) 1.$( ) 与 ` ` (反引号) 在 bash shell 中,$( ) 与 ` ` (反引号) 都是用来做命令替换用(comm ...

  7. 如何检测域名是否被微信屏蔽 微信域名检测接口API是如何实现

    微信域名检测技术的主要用户是微信域名防封,大家知道拼多多这种网站,靠诱导分享方式在微信里面摇身一变已经估值160亿美元,身价仅次于京东了 ,这是何等的速度,简直是惊为天人,but 如果你想玩微信病毒营 ...

  8. java中判断空字符串和null的判断方法

    简单总结几个方法: 1.直观的: if(s == null ||"".equals(s)); //先判断是否对象,再判断是否是空字符串 2.比较字符串长度, 效率高, 比较绕: i ...

  9. JAVA之工作线程数究竟要设置多少

    一.需求缘起 Web-Server通常有个配置,最大工作线程数,后端服务一般也有个配置,工作线程池的线程数量,这个线程数的配置不同的业务架构师有不同的经验值,有些业务设置为CPU核数的2倍,有些业务设 ...

  10. spring MVC 拦截有几种实现方式

    spring MVC 拦截有几种实现方式 实现HandelInterceptor接口方式        继承HandelInterceptor 的方式.一般有这两种方式 spring 如何走单元测式 ...