Hook.php 文件已更新
1、修复在linux环境下类的 \ 在basename 下无法获取到类名的问题
2、修复linux 环境下无法使用hook::call 调用失败问题

请先安装thinkphp5,包里的文件可直接使用,
application\tags.php、application\index\controller\Index.php 文件都已经把文件名改成tags.php.bak,Index.php.bak

钩子机制我这里分为两种使用方法,注:二者只能选其一
1、采用thinkphp5内置的钩子调用方案。 配置参数 jntoo_hook_call 为true 情况下才有效,为了兼容已经写好了钩子机制的使用
2、采用自有的钩子调用方案,此方法支持钩子分类,钩子权重自动排序

个人不建议使用think内置钩子作为系统的扩展。

如何写自动加载文件:
在模块下新建一个hook目录,并在目录下创建Category.php文件,如下图:

类名等于钩子类型,方法名等于钩子名称。(注:类型只存在于钩子机制方法2。方法1只有方法名生效)
如图所示:

我们创建了 Category 类型的钩子
里面有两个方法:
index 和 index_5

方法1的调用方法:\think\Hook::listen('index'); 就可以直接调用到Category 类 index 方法。
输出结果:

方法2的调用方法:\app\common\Hook::call('Category','index'); 就可以调用到 Category 类 index 方法和index_5 方法
输出结果:

方法2为什么能 调用Category.index 钩子时会调用到Category.index_5 这个钩子呢?这个我们后面进行一个讲解。

讲个小故事
有一天客户说要求实现一个会员的注册模块。
我这里键盘敲了半天完成了代码会员的注册模块。
第二天客户说“现在客户注册没问题了,我想能不能在注册完成后给他添加50积分,让他能进行一些消费。”,我说“没问题”
敲敲敲半天代码我完成了客户的要求。现在注册会员后,有赠送50积分了。
第三天同事问我你前两天不是在系统里弄了个注册模块和赠送积分?我说是啊。同事说“把你这个提交一下svn库,我这边也有个客户想弄个注册模块和赠送积分,还要添加发送邮件验证”,我:“好的,不过你要自己添加发送邮件验证。”代码提交到svn库,
同事敲了半天代码,完成了发送邮件。
第四天我找同事让他提交一下SVN库,让我进行一下代码同步。
第五天另一个客户问我“你们的注册模块有没有会员注册验证手机”,这个我当然只能说有(实际上是没有短信认证),客户说”能装好环境让我测试一下?“,我只能说:“抱歉先生,我们现在还没装好环境,这个我明天给您装好,您在测试一下”,客户说“好”
蹭蹭蹭敲了半天代码在会员注册模块上面加了会员注册手机验证。
第六天客户觉得挺好的。
故事讲到这里。
假设以上会员注册模块的形成是这样的一段代码片段:

  1. namespace app\index\controller;
  2. class member{
  3. function register($username , $password , $tel , $email){
  4. // 第五天加的代码:
  5. if(是否短信验证)
  6. VerifTel($tel , 验证码);
  7. // 第一天写的代码:
  8. if(table('member')->where('username' , $username)->count())
  9. {
  10. $this->error = '用户名已存在'
  11. return false;
  12. }
  13. $uid = 写入数据库得到新的用户UID
  14. ....省略大部分代码
  15. // 第二天加的代码:
  16. if(赠送积分)
  17. sendPoints($uid . 赠送积分); // 发送积分50
  18. // 第三天
  19. if(发送邮件)
  20. sendEmail($uid , 邮件);
  21. return true;
  22. }
  23. }
复制代码

从以上我们可以看出,当写完一个功能模块的时候,并不知道客户一些需求的情况,我们会在当前模块中加入代码。
如果一直下去加下去,回头看代码的时候你会发现你的代码实在是太多了。
这个代码是不是可以变得优雅一些

如果我们在这个代码中加入钩子机制会是怎么样的呢?
看如下代码

  1. namespace app\index\controller;
  2. class member{
  3. function register($username , $password , $tel , $email){
  4. // 我不知道以后会变成什么样,先加个前置钩子,参数:$username,$password,$tel,$email
  5. $param = [ $username,$password,$tel,$email ];
  6. \app\common\Hook::call('member','registerPre',$param);
  7. // 第一天写的代码:
  8. if(table('member')->where('username' , $username)->count())
  9. {
  10. $this->error = '用户名已存在'
  11. return false;
  12. }
  13. $uid = 写入数据库得到新的用户UID
  14. ....省略大部分代码
  15. // 我不知道以后会变成什么样,先加个注册成功后的钩子,参数 $uid
  16. $param = [ $uid ];
  17. \app\common\Hook::call('member','registerSuccess',$param);
  18. return true;
  19. }
  20. }
复制代码

那么问题来了我们怎么去解决钩子的问题?
假设我们的类都已经注册进了钩子里:
我们来完成第二天的事情,如下代码:

  1. namespace app\index\hook;
  2. class member{
  3. // 钩子类型,挂入 member 类型钩子,中的registerSuccess钩子
  4. function registerSuccess($uid)
  5. {
  6. if(赠送积分)
  7. sendPoints($uid . 赠送积分); // 发送积分50
  8. }
  9. }
复制代码

第三天的时候同事要用我的代码怎么办?那么我会告诉他:“我这边已经下了两个钩子,一个是注册前置钩子,一个是注册成功后的钩子”并告诉他钩子类型,名称,还有相应的参数,同事的代码将变成这样

  1. namespace app\tongshi\hook;
  2. class member{
  3. // 钩子类型,挂入 member 类型钩子,中的registerSuccess钩子
  4. function registerSuccess($uid)
  5. {
  6. if(发送邮件)
  7. sendEmail($uid , 邮件);
  8. }
  9. }
复制代码

第五天的时候这样的:

  1. namespace app\index\hook;
  2. class member{
  3. // 挂入 member 类型钩子,中的registerSuccess钩子
  4. function registerSuccess($uid)
  5. {
  6. if(赠送积分)
  7. sendPoints($uid . 赠送积分); // 发送积分50
  8. }
  9. // 挂入member 类型钩子中的registerPre钩子
  10. function registerPre()
  11. {
  12. if(是否短信验证)
  13. VerifTel($tel , 验证码);
  14. }
  15. }
复制代码

回到我们上面讲的“为什么能 调用Category.index 钩子时会调用到Category.index_5 这个钩子呢?”

在钩子机制设计时想到钩子有可能有先后顺序,而设计了一个能写入权重排序的数字,在尾部添加数字,排序从小到大排序,实际的钩子注册会自动删除尾部的“_数字”:

一:先来讲讲钩子机制
在项目代码中,你认为要扩展(暂时不扩展)的地方放置一个钩子函数,等需要扩展的时候,把需要实现的类和函数挂载到这个钩子上,就可以实现扩展了。
思想就是这样听起来比较笼统。
在二次开发别人写的代码时,如果有钩子机制,作者会告诉你哪些地方给下了钩子,当你扩展时就可以在不改动原代码的情况下进行一个升级扩展。

本插件扩展配置参数信息:

  1. 1、jntoo_hook_cache
  2. 逻辑值,是否开启钩子编译缓存,开启后只需要编译一次,以后都将成为惰性加载,如果安装了新的钩子,需要先调用Hook::clearCache() 清除缓存
  3. 2、jntoo_hook_call
  4. 逻辑值,是否使用think钩子系统。值为真使用think钩子后,将无法使用权重排序和钩子自动分类,只有类中的方法将自动注册到think钩子机制中
  5. 3、jntoo_hook_path
  6. 某个文件夹下的PHP文件都自动为其注册钩子
  7. 配置实现
  8. jntoo_hook_path => [
  9. [
  10. 'path'=>'你的路径', // 路径尾部必须加斜杠 "/"
  11. 'pattern'=> '规则,类的匹配规则' 例如:'/plugin\\\\module\\\\hook\\\\([0-9a-zA-Z_]+)/'
  12. ],
  13. ....
  14. ]
  15. 4、jntoo_hook_plugin
  16. 多模块目录自动编译,在模块文件夹下加入hook目录,此目录下的php文件会自动注册钩子
  17. 配置实现:
  18. 'jntoo_hook_plugin' => [
  19. [
  20. 'path'=>'你的app模块路径',
  21. 'pattern'=> '规则,类的匹配规则' 例如:'/plugin\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/'
  22. ],
  23. ....
  24. ]
复制代码


请在application/tags.php
'app_init'行为处加上:'\\app\\common\\Hook'
例如:
// 应用行为扩展定义文件
return [
// 应用初始化
'app_init' => [
'\\app\\common\\Hook'
],
// 应用开始
'app_begin' => [],
// 模块初始化
'module_init' => [],
// 操作开始执行
'action_begin' => [],
// 视图内容过滤
'view_filter' => [],
// 日志写入
'log_write' => [],
// 应用结束
'app_end' => [],
];

请自行创建文件:

application\index\controller\Index.php下钩子文件

  1. namespace app\index\controller;
  2. use app\common\Hook;
  3. use think\Hook AS thinkHook;
  4. class Index
  5. {
  6. public function index()
  7. {
  8. Hook::call('Category' , 'index');
  9. }
  10. }
复制代码

application\index\hook\Category.php钩子文件

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: JnToo
  5. * Date: 2016/11/12
  6. * Time: 1:11
  7. */
  8. namespace app\index\hook;
  9. class Category
  10. {
  11. function index()
  12. {
  13. echo '我是Category类型钩子中的index方法<br>';
  14. }
  15. function index_5()
  16. {
  17. echo '我是Category类型钩子中的index方法,我的权重比较低<br>';
  18. }
  19. }
复制代码

application\common\hook.php主文件

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: JnToo
  5. * Date: 2016/11/11
  6. * Time: 22:57
  7. */
  8. namespace app\common;
  9. use think\Config;
  10. use think\Hook as thinkHook;
  11. use think\Cache;
  12. /**
  13. * 请在application/tags.php
  14. * 'app_init'行为处加上:'\\app\\common\\Hook'
  15. * 例如:
  16. * // 应用行为扩展定义文件
  17. return [
  18. // 应用初始化
  19. 'app_init'     => [
  20. '\\app\\common\\Hook'
  21. ],
  22. // 应用开始
  23. 'app_begin'    => [],
  24. // 模块初始化
  25. 'module_init'  => [],
  26. // 操作开始执行
  27. 'action_begin' => [],
  28. // 视图内容过滤
  29. 'view_filter'  => [],
  30. // 日志写入
  31. 'log_write'    => [],
  32. // 应用结束
  33. 'app_end'      => [],
  34. ];
  35. * Class Hook
  36. * @package app\common
  37. */
  38. class Hook
  39. {
  40. /**
  41. * 编译钩子时使用计数器
  42. * @var int
  43. */
  44. static protected $index = 0;
  45. /**
  46. * 添加引用计数
  47. * @var int
  48. */
  49. static protected $indexAdd = 1;
  50. /**
  51. * 已编译好的钩子列表
  52. * @var array
  53. */
  54. static protected $hookList = array();
  55. /**
  56. * application/config.php 文件中加入如下的配置信息
  57. * @var array
  58. */
  59. static protected $default =[
  60. // 是否开启钩子编译缓存,开启后只需要编译一次,以后都将成为惰性加载,如果安装了新的钩子,需要先调用Hook::clearCache() 清除缓存
  61. 'jntoo_hook_cache'=>false,
  62. // 钩子是否使用think钩子系统
  63. 'jntoo_hook_call'=>false ,
  64. /**
  65. * 某个文件夹下hook加载,配置文件方法实现
  66. * jntoo_hook_path => [
  67. *     [
  68. *          'path'=>'你的路径', // 路径尾部必须加斜杠 "/"
  69. *          'pattern'=> '规则,类的匹配规则' 例如:'/plugin\\\\module\\\\hook\\\\([0-9a-zA-Z_]+)/'
  70. *     ],
  71. *     ....
  72. * ]
  73. */
  74. 'jntoo_hook_plugin'=>[],
  75. /**
  76. *  多模块目录下自动搜索,配置文件方法实现
  77. * 'jntoo_hook_plugin' => [
  78. *     [,
  79. *          'path'=>'你的app模块路径'
  80. *          'pattern'=> '规则,类的匹配规则' 例如:'/plugin\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/'
  81. *     ],
  82. *     ....
  83. * ]
  84. */
  85. 'jntoo_hook_plugin'=>[],
  86. ];
  87. /**
  88. * 提供行为调用
  89. */
  90. public function run()
  91. {
  92. self::init();
  93. }
  94. /**
  95. * 注册钩子
  96. * @param $type 钩子类型
  97. * @param $name 钩子名称
  98. * @param $param \Closure|array
  99. */
  100. static public function add($type , $name , $param , $listorder = 1)
  101. {
  102. $key = strtolower($type .'_'.$name);
  103. isset(self::$hookList[$key]) or self::$hookList[$key] = [];
  104. self::$hookList[$key][$listorder.'_'.self::$indexAdd++] = $param;
  105. ksort(self::$hookList[$key]);
  106. // 兼容
  107. if(Config::get('jntoo_hook_call'))
  108. {
  109. thinkHook::add($name , $param);
  110. }
  111. return;
  112. }
  113. /**
  114. * 清除编译钩子的缓存
  115. */
  116. static public function clearCache()
  117. {
  118. // 清楚编译钩子缓存
  119. if(Config::get('jntoo_hook_cache')){
  120. cache('jntoo_hook_cache' , null);
  121. }
  122. }
  123. /**
  124. * 执行钩子
  125. * @param $type string
  126. * @param $name string
  127. * @param array $array
  128. * @param mixe
  129. */
  130. static public function call($type , $name , &$array = array())
  131. {
  132. static $_cls = array();
  133. $ret = '';
  134. if(Config::get('jntoo_hook_call')){
  135. return thinkHook::listen($name , $array);
  136. }else{
  137. $key = strtolower($type.'_'.$name);
  138. // 自有的调用方案
  139. if(isset(self::$hookList[$key]))
  140. {
  141. foreach(self::$hookList[$key] as $r){
  142. // 闭包处理
  143. $result = '';
  144. if(is_callable($r)){
  145. $result = call_user_func_array($r, $array);
  146. }elseif(is_object($r)){
  147. // 自己定义对象钩子
  148. if(method_exists($r , $name)){
  149. $result = call_user_func_array(array($r , $name), $array);
  150. }
  151. }else{
  152. // 自动搜索出来的钩子
  153. $class = $r['class'];
  154. if(class_exists($class , false)){
  155. // 如果不存在
  156. if($r['filename'])require_once(ROOT_PATH.$r['filename']);
  157. }
  158. if(class_exists($class , false)){
  159. if(!isset($_cls[$class])){
  160. $_cls[$class] = new $class();
  161. }
  162. $func = $r['func'];
  163. $result = call_user_func_array(array($_cls[$class] , $func), $array);
  164. }
  165. }
  166. if($result)$ret.=$result;
  167. }
  168. }
  169. }
  170. return $ret;
  171. }
  172. /**
  173. * 初始化钩子
  174. */
  175. static protected function init()
  176. {
  177. // 取钩子的缓存
  178. self::$hookList = self::getCache();
  179. if(!self::$hookList)
  180. {
  181. // 保存在当前变量中
  182. $saveArray = [];
  183. // 钩子不存在,先搜索app目录下的模块
  184. //echo APP_PATH;
  185. //echo ROOT_PATH;
  186. $result = self::searchDir(APP_PATH);
  187. // 先编译此模块
  188. self::compileHook($result , '/app\\\\([0-9a-zA-Z_]+)\\\\hook\\\\([0-9a-zA-Z_]+)/' , $saveArray);
  189. //print_r($saveArray);
  190. // 多模块实现搜索加载
  191. $jntooHook = Config::get('jntoo_hook_plugin');
  192. if($jntooHook){
  193. foreach($jntooHook as $t){
  194. $result = self::searchDir($t['path']);
  195. self::compileHook($result , $t['pattern'] , $saveArray);
  196. }
  197. }
  198. // 单个路径的模块搜索
  199. $jntooHook = Config::get('jntoo_hook_path');
  200. if($jntooHook){
  201. foreach($jntooHook as $t){
  202. $result = [];
  203. self::searchHook($t['path'] , $result);
  204. self::compileHook($result , $saveArray);
  205. }
  206. }
  207. // 编译完成,现在进行一个权重排序
  208. foreach($saveArray as $k=>$t){
  209. ksort($saveArray[$k]);
  210. }
  211. self::setCache($saveArray);
  212. self::$hookList = $saveArray;
  213. }
  214. //print_r(self::$hookList);
  215. $calltype = Config::get('jntoo_hook_call');
  216. // 检测他的调用方法,是否需要注册到think中,不建议注册到 think 中,
  217. // 因为这个系统含有分类的形式,注册进去后将无法使用排序功能
  218. if($calltype){
  219. // 注册进think 钩子中
  220. self::registorThink();
  221. }else{
  222. // 注册系统行为钩子
  223. self::registorCall();
  224. }
  225. }
  226. /**
  227. * 注册系统行为调用
  228. */
  229. static protected function registorCall()
  230. {
  231. thinkHook::add('app_init' , function( &$params = null ){
  232. $arg = [&$params];
  233. Hook::call('system' , 'app_init' , $arg);
  234. });
  235. thinkHook::add('app_begin' , function( &$params = null ){
  236. $arg = [&$params];
  237. Hook::call('system' , 'app_begin' , $arg);
  238. });
  239. thinkHook::add('module_init' , function( &$params = null ){
  240. $arg = [&$params];
  241. Hook::call('system' , 'module_init' , $arg);
  242. });
  243. thinkHook::add('action_begin' , function( &$params = null ){
  244. $arg = [&$params];
  245. Hook::call('system' , 'action_begin' , $arg);
  246. });
  247. thinkHook::add('view_filter' , function( &$params = null ){
  248. $arg = [&$params];
  249. Hook::call('system' , 'view_filter' , $arg);
  250. });
  251. thinkHook::add('app_end' , function( &$params = null ){
  252. $arg = [&$params];
  253. Hook::call('system' , 'app_end' , $arg);
  254. });
  255. thinkHook::add('log_write' , function( &$params = null ){
  256. $arg = [&$params];
  257. Hook::call('system' , 'log_write' , $arg);
  258. });
  259. thinkHook::add('response_end' , function( &$params = null ){
  260. $arg = [&$params];
  261. Hook::call('system' , 'response_end' , $arg);
  262. });
  263. }
  264. /**
  265. * 将钩子注册进thinkHook 钩子中
  266. */
  267. static protected function registorThink()
  268. {
  269. foreach(self::$hookList as $key=>$list)
  270. {
  271. foreach($list as $r){
  272. thinkHook::add($r['func'] , $r['class']);
  273. }
  274. }
  275. }
  276. /**
  277. * 搜索目录下的钩子文件
  278. * @param $path string
  279. * @param $saveArray array 保存的文件路径
  280. * @return null
  281. */
  282. static protected function searchHook( $path , &$saveArray)
  283. {
  284. $fp = opendir($path);
  285. if($fp){
  286. while($file = readdir($fp))
  287. {
  288. if(substr($file , -4) == '.php')
  289. {
  290. $saveArray[] = $path.$file;
  291. }
  292. }
  293. }
  294. }
  295. /**
  296. * 编译钩子,编译后直接保存在静态成员变量 self::$hookList
  297. * @param $filelist array 文件路径
  298. * @param $namespace string 命名空间规则
  299. * @param $saveHook array 保存Hook
  300. * @return null
  301. */
  302. static protected function compileHook($filelist , $namespace , &$saveHook)
  303. {
  304. $root_path = strtr(ROOT_PATH,'\\' , '/');
  305. //print_r($filelist);
  306. // 当前引用计数
  307. $index = self::$index;
  308. $indexAdd = self::$indexAdd;
  309. foreach ($filelist as $file)
  310. {
  311. require_once($file);
  312. // 获取已经加载的类
  313. $class_list = get_declared_classes();
  314. // 搜索计数器
  315. for($len = count($class_list);$index<$len;$index++)
  316. {
  317. $classname = $class_list[$index];
  318. if(preg_match($namespace , $classname))
  319. {
  320. // 这个类满足我们的需求
  321. $ec = new \ReflectionClass($classname);
  322. // 钩子的类型
  323. $type = basename(strtr($classname , '\\' , '/'));
  324. foreach($ec->getMethods() as $r){
  325. if($r->name[0] != '_' && $r->class == $classname){
  326. // 暂时还不知道怎么实现排序 方法名后面有
  327. $name = $r->name;
  328. $listorder = 1;
  329. if(strpos($name , '_') !== false){
  330. // 存在排序
  331. $temp = explode('_',$name);
  332. $num  = array_pop($temp);
  333. if(is_numeric($num)){
  334. $name = implode('_' , $temp);
  335. $listorder = $num;
  336. }
  337. }
  338. $typename = strtolower($type.'_'.$name);
  339. !isset($saveHook[$typename]) AND $saveHook[$typename] = [];
  340. $saveHook[$typename][$listorder.'_'.$indexAdd++] = [
  341. 'filename'=>str_replace($root_path,'',$file), // 保存文件路径的好处是方便快速加载,无需在进行路径的查找
  342. 'class'=>$classname, // 保存类的名称
  343. 'func'=>$r->name,   // 保存方法名
  344. 'listorder'=>$listorder // 排序,编译完成后,进行一个权重的排序
  345. ];
  346. }
  347. }
  348. }
  349. }
  350. }
  351. self::$index = $index;
  352. self::$indexAdd = $indexAdd;
  353. }
  354. /**
  355. * @param $path 搜索模块路径
  356. * @return array
  357. */
  358. static protected function searchDir( $path )
  359. {
  360. // 目录自动补全
  361. $path = strtr(realpath($path),'\\' , '/');
  362. $char = substr($path,-1);
  363. if( $char != '/' || $char != '\\' ){
  364. $path .= '/';
  365. }
  366. $path .= '*';
  367. $dirs = glob($path, GLOB_ONLYDIR );
  368. $result = array();
  369. foreach($dirs as $dir){
  370. if(is_dir($dir .'/hook'))
  371. {
  372. self::searchHook($dir .'/hook/' , $result);
  373. }
  374. }
  375. return $result;
  376. }
  377. /**
  378. * 获取编译好的钩子
  379. * @return bool|array
  380. */
  381. static protected function getCache()
  382. {
  383. if(Config::get('jntoo_hook_cache')){
  384. // 获取缓存
  385. return cache('jntoo_hook_cache');
  386. }
  387. return false;
  388. }
  389. /**
  390. * 保存编译的缓存
  391. * @param $value array
  392. * @return bool
  393. */
  394. static protected function setCache( $value )
  395. {
  396. // 设置为永久缓存
  397. if(Config::get('jntoo_hook_cache')){
  398. cache('jntoo_hook_cache' , $value , null);
  399. }
  400. return true;
  401. }
  402. }

thinkphp5 自动注册Hook机制钩子扩展的更多相关文章

  1. 自动注册服务NET Core扩展IServiceCollection

    NET Core扩展IServiceCollection自动注册服务 前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中, ...

  2. Thinkphp5.1自动加载机制

    Thinkphp5.1自动加载机制 自动加载机制 注册自动加载 引入静态自动加载映射文件,autoload_static.php 根据首字母前缀将不同的加载类归类-$prefixLengthsPsr4 ...

  3. Unity3.0基于约定的自动注册机制

    前文<Unity2.0容器自动注册机制>中,介绍了如何在 Unity 2.0 版本中使用 Auto Registration 自动注册机制.在 Unity 3.0 版本中(2013年),新 ...

  4. Unity2.0容器自动注册机制

    现如今可能每个人都会在项目中使用着某种 IoC 容器,并且我们的意识中已经形成一些固定的使用模式,有时会很难想象如果没有 IoC 容器工作该怎么进展. IoC 容器通过某种特定设计的配置,用于在运行时 ...

  5. 框架Thinkphp5 简单的实现行为 钩子 Hook

    这篇文章主要介绍了关于框架Thinkphp5 简单的实现行为 钩子 Hook,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 实现在一个方法开始和结束加入两个行为:api_init.ap ...

  6. thinkphp5源码剖析系列1-类的自动加载机制

    前言 tp5想必大家都不陌生,但是大部分人都停留在应用的层面,我将开启系列随笔,深入剖析tp5源码,以供大家顺利进阶.本章将从类的自动加载讲起,自动加载是tp框架的灵魂所在,也是成熟php框架的必备功 ...

  7. .NET Core扩展IServiceCollection自动注册服务

    前言 在ASP.NET Core中使用依赖注入中使用很简单,只需在Startup类的ConfigureServices()方法中,通过IServiceCollection接口进行注入即可,其它的无需关 ...

  8. 微软IOC容器Unity简单代码示例3-基于约定的自动注册机制

    @(编程) [TOC] Unity在3.0之后,支持基于约定的自动注册机制Registration By Convention,本文简单介绍如何配置. 1. 通过Nuget下载Unity 版本号如下: ...

  9. 【repost】JS中的hook机制

    hook机制也就是钩子机制,由表驱动实现,常用来处理多种特殊情况的处理.我们预定义了一些钩子,在常用的代码逻辑中去适配一些特殊的事件,这样可以让我们少些很多if else语句.举个高考加分的例子,比如 ...

随机推荐

  1. Vue实例与组件的关系

    所有的 Vue 组件都是 Vue 实例,可以看成Vue组件就是Vue实例的扩展. <div id="app"> <child></child> ...

  2. mongodb 索引分类

    一. 普通索引篇 1.创建索引 创建索引:db.person.ensureIndex({"age":1}).这里我们使用了ensureIndex在age上建立了索引.“1”:表示按 ...

  3. Swagger添加文件上传测试

    先上对比图 图一无法选择文件,图二可以选择文件 图一 图二 添加过滤器 public class SwaggerFileUploadFilter : IOperationFilter { /// &l ...

  4. HTML5 canvas绘制文本

    demo.html <!DOCTYPE html> <html lang="zh"> <head> <meta charset=" ...

  5. Angular JS - 5 - Angular JS 模块和控制器

    1.引入 1.5版本的angularjs,直接打印angular对象: --> <!DOCTYPE html> <html> <head lang="en ...

  6. Kafka数据如何同步至MaxCompute之实践讲解

    摘要:本次分享主要介绍Kafka产品的原理和使用方式,以及同步数据到MaxCompute的参数介绍.独享集成资源组与自定义资源组的使用背景和配置方式.Kafka同步数据到MaxCompute的开发到生 ...

  7. 透明的UITableView

    // // ViewController.m // 透明table // // Created by LiuWei on 2018/4/23. // Copyright © 2018年 xxx. Al ...

  8. BZOJ 3772: 精神污染(dfs序+主席树)

    传送门 解题思路 比较神仙的一道题.首先计算答案时可以每条路径所包含的路径数,对于\(x,y\)这条路径,可以在\(x\)这处开个\(vector\)存\(y\),然后计算时只需要算这个路径上每个点的 ...

  9. BZOJ 4484: [Jsoi2015]最小表示(拓扑排序+bitset)

    传送门 解题思路 \(bitset\)维护连通性,给每个点开个\(bitset\),第\(i\)位为\(1\)则表示与第\(i\)位联通.算答案时显然要枚举每条边,而枚举边的顺序需要贪心,一个点先到达 ...

  10. bugku | 你从哪里来

    题目链接 之前一直以为要用x-forwarded-for ,谁道用的是referer,Orz.在此特地记录,x-forwarded-for 和 referer的区别 X-Forwarded-For(X ...