<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans"><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://codecooker.cn/feed.xml" rel="self" type="application/atom+xml" /><link href="https://codecooker.cn/" rel="alternate" type="text/html" hreflang="zh-Hans" /><updated>2022-04-24T01:04:23+08:00</updated><id>https://codecooker.cn/feed.xml</id><title type="html">Codecooker’s blog</title><subtitle>Codecooker's Blog
</subtitle><author><name>codecooker</name><email>codecooker@outlook.com</email></author><entry><title type="html">隔离体验卡</title><link href="https://codecooker.cn/%E6%84%9F%E6%82%9F/2022/04/23/%E9%9A%94%E7%A6%BB%E4%BD%93%E9%AA%8C%E5%8D%A1.html" rel="alternate" type="text/html" title="隔离体验卡" /><published>2022-04-23T00:00:00+08:00</published><updated>2022-04-23T00:00:00+08:00</updated><id>https://codecooker.cn/%E6%84%9F%E6%82%9F/2022/04/23/%E9%9A%94%E7%A6%BB%E4%BD%93%E9%AA%8C%E5%8D%A1</id><content type="html" xml:base="https://codecooker.cn/%E6%84%9F%E6%82%9F/2022/04/23/%E9%9A%94%E7%A6%BB%E4%BD%93%E9%AA%8C%E5%8D%A1.html">&lt;p&gt;一直以来总觉得疫情隔离和自己很远，去年多次居家隔离也是走走形式，但是最近一次的体验确实工作后很多年第一次经历。从最初的略有期待（想看看到底是什么情况），到中途的焦躁不安，再到最后的重获自由，整个过程跟做梦一样。简单的把体验分享一下。&lt;/p&gt;

&lt;h3 id=&quot;起源&quot;&gt;起源&lt;/h3&gt;

&lt;p&gt;4月12号由于工作需要出差到成都，其实成都的防疫情况在我心里一直认为做的比较好，我也始终坚信成都是一个拥有科学防疫办法的城市，所以本次出差并没有考虑太多疫情相关的因素。出发来成都前专门在家做了一次核酸，按照成都的防疫政策，到成都后还需要在本地再做一次，为了不变黄码，13号中午放弃了午睡的时间去郫都区人民医院又做了1次核酸。按说已经做了2次核酸，期间一直在研究所办公，周五晚上还和同事去聚餐，已经完完全全的融入了成都人群。&lt;br /&gt;
然而，昨天（4月17日）凌晨，安朴通知10号后来成都的人要居家隔离，看完没当回事，居家隔离嘛，咱有经验。但是后边的消息给我了一个大大的问号，安朴表示自身并没有隔离条件，社区会安排我们隔离。此时的我还以为就是换个酒店而已。
整个过程很魔幻，大家还是很配合社区工作。&lt;br /&gt;
4月17日下午2点多，大家收拾好行李，我们一行人集中上了一辆不知道去哪里的大巴，在车上听到同行人问另一个“有没有带床单、被罩”，我承认，我当时有些慌了，这绝不是换个酒店那么简单。事实也没有让我失望，我被拉到了这个地方一个一墙之隔的地方。整个过程到现在想起来都很魔幻，但是我们还是大家还是很配合社区工作。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/1.jpg&quot; alt=&quot;梦开始的地方&quot; title=&quot;梦开始的地方&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;梦开始的地方&lt;/p&gt;&lt;/center&gt;

&lt;h3 id=&quot;到达&quot;&gt;到达&lt;/h3&gt;

&lt;p&gt;当天下午2点48，我们一行人来到了隔离点，有点像电影里接头的场景，一切遵从指令，不能从正常门进入，被大白带着从车库进入。&lt;br /&gt;
进入车库后，整个车库就一个大白在等着我们，车库入口大门随即关闭，有种紧张的感觉。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/2.jpg&quot; alt=&quot;图片alt&quot; title=&quot;图片title&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;已经关闭的大门&lt;/p&gt;&lt;/center&gt;

&lt;p&gt;然后就是标准动作，大白叫名字，发防护用品（酒精，一个3m口罩，健康告知书，门牌纸），集中消毒后上楼，消毒完成我们10个人挤在一部电梯了上了楼。我的房间是1xx4，里边设施十分简陋，好在需要的日用品还是都有准备（洗漱用品、热水壶、盆，应有尽有）。这几天恰逢成都降温，整个房间应该是由于长时间没人住的原因，非常的冷。进到房间后落差特别大，恨不得马上离开，简单的铺了下床，就算是可以“安家”了，袋子里有床单，被罩，看着像是新的（走的时候才知道应该是用完洗过的）。&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/3.jpg&quot; alt=&quot;大白集中给箱子消毒&quot; title=&quot;大白集中给箱子消毒&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;大白集中给箱子消毒&lt;/p&gt;&lt;/center&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/4.jpg&quot; alt=&quot;放在门口桌子上的日用品&quot; title=&quot;放在门口桌子上的日用品&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;放在门口桌子上的日用品&lt;/p&gt;&lt;/center&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/5.jpg&quot; alt=&quot;这个床看起来还是有些伤感&quot; title=&quot;这个床看起来还是有些伤感&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;这个床看起来还是有些伤感&lt;/p&gt;&lt;/center&gt;

&lt;p&gt;由于房间很冷，就打算睡一会，也是由于冷就没睡着，主要是床给人的压迫感太强了，也不敢脱衣服睡。这里要划线说一点，房子不大，但是有两台挂式空调，可谓豪华配置，但是没有找到空调遥控。好在我是华为手机，有遥控功能，索性双开，这里为自己的机智点赞。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/6.jpg&quot; alt=&quot;被我打开的空调&quot; title=&quot;被我打开的空调&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;被我打开的空调&lt;/p&gt;&lt;/center&gt;

&lt;p&gt;送餐很及时，5点半左右，送来晚餐，两荤两素，可谓营养均衡，味道不好（主要还是因为环境，总觉得不干净），量一般，就当减肥了。庆幸自己中午定了一张披萨。&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/7.jpg&quot; alt=&quot;豪华晚餐&quot; title=&quot;豪华晚餐&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;豪华晚餐&lt;/p&gt;&lt;/center&gt;

&lt;p&gt;由于是周末，下来就是漫长的无聊时间，一点消息都没有，甚至有些绝望，没有人知道外边是啥情况，要隔离到多久，没有一点消息。给街道社区打电话，意料之中的没人接，继续等吧。好消息是晚上房间慢慢的暖和起来，想到对门也是我司员工，于是在门口看了他的电话号码，加了个微信，聊了会儿，拉了群互通有无。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/8.jpg&quot; alt=&quot;我们的微信群&quot; title=&quot;我们的微信群&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;我们的微信群&lt;/p&gt;&lt;/center&gt;

&lt;p&gt;可能是由于环境不好，晚上一直睡不着，迷迷糊糊的，等差不多了，已经到早晨了，8点半左右，开始发早餐，早餐也是很讲究搭配，但是没胃口 。&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/9.jpg&quot; alt=&quot;丰盛的早餐&quot; title=&quot;丰盛的早餐&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;丰盛的早餐&lt;/p&gt;&lt;/center&gt;
&lt;p&gt;简单的洗漱了下开始了“居家”办公，工作起来好多了，不知不觉到了中午，想着每次送的饭都不是很好吃就定了个外卖。和生活负责人（对，你没看错，我们有生活负责人）确定了要求，这里要强调下，外卖是不能直接送过来的，而且有很多要求，例如液体不让送（我定了咖啡，最后送外卖小哥了，还和小哥加了微信，小哥表示以后需要直接发微信），不能点熟食，只能是面包一类的。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/10.jpg&quot; alt=&quot;还是外卖好&quot; title=&quot;还是外卖好&quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;还是外卖好&lt;/p&gt;&lt;/center&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/11.jpg&quot; alt=&quot;就是这个，没法进来  &quot; title=&quot;就是这个，没法进来  &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;就是这个，没法进来  &lt;/p&gt;&lt;/center&gt;
&lt;p&gt;刚收完外卖，午餐就送到到了，午餐依旧是营养均衡，没啥胃口，吃了两口面包，最重要的是午餐有一个粉条炒什么肠的，真是没法吃&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/12.jpg&quot; alt=&quot;午餐  &quot; title=&quot;午餐  &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;午餐  &lt;/p&gt;&lt;/center&gt;
&lt;p&gt;一下午低头看着电脑，脖子非常酸，送饭倒是及时，5点10分左右就送到了，这次还有个鸡腿，这里的生活太规律了，规律的让人有些不适应，每餐都要浪费很多。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/13.jpg&quot; alt=&quot;晚餐  &quot; title=&quot;晚餐  &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;晚餐  &lt;/p&gt;&lt;/center&gt;
&lt;p&gt;6点左右启动吃晚饭，生活倒是健康规律，吃完睡了一会儿，睡的脑子疼，期待明天赶紧放出去，回归正常生活。
当天晚上可能是由于适应了，也可能是由于暖和了，还睡的不错，早上如期收到了解除隔离通知书，还是有些开心，这个地方是再也不想去第二次了 。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/14.jpg&quot; alt=&quot;解除隔离通知书  &quot; title=&quot;解除隔离通知书  &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;解除隔离通知书  &lt;/p&gt;&lt;/center&gt;
&lt;p&gt;然后就是要核酸，鼻拭子，双采，一个鼻孔一下。这里有个小插曲，就是体温的时候，几次都是红的，蛮吓人的，还是护士见多识广，说是空调引起的。出门在手上意思了下，绿了。下来就是漫长的等待，等待被放出去的信号，结果一直等不到，索性跟社区联系，打了无数个电话，都没有接通，这里必须吐槽一下。&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/15.jpg&quot; alt=&quot;这个是解除隔离须知  &quot; title=&quot;这个是解除隔离须知   &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;这个是解除隔离须知   &lt;/p&gt;&lt;/center&gt;
&lt;p&gt;下午4点左右，跟群里的兄弟约了下直接走，果真就走了，出电梯的时候碰到了工作人员，查验了接解除离通知书，顺利放行，最后在拍了几张照片记录下&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/16.jpg&quot; alt=&quot;看图 &quot; title=&quot;看图   &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;看图   &lt;/p&gt;&lt;/center&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/17.jpg&quot; alt=&quot;看图 &quot; title=&quot;看图   &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;看图   &lt;/p&gt;&lt;/center&gt;
&lt;div align=&quot;center&quot;&gt;
&lt;img src=&quot;/images/隔离体验卡/18.jpg&quot; alt=&quot;看图 &quot; title=&quot;看图   &quot; /&gt;
&lt;/div&gt;
&lt;center&gt;&lt;p&gt;看图   &lt;/p&gt;&lt;/center&gt;

&lt;h3 id=&quot;终章&quot;&gt;终章&lt;/h3&gt;

&lt;p&gt;出门打个车回到酒店，以最快的速度内外洗了一边，算是回归了正常生活。&lt;br /&gt;
这次隔离整体流程还是很正规的，社区服务人员也很礼貌，而且整个过程都是免费的。唯一的遗憾就是自己可能不太适应，整个两天感觉被偷走了一样。&lt;br /&gt;
最后，大家一定要做好防护，非必要，不流动！&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="感悟" /><category term="心得" /><category term="感悟" /><summary type="html">一直以来总觉得疫情隔离和自己很远，去年多次居家隔离也是走走形式，但是最近一次的体验确实工作后很多年第一次经历。从最初的略有期待（想看看到底是什么情况），到中途的焦躁不安，再到最后的重获自由，整个过程跟做梦一样。简单的把体验分享一下。 起源 4月12号由于工作需要出差到成都，其实成都的防疫情况在我心里一直认为做的比较好，我也始终坚信成都是一个拥有科学防疫办法的城市，所以本次出差并没有考虑太多疫情相关的因素。出发来成都前专门在家做了一次核酸，按照成都的防疫政策，到成都后还需要在本地再做一次，为了不变黄码，13号中午放弃了午睡的时间去郫都区人民医院又做了1次核酸。按说已经做了2次核酸，期间一直在研究所办公，周五晚上还和同事去聚餐，已经完完全全的融入了成都人群。 然而，昨天（4月17日）凌晨，安朴通知10号后来成都的人要居家隔离，看完没当回事，居家隔离嘛，咱有经验。但是后边的消息给我了一个大大的问号，安朴表示自身并没有隔离条件，社区会安排我们隔离。此时的我还以为就是换个酒店而已。 整个过程很魔幻，大家还是很配合社区工作。 4月17日下午2点多，大家收拾好行李，我们一行人集中上了一辆不知道去哪里的大巴，在车上听到同行人问另一个“有没有带床单、被罩”，我承认，我当时有些慌了，这绝不是换个酒店那么简单。事实也没有让我失望，我被拉到了这个地方一个一墙之隔的地方。整个过程到现在想起来都很魔幻，但是我们还是大家还是很配合社区工作。 梦开始的地方 到达 当天下午2点48，我们一行人来到了隔离点，有点像电影里接头的场景，一切遵从指令，不能从正常门进入，被大白带着从车库进入。 进入车库后，整个车库就一个大白在等着我们，车库入口大门随即关闭，有种紧张的感觉。 已经关闭的大门 然后就是标准动作，大白叫名字，发防护用品（酒精，一个3m口罩，健康告知书，门牌纸），集中消毒后上楼，消毒完成我们10个人挤在一部电梯了上了楼。我的房间是1xx4，里边设施十分简陋，好在需要的日用品还是都有准备（洗漱用品、热水壶、盆，应有尽有）。这几天恰逢成都降温，整个房间应该是由于长时间没人住的原因，非常的冷。进到房间后落差特别大，恨不得马上离开，简单的铺了下床，就算是可以“安家”了，袋子里有床单，被罩，看着像是新的（走的时候才知道应该是用完洗过的）。 大白集中给箱子消毒 放在门口桌子上的日用品 这个床看起来还是有些伤感 由于房间很冷，就打算睡一会，也是由于冷就没睡着，主要是床给人的压迫感太强了，也不敢脱衣服睡。这里要划线说一点，房子不大，但是有两台挂式空调，可谓豪华配置，但是没有找到空调遥控。好在我是华为手机，有遥控功能，索性双开，这里为自己的机智点赞。 被我打开的空调 送餐很及时，5点半左右，送来晚餐，两荤两素，可谓营养均衡，味道不好（主要还是因为环境，总觉得不干净），量一般，就当减肥了。庆幸自己中午定了一张披萨。 豪华晚餐 由于是周末，下来就是漫长的无聊时间，一点消息都没有，甚至有些绝望，没有人知道外边是啥情况，要隔离到多久，没有一点消息。给街道社区打电话，意料之中的没人接，继续等吧。好消息是晚上房间慢慢的暖和起来，想到对门也是我司员工，于是在门口看了他的电话号码，加了个微信，聊了会儿，拉了群互通有无。 我们的微信群 可能是由于环境不好，晚上一直睡不着，迷迷糊糊的，等差不多了，已经到早晨了，8点半左右，开始发早餐，早餐也是很讲究搭配，但是没胃口 。 丰盛的早餐 简单的洗漱了下开始了“居家”办公，工作起来好多了，不知不觉到了中午，想着每次送的饭都不是很好吃就定了个外卖。和生活负责人（对，你没看错，我们有生活负责人）确定了要求，这里要强调下，外卖是不能直接送过来的，而且有很多要求，例如液体不让送（我定了咖啡，最后送外卖小哥了，还和小哥加了微信，小哥表示以后需要直接发微信），不能点熟食，只能是面包一类的。 还是外卖好 就是这个，没法进来 刚收完外卖，午餐就送到到了，午餐依旧是营养均衡，没啥胃口，吃了两口面包，最重要的是午餐有一个粉条炒什么肠的，真是没法吃 午餐 一下午低头看着电脑，脖子非常酸，送饭倒是及时，5点10分左右就送到了，这次还有个鸡腿，这里的生活太规律了，规律的让人有些不适应，每餐都要浪费很多。 晚餐 6点左右启动吃晚饭，生活倒是健康规律，吃完睡了一会儿，睡的脑子疼，期待明天赶紧放出去，回归正常生活。 当天晚上可能是由于适应了，也可能是由于暖和了，还睡的不错，早上如期收到了解除隔离通知书，还是有些开心，这个地方是再也不想去第二次了 。 解除隔离通知书 然后就是要核酸，鼻拭子，双采，一个鼻孔一下。这里有个小插曲，就是体温的时候，几次都是红的，蛮吓人的，还是护士见多识广，说是空调引起的。出门在手上意思了下，绿了。下来就是漫长的等待，等待被放出去的信号，结果一直等不到，索性跟社区联系，打了无数个电话，都没有接通，这里必须吐槽一下。 这个是解除隔离须知 下午4点左右，跟群里的兄弟约了下直接走，果真就走了，出电梯的时候碰到了工作人员，查验了接解除离通知书，顺利放行，最后在拍了几张照片记录下 看图 看图 看图 终章 出门打个车回到酒店，以最快的速度内外洗了一边，算是回归了正常生活。 这次隔离整体流程还是很正规的，社区服务人员也很礼貌，而且整个过程都是免费的。唯一的遗憾就是自己可能不太适应，整个两天感觉被偷走了一样。 最后，大家一定要做好防护，非必要，不流动！</summary></entry><entry><title type="html">Github 开发环境</title><link href="https://codecooker.cn/java/2022/02/14/Github%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83.html" rel="alternate" type="text/html" title="Github 开发环境" /><published>2022-02-14T00:00:00+08:00</published><updated>2022-02-14T00:00:00+08:00</updated><id>https://codecooker.cn/java/2022/02/14/Github%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83</id><content type="html" xml:base="https://codecooker.cn/java/2022/02/14/Github%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83.html">&lt;p&gt;github提供在线编辑器，只需要将com改成dev即可。&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="Java" /><category term="Java" /><summary type="html">github提供在线编辑器，只需要将com改成dev即可。</summary></entry><entry><title type="html">Java ClassLoader</title><link href="https://codecooker.cn/java/2021/07/18/Java-ClassLoader.html" rel="alternate" type="text/html" title="Java ClassLoader" /><published>2021-07-18T00:00:00+08:00</published><updated>2021-07-18T00:00:00+08:00</updated><id>https://codecooker.cn/java/2021/07/18/Java%20ClassLoader</id><content type="html" xml:base="https://codecooker.cn/java/2021/07/18/Java-ClassLoader.html">&lt;p&gt;众所周知Java中的类是由&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;负责加载的，最近定位了一个关于类加载的问题，顺便回顾了下Java中类的加载机制，对Java中提供的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;进行一一说明。&lt;/p&gt;

&lt;h3 id=&quot;java中提供的classloader&quot;&gt;Java中提供的ClassLoader&lt;/h3&gt;

&lt;p&gt;开始说明该问题前不妨先看看如下代码:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 用户自定义类&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// sun.misc.Launcher$AppClassLoader@14dad5dc&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 系统基本类&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// null&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ext目录下的类&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// sun.misc.Launcher$ExtClassLoader@1c20c684&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DNSNameService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由上述代码可以看出，用户自定义的类是有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppClassLoader&lt;/code&gt;加载，ext目录的类是有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtClassLoader&lt;/code&gt;加载，系统基本类的加载器为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;。查看Java手册得知，Java语言自带有3个加载器，分别为:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Bootstrap ClassLoader&lt;/strong&gt;,最顶层的加载类，主要加载核心类库，%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Extention ClassLoader&lt;/strong&gt;, 扩展的类加载器，加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Appclass Loader&lt;/strong&gt;也称为SystemAppClass, 加载当前应用的classpath的所有类。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;双亲委托&quot;&gt;双亲委托&lt;/h3&gt;

&lt;h4 id=&quot;简单模型&quot;&gt;简单模型&lt;/h4&gt;

&lt;p&gt;要弄明白各个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;是如何工作的，在这里要引入一个新的概念&lt;strong&gt;双亲委托&lt;/strong&gt;,介绍&lt;strong&gt;双亲委托&lt;/strong&gt;之前，我们先看&lt;em&gt;ExtClassLoader&lt;/em&gt;、&lt;em&gt;AppClassLoader&lt;/em&gt;的源码。从源码中，可以看出一个前提，每个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;内都包含一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent ClassLoader&lt;/code&gt;引用，可用通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getParent()&lt;/code&gt;获取。(用法后边在展开)&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppClassLoader&lt;/code&gt;在加载类时会调用如下方法:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loadClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassNotFoundException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;46&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;SecurityManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSecurityManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;var4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;checkPackageAccess&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ucp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;knownToNotExist&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findLoadedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resolveClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ClassNotFoundException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;loadClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;从源码中可以看出，首先&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppClassLoader&lt;/code&gt;会判断class是否已经加载，如果已经加载过则从加载过的cache中直接返回，如果不存在则调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super.loadClass&lt;/code&gt;进行加载(需要注意的是，这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super.loadClass&lt;/code&gt;只是调动父类方法，而不是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent&lt;/code&gt;去加载)。&lt;br /&gt;
跟着代码查看&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super.loadClass&lt;/code&gt;方法：&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loadClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassNotFoundException&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;synchronized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getClassLoadingLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// First, check if the class has already been loaded&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findLoadedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;nanoTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;loadClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findBootstrapClassOrNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ClassNotFoundException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// ClassNotFoundException thrown if class not found&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// from the non-null parent class loader&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// If still not found, then invoke findClass in order&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// to find the class.&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;nanoTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// this is the defining class loader; record the stats&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sun&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;misc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PerfCounter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParentDelegationTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sun&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;misc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PerfCounter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFindClassTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addElapsedTimeFrom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;sun&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;misc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;PerfCounter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFindClasses&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;resolveClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;可以很清楚的看到，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super.loadClass&lt;/code&gt;同样在cache中查找class是否已经加载，如果加载了直接返回，如果没有加载的话，则调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loadclass&lt;/code&gt;方法加载，当&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loadclass&lt;/code&gt;为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;时，再调用自身的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findClass&lt;/code&gt;进行查找class，这样就形成了一个双向查找链，大致如下:&lt;br /&gt;
&lt;img src=&quot;/images/Java_ClassLoader/1.png&quot; alt=&quot;整体框架&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;首先在cache中找，cache中找不到也不自己加载，而是委托Parent加载&lt;/li&gt;
  &lt;li&gt;Parent加载不到再委托子加载器按照反方向逐级加载&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如上，就是&lt;strong&gt;双亲委托&lt;/strong&gt;的简单模型。&lt;/p&gt;

&lt;h4 id=&quot;谁是谁的parent&quot;&gt;谁是谁的Parent&lt;/h4&gt;

&lt;p&gt;同样，先看如下代码,Java虚拟机启动时的流程:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ExtClassLoader&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ExtClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getExtClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InternalError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Could not create extension class loader&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Launcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AppClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAppClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;var1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InternalError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Could not create application class loader&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在Java虚拟机启动的时分别加载了&lt;em&gt;ExtClassLoader&lt;/em&gt;,&lt;em&gt;AppClassLoader&lt;/em&gt;。  &lt;br /&gt;
分别打印各个loader的Parent，如下:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 用户自定义类,sun.misc.Launcher$AppClassLoader@14dad5dc&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// AppClassLoader-&amp;gt;Parent,sun.misc.Launcher$ExtClassLoader@681a9515&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ExtClassLoader-&amp;gt;Parent,null&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getClassLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getParent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;由结果可以看出，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtClassLoader&lt;/code&gt;的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent&lt;/code&gt;为null，并不代表&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtClassLoader&lt;/code&gt;没有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Parent&lt;/code&gt;，而是因为他是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bootstrap ClassLoader&lt;/code&gt;,由于&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bootstrap ClassLoader&lt;/code&gt;是启动加载器，属于Jvm的一部分，有C/C++编写，在Jvm中处理，所以Java中自然就为null，细心的同学可以在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Launcher.class&lt;/code&gt;中发现线索，在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Launcher.class&lt;/code&gt;中有如下配置：&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bootClassPath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sun.boot.class.path&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Bootstrap加载器在启动时通过&lt;em&gt;sun.boot.class.path&lt;/em&gt;,来加载启动路径下的class.&lt;/p&gt;

&lt;h4 id=&quot;为什么双亲委托&quot;&gt;为什么双亲委托&lt;/h4&gt;

&lt;p&gt;谈这个问题，不妨回过头来看看Java的class加载顺序有什么限制，系统类、扩展类都是有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bootstrap ClassLoader&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtClassLoader&lt;/code&gt;加载，无论是否自定义自己的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassLoader&lt;/code&gt;都无法实现覆写诸如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt;这些基本类的操作，即保留了灵活性，也保证了JVM的运行安全。&lt;/p&gt;

&lt;h3 id=&quot;自定义classloader&quot;&gt;自定义ClassLoader&lt;/h3&gt;

&lt;p&gt;自定义ClassLoader可以有效的保证类隔离，避免互相冲突加载，影响业务功能，同时可以根据业务自身需求提供多种加载方式，例如从网络上加载对应的class，或者在加载class做一些预处理。&lt;br /&gt;
定义自己的ClassLoader，同时需要指定Parent，完成&lt;strong&gt;双亲代理&lt;/strong&gt;的双向链条，通常情况下我们可以指定为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppClassLoader&lt;/code&gt;,如果不指定则默认为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppClassLoader&lt;/code&gt;，如下。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;findClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassNotFoundException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getFileName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;FileInputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FileInputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ByteArrayOutputStream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toByteArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;bos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;defineClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;printStackTrace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 获取要加载 的class文件名&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getFileName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'.'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.class&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.class&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;其中最重要的一点就是调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defineClass&lt;/code&gt;方法，完成二进制文件到class的定义。&lt;br /&gt;
至此，关于Java的ClassLoader总结到一段落。&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="Java" /><category term="Java" /><summary type="html">众所周知Java中的类是由ClassLoader负责加载的，最近定位了一个关于类加载的问题，顺便回顾了下Java中类的加载机制，对Java中提供的ClassLoader进行一一说明。 Java中提供的ClassLoader 开始说明该问题前不妨先看看如下代码: public static void main(String[] args) { // 用户自定义类 // sun.misc.Launcher$AppClassLoader@14dad5dc System.out.println(Application.class.getClassLoader()); // 系统基本类 // null System.out.println(String.class.getClassLoader()); // ext目录下的类 // sun.misc.Launcher$ExtClassLoader@1c20c684 System.out.println(DNSNameService.class.getClassLoader()); } 由上述代码可以看出，用户自定义的类是有AppClassLoader加载，ext目录的类是有ExtClassLoader加载，系统基本类的加载器为null。查看Java手册得知，Java语言自带有3个加载器，分别为: Bootstrap ClassLoader,最顶层的加载类，主要加载核心类库，%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。 Extention ClassLoader, 扩展的类加载器，加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。 Appclass Loader也称为SystemAppClass, 加载当前应用的classpath的所有类。 双亲委托 简单模型 要弄明白各个ClassLoader是如何工作的，在这里要引入一个新的概念双亲委托,介绍双亲委托之前，我们先看ExtClassLoader、AppClassLoader的源码。从源码中，可以看出一个前提，每个ClassLoader内都包含一个Parent ClassLoader引用，可用通过ClassLoader中的getParent()获取。(用法后边在展开) AppClassLoader在加载类时会调用如下方法: public Class&amp;lt;?&amp;gt; loadClass(String var1, boolean var2) throws ClassNotFoundException { int var3 = var1.lastIndexOf(46); if (var3 != -1) { SecurityManager var4 = System.getSecurityManager(); if (var4 != null) { var4.checkPackageAccess(var1.substring(0, var3)); } } if (this.ucp.knownToNotExist(var1)) { Class var5 = this.findLoadedClass(var1); if (var5 != null) { if (var2) { this.resolveClass(var5); } return var5; } else { throw new ClassNotFoundException(var1); } } else { return super.loadClass(var1, var2); } } 从源码中可以看出，首先AppClassLoader会判断class是否已经加载，如果已经加载过则从加载过的cache中直接返回，如果不存在则调用super.loadClass进行加载(需要注意的是，这里的super.loadClass只是调动父类方法，而不是Parent去加载)。 跟着代码查看super.loadClass方法： protected Class&amp;lt;?&amp;gt; loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class&amp;lt;?&amp;gt; c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } 可以很清楚的看到，super.loadClass同样在cache中查找class是否已经加载，如果加载了直接返回，如果没有加载的话，则调用Parent的loadclass方法加载，当Parent的loadclass为null时，再调用自身的findClass进行查找class，这样就形成了一个双向查找链，大致如下: 首先在cache中找，cache中找不到也不自己加载，而是委托Parent加载 Parent加载不到再委托子加载器按照反方向逐级加载 如上，就是双亲委托的简单模型。 谁是谁的Parent 同样，先看如下代码,Java虚拟机启动时的流程: public Launcher() { Launcher.ExtClassLoader var1; try { var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError(&quot;Could not create extension class loader&quot;, var10); } try { this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError(&quot;Could not create application class loader&quot;, var9); } ... } 在Java虚拟机启动的时分别加载了ExtClassLoader,AppClassLoader。 分别打印各个loader的Parent，如下: public static void main(String[] args) { // 用户自定义类,sun.misc.Launcher$AppClassLoader@14dad5dc System.out.println(Application.class.getClassLoader()); // AppClassLoader-&amp;gt;Parent,sun.misc.Launcher$ExtClassLoader@681a9515 System.out.println(Application.class.getClassLoader().getParent()); // ExtClassLoader-&amp;gt;Parent,null System.out.println(Application.class.getClassLoader().getParent().getParent()); } 由结果可以看出，ExtClassLoader的Parent为null，并不代表ExtClassLoader没有Parent，而是因为他是Bootstrap ClassLoader,由于Bootstrap ClassLoader是启动加载器，属于Jvm的一部分，有C/C++编写，在Jvm中处理，所以Java中自然就为null，细心的同学可以在Launcher.class中发现线索，在Launcher.class中有如下配置： private static String bootClassPath = System.getProperty(&quot;sun.boot.class.path&quot;); Bootstrap加载器在启动时通过sun.boot.class.path,来加载启动路径下的class. 为什么双亲委托 谈这个问题，不妨回过头来看看Java的class加载顺序有什么限制，系统类、扩展类都是有Bootstrap ClassLoader，ExtClassLoader加载，无论是否自定义自己的ClassLoader都无法实现覆写诸如String这些基本类的操作，即保留了灵活性，也保证了JVM的运行安全。 自定义ClassLoader 自定义ClassLoader可以有效的保证类隔离，避免互相冲突加载，影响业务功能，同时可以根据业务自身需求提供多种加载方式，例如从网络上加载对应的class，或者在加载class做一些预处理。 定义自己的ClassLoader，同时需要指定Parent，完成双亲代理的双向链条，通常情况下我们可以指定为AppClassLoader,如果不指定则默认为AppClassLoader，如下。 @Override protected Class&amp;lt;?&amp;gt; findClass(String name) throws ClassNotFoundException { String fileName = getFileName(name); File file = new File(fileName); try { FileInputStream is = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len = 0; try { while ((len = is.read()) != -1) { bos.write(len); } } catch (IOException e) { e.printStackTrace(); } byte[] data = bos.toByteArray(); is.close(); bos.close(); return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); } return super.findClass(name); } // 获取要加载 的class文件名 private String getFileName(String name) { int index = name.lastIndexOf('.'); if(index == -1){ return name+&quot;.class&quot;; }else{ return name.substring(index+1)+&quot;.class&quot;; } } 其中最重要的一点就是调用defineClass方法，完成二进制文件到class的定义。 至此，关于Java的ClassLoader总结到一段落。</summary></entry><entry><title type="html">人生三重境界</title><link href="https://codecooker.cn/%E6%84%9F%E6%82%9F/2020/11/22/%E4%BA%BA%E7%94%9F%E4%B8%89%E9%87%8D%E5%A2%83%E7%95%8C.html" rel="alternate" type="text/html" title="人生三重境界" /><published>2020-11-22T00:00:00+08:00</published><updated>2020-11-22T00:00:00+08:00</updated><id>https://codecooker.cn/%E6%84%9F%E6%82%9F/2020/11/22/%E4%BA%BA%E7%94%9F%E4%B8%89%E9%87%8D%E5%A2%83%E7%95%8C</id><content type="html" xml:base="https://codecooker.cn/%E6%84%9F%E6%82%9F/2020/11/22/%E4%BA%BA%E7%94%9F%E4%B8%89%E9%87%8D%E5%A2%83%E7%95%8C.html">&lt;p&gt;最近在搞一个务虚的事情，从痛点分析到目标制定再到措施落地，目标要可量化，措施要可执行，结果要可衡量。&lt;br /&gt;
如何衡量一个人的能力，通常会转换成另一个一般化指标，例如，考试分数可以比较客观的反应学生能力，但不能完全代表学生的能力。&lt;br /&gt;
所以我们可以将我们的衡量标准价值导向绑定，假设我们绩效导向为代码量，那这个我们定义的能力强就需要包含开发代码量多。&lt;/p&gt;

&lt;h3 id=&quot;立&quot;&gt;立&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;昨夜西风凋碧树，独上高楼，望尽天涯路&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;该阶段需要“立”志，确定目标，也就是价值导向。&lt;/p&gt;

&lt;h3 id=&quot;守&quot;&gt;守&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;衣带渐宽终不悔，为伊消得人憔悴&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;主要为了目标不断的求索，努力。&lt;/p&gt;

&lt;h3 id=&quot;得&quot;&gt;得&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;众里寻他千百度，蓦然回首，那人却在灯火阑珊处&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;付出努力，一步一个脚印，结果（真理）会来找我们，这是一个自然而然的过程。&lt;/p&gt;

&lt;h3 id=&quot;实操&quot;&gt;实操&lt;/h3&gt;

&lt;p&gt;例如公司需要员工加班，所以我们制定的精英标准需要支撑到公司的诉求。&lt;br /&gt;
那我们的目标就是，成为一个加班多的人；可以分解为如下三步：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;立：加班工时超过100h&lt;/li&gt;
  &lt;li&gt;守：拉通、扯皮、放慢节奏、工作留在加班时候干&lt;/li&gt;
  &lt;li&gt;得：2020年底实现月加班工时100h&lt;/li&gt;
&lt;/ul&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="感悟" /><category term="心得" /><category term="感悟" /><summary type="html">最近在搞一个务虚的事情，从痛点分析到目标制定再到措施落地，目标要可量化，措施要可执行，结果要可衡量。 如何衡量一个人的能力，通常会转换成另一个一般化指标，例如，考试分数可以比较客观的反应学生能力，但不能完全代表学生的能力。 所以我们可以将我们的衡量标准价值导向绑定，假设我们绩效导向为代码量，那这个我们定义的能力强就需要包含开发代码量多。 立 昨夜西风凋碧树，独上高楼，望尽天涯路 该阶段需要“立”志，确定目标，也就是价值导向。 守 衣带渐宽终不悔，为伊消得人憔悴 主要为了目标不断的求索，努力。 得 众里寻他千百度，蓦然回首，那人却在灯火阑珊处 付出努力，一步一个脚印，结果（真理）会来找我们，这是一个自然而然的过程。 实操 例如公司需要员工加班，所以我们制定的精英标准需要支撑到公司的诉求。 那我们的目标就是，成为一个加班多的人；可以分解为如下三步： 立：加班工时超过100h 守：拉通、扯皮、放慢节奏、工作留在加班时候干 得：2020年底实现月加班工时100h</summary></entry><entry><title type="html">Mac用户构建Linux开发环境</title><link href="https://codecooker.cn/linux/2017/12/10/Mac%E7%94%A8%E6%88%B7%E6%9E%84%E5%BB%BALinux%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83.html" rel="alternate" type="text/html" title="Mac用户构建Linux开发环境" /><published>2017-12-10T00:00:00+08:00</published><updated>2017-12-10T00:00:00+08:00</updated><id>https://codecooker.cn/linux/2017/12/10/Mac%E7%94%A8%E6%88%B7%E6%9E%84%E5%BB%BALinux%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83</id><content type="html" xml:base="https://codecooker.cn/linux/2017/12/10/Mac%E7%94%A8%E6%88%B7%E6%9E%84%E5%BB%BALinux%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83.html">&lt;p&gt;最近可能会从Mac暂时迁移至Linux环境进行常规软件开发，所以本周末利用空隙时间从Linux发行版本到常用软件再到开发环境的准备做了一个大致的梳理。由于我是重度Mac使用者，所以我会分大概3篇文章来说明怎么准备一个Linux系统便于Mac重度开发者迁移。&lt;/p&gt;

&lt;h3 id=&quot;发行版的选择&quot;&gt;发行版的选择&lt;/h3&gt;
&lt;p&gt;在发行版的选择上，本人也有过纠结，毕竟Linux发行版众多，选出一种合适的发行版确实是一个头疼的事儿。经过慎重考虑，我从Centos、Fedora、ArchLinux、Ubuntu、LinuxMint中选择了LinuxMint。具体原因如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;LinuxMint是基于Ubuntu的发行版，在Ubuntu的基础了做了一些改变。Ubuntu的优点就是软件支持性好，生态好，这样其实LinuxMint也就可以轻松的借力Ubuntu的软件和社区力量&lt;/li&gt;
  &lt;li&gt;LinuxMint的桌面环境为基于GNOME Shell的Cinnamon及基于GNOME 2的MATE，另有KDE及Xfce的独立版本可供选择。这个对于不喜欢unity的人是个很好的选择&lt;/li&gt;
  &lt;li&gt;LinuxMint入门很简单，开闭源驱动支持的很好，当然这点跟Ubuntu没啥区别&lt;/li&gt;
  &lt;li&gt;家里电脑也装的这个系统&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Linux Mint是由Linux Mint Team团队于2006年开始发行，是一份基于Debian和Ubuntu的Linux发行版。其目标是提供一种更完整的即刻可用体验，这包括提供浏览器插件、多媒体编解码器、对DVD播放的支持、Java和其他组件，它也增加了一套定制桌面及各种菜单，一些独特的配置工具，以及一份基于web的软件包安装界面。Linux Mint是对用户友好而功能强大的操作系统。它诞生的目的是为家庭用户和企业提供一个免费的，易用的，舒适而优雅的桌面操作系统。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;版本选择&quot;&gt;版本选择&lt;/h3&gt;
&lt;p&gt;这个不用太纠结，直接选择最新的就行。现在家里用的是半年前安装的18.1，昨天做了最新的安装镜像，18.3，如果条件允许的话，用最新的系统应该不会有什么坏处。这点上不像Ubuntu会有LTS版本，让我这些选择恐惧症的人在这个节点不知道是选择16.04还是明年的18.04&lt;/p&gt;

&lt;h3 id=&quot;制作安装镜像&quot;&gt;制作安装镜像&lt;/h3&gt;
&lt;h4 id=&quot;第一步下载镜像&quot;&gt;第一步，下载镜像&lt;/h4&gt;
&lt;p&gt;首先下载最新的安装镜像，这个可以直接从官网获取。点击&lt;a href=&quot;https://linuxmint.com/download.php&quot;&gt;这里打开&lt;/a&gt;，记得选择比较近的节点进行下载，中科大有个源是支持的，可以在下载的时候进行选择&lt;/p&gt;
&lt;h4 id=&quot;第二步制作安装盘&quot;&gt;第二步，制作安装盘&lt;/h4&gt;
&lt;p&gt;准备一个不小于2G的优盘，不小于4G最好。下载&lt;a href=&quot;https://www.pendrivelinux.com/tag/universal-usb-installer/&quot;&gt;Universal USB Installer&lt;/a&gt;，按照提示一步一步的操作就行，如图(网上盗的图，见谅)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.tweaking4all.nl/wp-content/uploads/sites/2/2014/07/universal-usb-installer-create.jpg&quot; alt=&quot;universal-usb-installer-create&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;安装&quot;&gt;安装&lt;/h3&gt;
&lt;p&gt;制作完成安装镜像后，就可以重启电脑，然后选择由优盘引导启动进行安装了。这个是Live
CD制作的，可以在体验的过程中完成安装。如果是初次安装Linux的话，一点要有几点注意事项：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;双系统的话切记备份重要数据再开始安装&lt;/li&gt;
  &lt;li&gt;不要被那么多的挂载点搞晕，一般情况下，一个swap分区+一个/挂载点就可以了。因为之所以那么多挂载点历史原因比较大&lt;/li&gt;
  &lt;li&gt;如果是SSD+HDD的电脑，可以外加一个home挂载点，可选一个opt挂载点，这两个挂载点分区至HDD，/挂载点分区至SSD。新的MBR可以写在另一块硬盘上&lt;/li&gt;
  &lt;li&gt;最好备用网线链接网络的方式，因为无线网卡很有可能并不能正确的安装驱动&lt;/li&gt;
  &lt;li&gt;如果显卡过新，安装完后可能会导致驱动没有，这个问题也不要惊恐，据我发现，一般A卡的开源驱动都是不错的，N卡的话官方有提供驱动，按照型号去下载安装&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;安装完成&quot;&gt;安装完成&lt;/h3&gt;
&lt;p&gt;安装过程一般会很快完成，完成后就可以进行体验了。下一篇我会重点说下如果打造一个和MacOS很相近的开发系统，然后会根据不同的工种开分别说下搭建环境的问题。&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="Linux" /><category term="Mac" /><category term="Linux" /><summary type="html">最近可能会从Mac暂时迁移至Linux环境进行常规软件开发，所以本周末利用空隙时间从Linux发行版本到常用软件再到开发环境的准备做了一个大致的梳理。由于我是重度Mac使用者，所以我会分大概3篇文章来说明怎么准备一个Linux系统便于Mac重度开发者迁移。 发行版的选择 在发行版的选择上，本人也有过纠结，毕竟Linux发行版众多，选出一种合适的发行版确实是一个头疼的事儿。经过慎重考虑，我从Centos、Fedora、ArchLinux、Ubuntu、LinuxMint中选择了LinuxMint。具体原因如下： LinuxMint是基于Ubuntu的发行版，在Ubuntu的基础了做了一些改变。Ubuntu的优点就是软件支持性好，生态好，这样其实LinuxMint也就可以轻松的借力Ubuntu的软件和社区力量 LinuxMint的桌面环境为基于GNOME Shell的Cinnamon及基于GNOME 2的MATE，另有KDE及Xfce的独立版本可供选择。这个对于不喜欢unity的人是个很好的选择 LinuxMint入门很简单，开闭源驱动支持的很好，当然这点跟Ubuntu没啥区别 家里电脑也装的这个系统 Linux Mint是由Linux Mint Team团队于2006年开始发行，是一份基于Debian和Ubuntu的Linux发行版。其目标是提供一种更完整的即刻可用体验，这包括提供浏览器插件、多媒体编解码器、对DVD播放的支持、Java和其他组件，它也增加了一套定制桌面及各种菜单，一些独特的配置工具，以及一份基于web的软件包安装界面。Linux Mint是对用户友好而功能强大的操作系统。它诞生的目的是为家庭用户和企业提供一个免费的，易用的，舒适而优雅的桌面操作系统。 版本选择 这个不用太纠结，直接选择最新的就行。现在家里用的是半年前安装的18.1，昨天做了最新的安装镜像，18.3，如果条件允许的话，用最新的系统应该不会有什么坏处。这点上不像Ubuntu会有LTS版本，让我这些选择恐惧症的人在这个节点不知道是选择16.04还是明年的18.04 制作安装镜像 第一步，下载镜像 首先下载最新的安装镜像，这个可以直接从官网获取。点击这里打开，记得选择比较近的节点进行下载，中科大有个源是支持的，可以在下载的时候进行选择 第二步，制作安装盘 准备一个不小于2G的优盘，不小于4G最好。下载Universal USB Installer，按照提示一步一步的操作就行，如图(网上盗的图，见谅) 安装 制作完成安装镜像后，就可以重启电脑，然后选择由优盘引导启动进行安装了。这个是Live CD制作的，可以在体验的过程中完成安装。如果是初次安装Linux的话，一点要有几点注意事项： 双系统的话切记备份重要数据再开始安装 不要被那么多的挂载点搞晕，一般情况下，一个swap分区+一个/挂载点就可以了。因为之所以那么多挂载点历史原因比较大 如果是SSD+HDD的电脑，可以外加一个home挂载点，可选一个opt挂载点，这两个挂载点分区至HDD，/挂载点分区至SSD。新的MBR可以写在另一块硬盘上 最好备用网线链接网络的方式，因为无线网卡很有可能并不能正确的安装驱动 如果显卡过新，安装完后可能会导致驱动没有，这个问题也不要惊恐，据我发现，一般A卡的开源驱动都是不错的，N卡的话官方有提供驱动，按照型号去下载安装 安装完成 安装过程一般会很快完成，完成后就可以进行体验了。下一篇我会重点说下如果打造一个和MacOS很相近的开发系统，然后会根据不同的工种开分别说下搭建环境的问题。</summary></entry><entry><title type="html">Java泛型函数</title><link href="https://codecooker.cn/java/2017/08/21/Java%E6%B3%9B%E5%9E%8B%E5%87%BD%E6%95%B0.html" rel="alternate" type="text/html" title="Java泛型函数" /><published>2017-08-21T00:00:00+08:00</published><updated>2017-08-21T00:00:00+08:00</updated><id>https://codecooker.cn/java/2017/08/21/Java%E6%B3%9B%E5%9E%8B%E5%87%BD%E6%95%B0</id><content type="html" xml:base="https://codecooker.cn/java/2017/08/21/Java%E6%B3%9B%E5%9E%8B%E5%87%BD%E6%95%B0.html">&lt;h3 id=&quot;背景&quot;&gt;背景&lt;/h3&gt;
&lt;p&gt;PHP的Array以方便灵活著称，再加上丰富的库函数，可以让开发者灵活的应对大部分需求。经常和数据库打交道的同学，肯定熟悉这个方法&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array_column&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;array_column() 返回input数组中键值为column_key的列， 如果指定了可选参数index_key，那么input数组中的这一列的值将作为返回数组中对应值的键。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;做Java后，我们也常碰到需要如此操作的场景，但并没有合适的库函数，于是本着解放劳动力的出发点，打算自己开发一个。但是由于Java是一个强类型语言，我们经常会碰到如此的类型&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;，如下：&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;codecooker&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;school&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UserInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;students&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;


    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getStudentNameList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 原始的做法，我们需要遍历&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;studentName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;UserInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;students&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;studentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;userName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;studentName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;泛型方法&quot;&gt;泛型方法&lt;/h3&gt;
&lt;p&gt;解决方案是由于看到系统函数的实现有所启发，想到了泛型函数。下面从自己的理解对泛型函数做一个简单的解释&lt;br /&gt;
一般的泛型函数形式如下：  &lt;br /&gt;
 &lt;img src=&quot;/images/Java泛型函数/1503304195862.jpg&quot; alt=&quot;session的工作机制&quot; /&gt;
其中&lt;br /&gt;
&lt;strong&gt;1&lt;/strong&gt;表示在此泛型函数中我们定义要使用的泛型，这里只代表一个符号，可以试T也可以是&lt;br /&gt;
&lt;strong&gt;2&lt;/strong&gt;表示该泛型函数的返回类型，必须是已经定义过的类型，比如是T或者U&lt;br /&gt;
&lt;strong&gt;3&lt;/strong&gt;方法名&lt;br /&gt;
&lt;strong&gt;4&lt;/strong&gt;、&lt;strong&gt;5&lt;/strong&gt;参数列表&lt;br /&gt;
这里需要注意区分泛型方法和不同泛型类内使用泛型的方法的区别，例如:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 泛型方法&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clounms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 不同类成员方法，只不过是返回了一个类定义时的泛型列表&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getALl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;其实，按照我的理解，最重要的区别就是是否有在方法定义时有泛型的定义过程，如果有则是泛型方法。&lt;/p&gt;

&lt;h3 id=&quot;实现array_column方法&quot;&gt;实现array_column方法&lt;/h3&gt;
&lt;p&gt;有了上面的铺垫，我们可以定义简单的泛型方法来实现&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array_column&lt;/code&gt;的功能&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ListUtil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clounms&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如此，完成&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="Java" /><category term="Java" /><summary type="html">背景 PHP的Array以方便灵活著称，再加上丰富的库函数，可以让开发者灵活的应对大部分需求。经常和数据库打交道的同学，肯定熟悉这个方法array_column array_column() 返回input数组中键值为column_key的列， 如果指定了可选参数index_key，那么input数组中的这一列的值将作为返回数组中对应值的键。 做Java后，我们也常碰到需要如此操作的场景，但并没有合适的库函数，于是本着解放劳动力的出发点，打算自己开发一个。但是由于Java是一个强类型语言，我们经常会碰到如此的类型List&amp;lt;T&amp;gt;，如下： public class UserInfo { private String userName = &quot;codecooker&quot;; private Integer age = 18; } public class school { private List&amp;lt;UserInfo&amp;gt; students; public List&amp;lt;String&amp;gt; getStudentNameList() { // 原始的做法，我们需要遍历 List&amp;lt;String&amp;gt; studentName = new ArrayList&amp;lt;&amp;gt;(); for (UserInfo usInfo : this.students) { studentName.add(usInfo.userName); } return studentName; } } 泛型方法 解决方案是由于看到系统函数的实现有所启发，想到了泛型函数。下面从自己的理解对泛型函数做一个简单的解释 一般的泛型函数形式如下： 其中 1表示在此泛型函数中我们定义要使用的泛型，这里只代表一个符号，可以试T也可以是 2表示该泛型函数的返回类型，必须是已经定义过的类型，比如是T或者U 3方法名 4、5参数列表 这里需要注意区分泛型方法和不同泛型类内使用泛型的方法的区别，例如: public class Test&amp;lt;T&amp;gt; { // 泛型方法 public static &amp;lt;T,U&amp;gt; List&amp;lt;T&amp;gt; clounms(List&amp;lt;U&amp;gt; list, Function&amp;lt;U,T&amp;gt; function); // 不同类成员方法，只不过是返回了一个类定义时的泛型列表 public List&amp;lt;T&amp;gt; getALl(); } 其实，按照我的理解，最重要的区别就是是否有在方法定义时有泛型的定义过程，如果有则是泛型方法。 实现array_column方法 有了上面的铺垫，我们可以定义简单的泛型方法来实现array_column的功能 public class ListUtil { public static &amp;lt;T,U&amp;gt; List&amp;lt;T&amp;gt; clounms(List&amp;lt;U&amp;gt; list, Function&amp;lt;U,T&amp;gt; function) { List&amp;lt;T&amp;gt; result = new ArrayList&amp;lt;&amp;gt;(); for (U u : list) { result.add(function.apply(u)); } return result; } } 如此，完成</summary></entry><entry><title type="html">Linux安装PHP7.x</title><link href="https://codecooker.cn/php/2017/02/17/Linux%E5%AE%89%E8%A3%85PHP7.x.html" rel="alternate" type="text/html" title="Linux安装PHP7.x" /><published>2017-02-17T00:00:00+08:00</published><updated>2017-02-17T00:00:00+08:00</updated><id>https://codecooker.cn/php/2017/02/17/Linux%E5%AE%89%E8%A3%85PHP7.x</id><content type="html" xml:base="https://codecooker.cn/php/2017/02/17/Linux%E5%AE%89%E8%A3%85PHP7.x.html">&lt;p&gt;众所周知，PHP7的性能和执行速度都有了很大的改进。恰逢最近公司的一些项目也在尝试着迁移到PHP7的环境，由于本人平时开发使用MAC和Linux，而且不愿意使用现成的批量部署工具（不是愿意重复造轮子，而是想知道到底安装了哪些东西）。所以就选择自己编译安装环境。&lt;/p&gt;

&lt;h3 id=&quot;获取源代码&quot;&gt;获取源代码&lt;/h3&gt;
&lt;p&gt;PHP源代码是开源的，我们可以在PHP的官方下载我们需要的源码包，这里我们以PHP7.1.1为例&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 下载源码&lt;/span&gt;
wget http://cn2.php.net/get/php-7.1.2.tar.gz/from/this/mirror
&lt;span class=&quot;c&quot;&gt;# 解压&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tar &lt;/span&gt;xvf mirror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;安装依赖库&quot;&gt;安装依赖库&lt;/h3&gt;
&lt;p&gt;下面就列举下常用的Centos7.x和Ubuntu系的依赖安装&lt;/p&gt;
&lt;h4 id=&quot;centos&quot;&gt;Centos&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 包含扩展依赖的开发库&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel
&lt;span class=&quot;c&quot;&gt;# 包含编译源码必要的类库&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;yum groupinstall &lt;span class=&quot;s2&quot;&gt;&quot;Development Tools&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;ubuntu&quot;&gt;Ubuntu&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libcurl4-openssl-dev libbz2-dev libssl-dev libxml2-dev libjpeg-dev libpng12-dev libfreetype6 libfreetype6-dev libgmp3-dev libmcrypt4 libmcrypt-dev libreadline-dev libxslt1.1 libxslt-dev  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;开始编译&quot;&gt;开始编译&lt;/h3&gt;
&lt;p&gt;进入我们的源码目录，首先配置安装。安装上所有常用的扩展。注意，这里的PHP的安装目录是&lt;em&gt;/alidata/server/php&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./configure &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/alidata/server/php &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-config-file-path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-fpm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-fpm-user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nginx  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-fpm-group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nginx &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-inline-optimization&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--disable-debug&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--disable-rpath&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-shared&lt;/span&gt;  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-soap&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-libxml-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-xmlrpc&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-openssl&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-mcrypt&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-mhash&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-pcre-regex&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-sqlite3&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-zlib&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-bcmath&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-iconv&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-bz2&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-calendar&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-curl&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-cdb&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-dom&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-exif&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-fileinfo&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-filter&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-pcre-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-ftp&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-gd&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-openssl-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-jpeg-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-png-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-zlib-dir&lt;/span&gt;  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-freetype-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-gd-native-ttf&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-gd-jis-conv&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-gettext&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-gmp&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-mhash&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-json&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-mbstring&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-mbregex&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-mbregex-backtrack&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-libmbfl&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-onig&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-pdo&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-mysqli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mysqlnd &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-pdo-mysql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mysqlnd &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-zlib-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-pdo-sqlite&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-readline&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-session&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-shmop&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-simplexml&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-sockets&lt;/span&gt;  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-sysvmsg&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-sysvsem&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-sysvshm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-wddx&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-libxml-dir&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-xsl&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-zip&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-mysqlnd-compression-support&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-pear&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--enable-opcache&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--with-libdir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;lib64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;配置和检查完依赖后，下来就要开始编译了&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make

&lt;span class=&quot;c&quot;&gt;# 如果一切顺利，make结束后，安装就行。如果安装目录没有当前用户的写权限，需要上sudo&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;安装完成&quot;&gt;安装完成&lt;/h3&gt;
&lt;p&gt;安装完成后，还需要做两小步骤&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;添加/alidata/server/php/lib/php到php.ini的include_path&lt;/li&gt;
  &lt;li&gt;添加/alidata/server/php/bin 到环境变量，方便后续使用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;至此，PHP安装完成&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  /etc php &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt;
PHP 7.1.2 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;cli&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;built: Feb 17 2017 23:04:39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; NTS &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Copyright &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 1998-2017 Zend Technologie
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;常见错误处理&quot;&gt;常见错误处理&lt;/h3&gt;
&lt;h4 id=&quot;centos7-安装报如下错误&quot;&gt;Centos7 安装报如下错误&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libmcrypt libmcrypt-devel&lt;/code&gt;未找到，需要下载源码进行安装。默认安装在 /usr/local/lib&lt;/li&gt;
  &lt;li&gt;编辑文件/etc/ld.so.conf，添加/usr/local/lib到最后，执行ldconfig&lt;/li&gt;
&lt;/ul&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="PHP" /><category term="Linux" /><category term="Ubuntu" /><category term="Linuxmint" /><category term="PHP" /><summary type="html">众所周知，PHP7的性能和执行速度都有了很大的改进。恰逢最近公司的一些项目也在尝试着迁移到PHP7的环境，由于本人平时开发使用MAC和Linux，而且不愿意使用现成的批量部署工具（不是愿意重复造轮子，而是想知道到底安装了哪些东西）。所以就选择自己编译安装环境。 获取源代码 PHP源代码是开源的，我们可以在PHP的官方下载我们需要的源码包，这里我们以PHP7.1.1为例 # 下载源码 wget http://cn2.php.net/get/php-7.1.2.tar.gz/from/this/mirror # 解压 tar xvf mirror 安装依赖库 下面就列举下常用的Centos7.x和Ubuntu系的依赖安装 Centos # 包含扩展依赖的开发库 sudo yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel # 包含编译源码必要的类库 sudo yum groupinstall &quot;Development Tools&quot; Ubuntu sudo apt-get install libcurl4-openssl-dev libbz2-dev libssl-dev libxml2-dev libjpeg-dev libpng12-dev libfreetype6 libfreetype6-dev libgmp3-dev libmcrypt4 libmcrypt-dev libreadline-dev libxslt1.1 libxslt-dev 开始编译 进入我们的源码目录，首先配置安装。安装上所有常用的扩展。注意，这里的PHP的安装目录是/alidata/server/php ./configure \ --prefix=/alidata/server/php \ --with-config-file-path=/etc \ --enable-fpm \ --with-fpm-user=nginx \ --with-fpm-group=nginx \ --enable-inline-optimization \ --disable-debug \ --disable-rpath \ --enable-shared \ --enable-soap \ --with-libxml-dir \ --with-xmlrpc \ --with-openssl \ --with-mcrypt \ --with-mhash \ --with-pcre-regex \ --with-sqlite3 \ --with-zlib \ --enable-bcmath \ --with-iconv \ --with-bz2 \ --enable-calendar \ --with-curl \ --with-cdb \ --enable-dom \ --enable-exif \ --enable-fileinfo \ --enable-filter \ --with-pcre-dir \ --enable-ftp \ --with-gd \ --with-openssl-dir \ --with-jpeg-dir \ --with-png-dir \ --with-zlib-dir \ --with-freetype-dir \ --enable-gd-native-ttf \ --enable-gd-jis-conv \ --with-gettext \ --with-gmp \ --with-mhash \ --enable-json \ --enable-mbstring \ --enable-mbregex \ --enable-mbregex-backtrack \ --with-libmbfl \ --with-onig \ --enable-pdo \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ --with-zlib-dir \ --with-pdo-sqlite \ --with-readline \ --enable-session \ --enable-shmop \ --enable-simplexml \ --enable-sockets \ --enable-sysvmsg \ --enable-sysvsem \ --enable-sysvshm \ --enable-wddx \ --with-libxml-dir \ --with-xsl \ --enable-zip \ --enable-mysqlnd-compression-support \ --with-pear \ --enable-opcache \ --with-libdir=lib64 配置和检查完依赖后，下来就要开始编译了 make # 如果一切顺利，make结束后，安装就行。如果安装目录没有当前用户的写权限，需要上sudo sudo make install 安装完成 安装完成后，还需要做两小步骤 添加/alidata/server/php/lib/php到php.ini的include_path 添加/alidata/server/php/bin 到环境变量，方便后续使用 至此，PHP安装完成 ➜ /etc php -v PHP 7.1.2 (cli) (built: Feb 17 2017 23:04:39) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologie 常见错误处理 Centos7 安装报如下错误 libmcrypt libmcrypt-devel未找到，需要下载源码进行安装。默认安装在 /usr/local/lib 编辑文件/etc/ld.so.conf，添加/usr/local/lib到最后，执行ldconfig</summary></entry><entry><title type="html">ThinkPHP定时任务（一）</title><link href="https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1-%E4%B8%80.html" rel="alternate" type="text/html" title="ThinkPHP定时任务（一）" /><published>2017-01-23T00:00:00+08:00</published><updated>2017-01-23T00:00:00+08:00</updated><id>https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1(%E4%B8%80)</id><content type="html" xml:base="https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1-%E4%B8%80.html">&lt;p&gt;后台开发的过程中，免不了和定时任务打交道。每天定时的发短信给用户，定时的计算收益、利息，固定间隔的去执行某项大型任务（不适宜同步执行）等等，等等&lt;br /&gt;
常用的定时任务都是基于linux中的crontab进行，不过今天我们主要做TP框架内的定时任务实现解析，分析其实现原理和缺陷。在下一部分，会介绍一个新的方案&lt;/p&gt;

&lt;h3 id=&quot;行为扩展&quot;&gt;行为扩展&lt;/h3&gt;
&lt;p&gt;细心的朋友应该知道TP框架中有一个名叫行为扩展的东西。其实简单的概括起来就是HOOK,可以实现在某个时机进行HOOK，然后注册监听(Listen),实现代码和功能的无侵入式嵌入。行为扩展在我们的项目中使用的比较多，静态资源替换、错误模板替换（这个只有经历过才知道为什么）、自动初始化，太多的地方。总体来说，由于TP暴露了很多关键点的HOOK，我们在使用的时候，只需要去监听即可，所以使用起来还是很不错的。如果对行为扩展还有不了解的，可以去查看TP官方文档寻找答案&lt;/p&gt;

&lt;h3 id=&quot;基于行为扩展的定时任务&quot;&gt;基于行为扩展的定时任务&lt;/h3&gt;
&lt;p&gt;没错，TP的定时任务既是建立在自身的行为扩展的基础上的，如果了解了行为扩展的朋友，应该知道行为扩展只是一种变相的代码调用，所以问题就随之而来，什么时候触发定时任务？我们先来看看在框架内应该以什么样的姿势来打开Crons&lt;br /&gt;
一般情况下，我们做如下初始化：&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Common/Conf/tags.php 文件&lt;/span&gt;
&lt;span class=&quot;s1&quot;&gt;'app_begin'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'Behavior\CronRunBehavior'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Common/Conf/tags.php 文件用于快速的注册行为扩展，跟HOOK有异曲同工之处。由于这个方法比较简洁方便，且对于框架代码无嵌入，值得使用&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个注册的意思是在程序启动的时候，直接调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Behavior\CronRunBehavior&lt;/code&gt;脚本，而&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Behavior\CronRunBehavior&lt;/code&gt;脚本的任务既是对定时任务的调用&lt;/p&gt;

&lt;h3 id=&quot;时间间隔&quot;&gt;时间间隔&lt;/h3&gt;
&lt;p&gt;做定时任务，肯定要关心调用频次，这里我们看下&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CronRunBehaviorController&lt;/code&gt;的实现代码来简单的分析下这个定时任务的实现逻辑&lt;br /&gt;
如下代码:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c1&quot;&gt;// 锁定自动执行&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;$lockfile&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;no&quot;&gt;RUNTIME_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'cron.lock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lockfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;filemtime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lockfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'REQUEST_TIME'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'CRON_MAX_TIME'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lockfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;set_time_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;// 即使客户端断开连接，也要继续执行&lt;/span&gt;
 &lt;span class=&quot;nb&quot;&gt;ignore_user_abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这段代码的意思是，在执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CronRunBehaviorController&lt;/code&gt;前，大概分为如下流程:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;判断是否有锁文件，如果没有则创建锁文件，进行下一步，如有进行第2步&lt;/li&gt;
  &lt;li&gt;判断所文件的创建时间是否和当前时间间隔超过我们设置的时间间隔&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CRON_MAX_TIME&lt;/code&gt;，默认为60s，如果超过则进行下一步，反之不执行，直接退出&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;再看下面代码：&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RUNTIME_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'~crons.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$crons&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RUNTIME_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'~crons.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elseif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;COMMON_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/crons.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)){&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$crons&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;COMMON_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/crons.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这块代码可以看出来我们的定时任务配置是在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Conf/crons.php&lt;/code&gt;路径下，有runtime缓存，每次修改需要清除缓存，这里比较简单，我来贴上&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Conf/crons.php&lt;/code&gt;的配置即可&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'callback'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Agreement/Cron/call_back.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callback&lt;/code&gt;是任务脚本名称，而对于的value则是任务脚本的配置&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'Agreement/Cron/call_back.php'&lt;/code&gt;为具体执行脚本，依次为间隔时间，上次执行时间&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至此，我们的基于TP的定时任务已经全部准备就绪。&lt;/p&gt;

&lt;h3 id=&quot;写在最后&quot;&gt;写在最后&lt;/h3&gt;

&lt;h4 id=&quot;优势&quot;&gt;优势&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;方便使用，如果是用TP实现的代码逻辑，全部逻辑都可以复用&lt;/li&gt;
  &lt;li&gt;不用考虑实现具体调用细节，只需要按照规范来写就行&lt;/li&gt;
  &lt;li&gt;所有的参数都比较直白，不用和crontab中的星星打交道&lt;/li&gt;
  &lt;li&gt;对时效性不强的简单定时任务可以首选&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;缺点&quot;&gt;缺点&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;由实现逻辑可以看出，每次脚本的启动都是由客户触发，不能保证稳定的时间间隔&lt;/li&gt;
  &lt;li&gt;配置时间不是很灵活（之前为了使用，也做了些许扩展）&lt;/li&gt;
  &lt;li&gt;同步执行，很影响使用这的执行效率，如果任务脚本比较大，则会极度影响用户体验&lt;/li&gt;
  &lt;li&gt;文件锁的方式虽然好用，但是对于分布式系统，无法做到锁功能&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;尝试过的改造&quot;&gt;尝试过的改造&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;对配置文件进行了升级，支持间隔时间的配置&lt;/li&gt;
  &lt;li&gt;对执行方式进行了改造，支持基于pcntl的多进程调用，由于主进程需要先结束，为了实现逻辑最后造成进程混乱，不是很可取。&lt;/li&gt;
  &lt;li&gt;使用redis对执行加锁，完成分布式锁&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上述改造，由于第三点不好处理（也是最重要的一点），遂另辟蹊径，采用了另一种方式来处理。下一章节，我会对这个进行详细说明！&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="PHP" /><category term="PHP" /><category term="ThinkPHP" /><summary type="html">后台开发的过程中，免不了和定时任务打交道。每天定时的发短信给用户，定时的计算收益、利息，固定间隔的去执行某项大型任务（不适宜同步执行）等等，等等 常用的定时任务都是基于linux中的crontab进行，不过今天我们主要做TP框架内的定时任务实现解析，分析其实现原理和缺陷。在下一部分，会介绍一个新的方案 行为扩展 细心的朋友应该知道TP框架中有一个名叫行为扩展的东西。其实简单的概括起来就是HOOK,可以实现在某个时机进行HOOK，然后注册监听(Listen),实现代码和功能的无侵入式嵌入。行为扩展在我们的项目中使用的比较多，静态资源替换、错误模板替换（这个只有经历过才知道为什么）、自动初始化，太多的地方。总体来说，由于TP暴露了很多关键点的HOOK，我们在使用的时候，只需要去监听即可，所以使用起来还是很不错的。如果对行为扩展还有不了解的，可以去查看TP官方文档寻找答案 基于行为扩展的定时任务 没错，TP的定时任务既是建立在自身的行为扩展的基础上的，如果了解了行为扩展的朋友，应该知道行为扩展只是一种变相的代码调用，所以问题就随之而来，什么时候触发定时任务？我们先来看看在框架内应该以什么样的姿势来打开Crons 一般情况下，我们做如下初始化： // Common/Conf/tags.php 文件 'app_begin'=&amp;gt;array( 'Behavior\CronRunBehavior' ), Common/Conf/tags.php 文件用于快速的注册行为扩展，跟HOOK有异曲同工之处。由于这个方法比较简洁方便，且对于框架代码无嵌入，值得使用 这个注册的意思是在程序启动的时候，直接调用Behavior\CronRunBehavior脚本，而Behavior\CronRunBehavior脚本的任务既是对定时任务的调用 时间间隔 做定时任务，肯定要关心调用频次，这里我们看下CronRunBehaviorController的实现代码来简单的分析下这个定时任务的实现逻辑 如下代码: // 锁定自动执行 $lockfile = RUNTIME_PATH.'cron.lock'; if(is_writable($lockfile) &amp;amp;&amp;amp; filemtime($lockfile) &amp;gt; $_SERVER['REQUEST_TIME'] - C('CRON_MAX_TIME',null,60)) { return ; } else { touch($lockfile); } set_time_limit(1000); // 即使客户端断开连接，也要继续执行 ignore_user_abort(true); 这段代码的意思是，在执行CronRunBehaviorController前，大概分为如下流程: 判断是否有锁文件，如果没有则创建锁文件，进行下一步，如有进行第2步 判断所文件的创建时间是否和当前时间间隔超过我们设置的时间间隔CRON_MAX_TIME，默认为60s，如果超过则进行下一步，反之不执行，直接退出 再看下面代码： if(is_file(RUNTIME_PATH.'~crons.php')) { $crons = include RUNTIME_PATH.'~crons.php'; }elseif(is_file(COMMON_PATH.'Conf/crons.php')){ $crons = include COMMON_PATH.'Conf/crons.php'; } 这块代码可以看出来我们的定时任务配置是在Conf/crons.php路径下，有runtime缓存，每次修改需要清除缓存，这里比较简单，我来贴上Conf/crons.php的配置即可 return [ 'callback' =&amp;gt; array('Agreement/Cron/call_back.php', 0.1), ]; 其中callback是任务脚本名称，而对于的value则是任务脚本的配置 'Agreement/Cron/call_back.php'为具体执行脚本，依次为间隔时间，上次执行时间 至此，我们的基于TP的定时任务已经全部准备就绪。 写在最后 优势 方便使用，如果是用TP实现的代码逻辑，全部逻辑都可以复用 不用考虑实现具体调用细节，只需要按照规范来写就行 所有的参数都比较直白，不用和crontab中的星星打交道 对时效性不强的简单定时任务可以首选 缺点 由实现逻辑可以看出，每次脚本的启动都是由客户触发，不能保证稳定的时间间隔 配置时间不是很灵活（之前为了使用，也做了些许扩展） 同步执行，很影响使用这的执行效率，如果任务脚本比较大，则会极度影响用户体验 文件锁的方式虽然好用，但是对于分布式系统，无法做到锁功能 尝试过的改造 对配置文件进行了升级，支持间隔时间的配置 对执行方式进行了改造，支持基于pcntl的多进程调用，由于主进程需要先结束，为了实现逻辑最后造成进程混乱，不是很可取。 使用redis对执行加锁，完成分布式锁 上述改造，由于第三点不好处理（也是最重要的一点），遂另辟蹊径，采用了另一种方式来处理。下一章节，我会对这个进行详细说明！</summary></entry><entry><title type="html">ThinkPHP定时任务（二）</title><link href="https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1-%E4%BA%8C.html" rel="alternate" type="text/html" title="ThinkPHP定时任务（二）" /><published>2017-01-23T00:00:00+08:00</published><updated>2017-01-23T00:00:00+08:00</updated><id>https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1(%E4%BA%8C)</id><content type="html" xml:base="https://codecooker.cn/php/2017/01/23/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1-%E4%BA%8C.html">&lt;p&gt;前面说了TP框架中定时任务实现，也对存在的问题和优势做了一个简单的分析。如果有兴趣看的朋友可以参看&lt;a href=&quot;http://codecooker.cn/2017/ThinkPHP%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1-%E4%B8%80/&quot; title=&quot;ThinkPHP定时任务（一&quot;&gt;ThinkPHP定时任务（一）&lt;/a&gt;。这篇主要对基于传统定时任务crontab[守护进程]，外加TP框架支持实现的基于TP的定时任务&lt;/p&gt;

&lt;h3 id=&quot;动机&quot;&gt;动机&lt;/h3&gt;
&lt;p&gt;用过TP的朋友都知道，TP框架是一个由国人开发的很优秀且轻量的PHP Web框架。由于其丰富的文档和强大的用户群体，在国内有不错的保有量。记得之前项目之初选择框架的时候，朋友建议选择TP，给的建议就是用户群体大，便于招人。&lt;br /&gt;
TP在Web模式下获得除了不俗的成绩，但是在非CGI模式下，通常（CLI），缺罕有人用。究其原因，个人觉得，无外乎以下几点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;文档解释的比较少&lt;/li&gt;
  &lt;li&gt;对CLI模式本身支持的比较简单&lt;/li&gt;
  &lt;li&gt;有这样前后台执行需求的用户大多选择了laravel等国际框架&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;然而，我们的工程确实是用TP开发，为了保证代码结构统一，不用重复造轮子。就难免需要探索下TP框架下怎么实现后台任务的执行，如果这个问题解决了，再结合crontab就能找到我们想要的解决方案&lt;/p&gt;

&lt;h3 id=&quot;tp框架的mode&quot;&gt;TP框架的Mode&lt;/h3&gt;
&lt;p&gt;mode即是指应用模式，TP文档中是这么说明的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;应用模式提供了对核心框架进行改造的机会，可以让你的应用适应更多的环境和不同的要求。
每个应用模式有自己的模式定义文件，用于配置当前模式需要加载的核心文件和配置文件，以及别名定义、行为扩展定义等等。根据模式定义文件的定义位置和入口是否需要定义模式，可以分为显式应用模式和隐含应用模式。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而这里，我们要使用的就是TP3.2自带的&lt;em&gt;api&lt;/em&gt;模式，要开启api模式，我们只需要做如下简单的配置&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//cli模式&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'APP_MODE'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'api'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这个宏有默认值，如果我们没有定义的的话，会在应用的入口自动设置为我们的常用模式&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'APP_MODE'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'APP_MODE'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;s1&quot;&gt;'common'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 应用模式 默认为普通模式   &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于api模式的配置文件，可以参考&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Think/mode/api.php&lt;/code&gt;，里边有对内置api应用模式的基础配置，我们可以根据我们的需求进行改写，或者通过配置覆写系统配置。而我们常用的普通模式就是该目录下的common.php，大家可以对比进行学习&lt;/p&gt;

&lt;h3 id=&quot;兼容配置&quot;&gt;兼容配置&lt;/h3&gt;

&lt;h4 id=&quot;app_path-配置&quot;&gt;APP_PATH 配置&lt;/h4&gt;
&lt;p&gt;由于我们的应用是在普通模式下工作的，当我们在调用API模式时，肯定希望应用目录保持一致，这样就不用做任何代码的迁移和改动。在api模式中加入如下配置&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 定义应用目录,同样指向common模式下的app目录&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'APP_PATH'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'../Apps/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;app_status-配置&quot;&gt;APP_STATUS 配置&lt;/h4&gt;
&lt;p&gt;起先，本人以为截止上一步就完成了所有配置。但是在调试的过程中，发现数据库无法连接，最终定位到。APPS目录中的配置没有正常加载。这个过程是个比较长的调试过程，具体就不表了。查看源代码发现，common模式下的所有配置是在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Think/Libary/Think/Dispatcher.class.php&lt;/code&gt;中进行引入的，其中有如下代码，是完成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MODULE&lt;/code&gt;中配置的加载的，有兴趣的同学可以自己看看&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 当前应用状态对应的配置文件&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APP_STATUS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/'&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APP_STATUS&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONF_EXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/'&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;APP_STATUS&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONF_EXT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 加载模块别名定义&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/alias.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Think&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/alias.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 加载模块tags文件定义&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/tags.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Hook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Conf/tags.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 加载模块函数文件&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Common/function.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Common/function.php'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 加载模块的扩展配置文件&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;load_ext_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MODULE_PATH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样找到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Think/Mode/Api/Dispatcher.class.php&lt;/code&gt;,在合适的时机添加上述加载代码(本人TP3.2是在146行开始加的)，这样后就可加载&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MODULE&lt;/code&gt;中配置。&lt;br /&gt;
至此，所有的配置均已完成，下来我们来看看怎么操作。&lt;/p&gt;

&lt;h3 id=&quot;目录结构&quot;&gt;目录结构&lt;/h3&gt;
&lt;p&gt;先看看我这边的目录结构吧，便于后续表述&lt;/p&gt;

&lt;pre&gt;
    PROJECT
        - APPS
            + module_1
            + module_2
        - Crons
            + crons.conf
            + curl_runloop.sh
            + daemon.sh
            + exec.php
            + runloop.sh
            + start.sh
&lt;/pre&gt;

&lt;p&gt;Crons就是我们的定时任务目录，这里我着重说一下Crons目录下各个文件的用途吧。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;crons.conf 定时任务的配置文件，配置了要执行什么定时任务&lt;/li&gt;
  &lt;li&gt;curl_runloop.sh 通过url调用时的runloop脚本&lt;/li&gt;
  &lt;li&gt;daemon.sh 守护进程&lt;/li&gt;
  &lt;li&gt;runloop.sh 直接PHP调用的runloop脚本&lt;/li&gt;
  &lt;li&gt;exec.php命令执行脚本&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;如何执行&quot;&gt;如何执行&lt;/h3&gt;
&lt;p&gt;有了上面的铺垫，启动脚本其实很简单，如下：&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这里做个简单的说明，比如我们之前的访问的URL如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-url&quot;&gt;http://xxx.com/abc/controller/action
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们的script_path的值应该传:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;abc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样即可调用的我们的对应方法，完成后台执行&lt;/p&gt;

&lt;h3 id=&quot;时间控制&quot;&gt;时间控制&lt;/h3&gt;
&lt;p&gt;定时任务肯定逃不过计划执行时间，轮训时间。这里我从controller层做了些许限制。当然也可以在runloop中去做。将配置信息写在crons.conf中。进行配置既可&lt;br /&gt;
建立一个用于执行定时任务的基类来统一处理调度和并发逻辑。需要能别后台调用起的定时任务需要继承该基类，完成基础功能。主要涉及的一下几个地方。&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// cronbaseclass 实现&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 多少时间执行一次&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$interval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 检测是否有权限调用&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;PHP_SAPI&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'cli'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;in_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_client_ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'CRONS_BIND_IP'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'what a fucking day!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 入口方法&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;set_time_limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$exe_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cron_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONTROLLER_NAME&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ACTION_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$exe_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update_cron_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONTROLLER_NAME&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ACTION_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$lock_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RUNTIME_PATH&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;md5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONTROLLER_NAME&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ACTION_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lock_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lock_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$lock_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 子类逻辑执行方法&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;runloop&quot;&gt;runloop&lt;/h3&gt;
&lt;p&gt;runloop 其实很简单，就是一个死循环，然后去读取配置文件，通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php exec.php xxx&lt;/code&gt;去执行脚本任务。特别需要注意的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;daemon.sh&lt;/code&gt;脚本，是我们统一守护进程。crontab中需要进行配置如下：&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 守护进程守护runloop.sh进程，去执行release环境[APP_STATUS]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; daemon.sh runloop.sh release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;结语&quot;&gt;结语&lt;/h3&gt;
&lt;p&gt;到现在，我们所有的配置和执行方案已经分析完成。根据这些配置，大家可以灵活的组织各种定时情况。&lt;br /&gt;
其实每个框架都有自身的强大之处和不足，只要根据它的特性和设计方案出发，一般都能找到合适的方案。自己碰到的问题，相信框架的设计者也有所涉及。本文其实就是在TP的框架中做了一些小的改动来支持定时任务。&lt;/p&gt;

&lt;h3 id=&quot;附录&quot;&gt;附录&lt;/h3&gt;

&lt;h4 id=&quot;cronsconf内容格式&quot;&gt;crons.conf内容格式&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Agreement/CCallback
Agreement/Achive
Agreement/SynchronizeStatus
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="PHP" /><category term="PHP" /><category term="ThinkPHP" /><summary type="html">前面说了TP框架中定时任务实现，也对存在的问题和优势做了一个简单的分析。如果有兴趣看的朋友可以参看ThinkPHP定时任务（一）。这篇主要对基于传统定时任务crontab[守护进程]，外加TP框架支持实现的基于TP的定时任务 动机 用过TP的朋友都知道，TP框架是一个由国人开发的很优秀且轻量的PHP Web框架。由于其丰富的文档和强大的用户群体，在国内有不错的保有量。记得之前项目之初选择框架的时候，朋友建议选择TP，给的建议就是用户群体大，便于招人。 TP在Web模式下获得除了不俗的成绩，但是在非CGI模式下，通常（CLI），缺罕有人用。究其原因，个人觉得，无外乎以下几点： 文档解释的比较少 对CLI模式本身支持的比较简单 有这样前后台执行需求的用户大多选择了laravel等国际框架 然而，我们的工程确实是用TP开发，为了保证代码结构统一，不用重复造轮子。就难免需要探索下TP框架下怎么实现后台任务的执行，如果这个问题解决了，再结合crontab就能找到我们想要的解决方案 TP框架的Mode mode即是指应用模式，TP文档中是这么说明的： 应用模式提供了对核心框架进行改造的机会，可以让你的应用适应更多的环境和不同的要求。 每个应用模式有自己的模式定义文件，用于配置当前模式需要加载的核心文件和配置文件，以及别名定义、行为扩展定义等等。根据模式定义文件的定义位置和入口是否需要定义模式，可以分为显式应用模式和隐含应用模式。 而这里，我们要使用的就是TP3.2自带的api模式，要开启api模式，我们只需要做如下简单的配置 //cli模式 define('APP_MODE','api'); 这个宏有默认值，如果我们没有定义的的话，会在应用的入口自动设置为我们的常用模式 defined('APP_MODE') or define('APP_MODE', 'common'); // 应用模式 默认为普通模式 关于api模式的配置文件，可以参考Think/mode/api.php，里边有对内置api应用模式的基础配置，我们可以根据我们的需求进行改写，或者通过配置覆写系统配置。而我们常用的普通模式就是该目录下的common.php，大家可以对比进行学习 兼容配置 APP_PATH 配置 由于我们的应用是在普通模式下工作的，当我们在调用API模式时，肯定希望应用目录保持一致，这样就不用做任何代码的迁移和改动。在api模式中加入如下配置 // 定义应用目录,同样指向common模式下的app目录 define('APP_PATH','../Apps/'); APP_STATUS 配置 起先，本人以为截止上一步就完成了所有配置。但是在调试的过程中，发现数据库无法连接，最终定位到。APPS目录中的配置没有正常加载。这个过程是个比较长的调试过程，具体就不表了。查看源代码发现，common模式下的所有配置是在Think/Libary/Think/Dispatcher.class.php中进行引入的，其中有如下代码，是完成MODULE中配置的加载的，有兴趣的同学可以自己看看 // 当前应用状态对应的配置文件 if(APP_STATUS &amp;amp;&amp;amp; is_file(MODULE_PATH.'Conf/'.APP_STATUS.CONF_EXT)) C(load_config(MODULE_PATH.'Conf/'.APP_STATUS.CONF_EXT)); // 加载模块别名定义 if(is_file(MODULE_PATH.'Conf/alias.php')) Think::addMap(include MODULE_PATH.'Conf/alias.php'); // 加载模块tags文件定义 if(is_file(MODULE_PATH.'Conf/tags.php')) Hook::import(include MODULE_PATH.'Conf/tags.php'); // 加载模块函数文件 if(is_file(MODULE_PATH.'Common/function.php')) include MODULE_PATH.'Common/function.php'; // 加载模块的扩展配置文件 load_ext_file(MODULE_PATH); 同样找到Think/Mode/Api/Dispatcher.class.php,在合适的时机添加上述加载代码(本人TP3.2是在146行开始加的)，这样后就可加载MODULE中配置。 至此，所有的配置均已完成，下来我们来看看怎么操作。 目录结构 先看看我这边的目录结构吧，便于后续表述 PROJECT - APPS + module_1 + module_2 - Crons + crons.conf + curl_runloop.sh + daemon.sh + exec.php + runloop.sh + start.sh Crons就是我们的定时任务目录，这里我着重说一下Crons目录下各个文件的用途吧。 crons.conf 定时任务的配置文件，配置了要执行什么定时任务 curl_runloop.sh 通过url调用时的runloop脚本 daemon.sh 守护进程 runloop.sh 直接PHP调用的runloop脚本 exec.php命令执行脚本 如何执行 有了上面的铺垫，启动脚本其实很简单，如下： php exec.php script_path 这里做个简单的说明，比如我们之前的访问的URL如下： http://xxx.com/abc/controller/action 我们的script_path的值应该传: abc/controller/action 这样即可调用的我们的对应方法，完成后台执行 时间控制 定时任务肯定逃不过计划执行时间，轮训时间。这里我从controller层做了些许限制。当然也可以在runloop中去做。将配置信息写在crons.conf中。进行配置既可 建立一个用于执行定时任务的基类来统一处理调度和并发逻辑。需要能别后台调用起的定时任务需要继承该基类，完成基础功能。主要涉及的一下几个地方。 // cronbaseclass 实现 // 多少时间执行一次 protected $interval = 60; // 检测是否有权限调用 private function check() { if (PHP_SAPI != 'cli' &amp;amp;&amp;amp; !in_array(get_client_ip(),C('CRONS_BIND_IP'))) { exit('what a fucking day!'); } } // 入口方法 public function index() { set_time_limit(0); $exe_time = $this-&amp;gt;cron_config(CONTROLLER_NAME.ACTION_NAME); if ($exe_time &amp;lt; time()) { $this-&amp;gt;update_cron_config(CONTROLLER_NAME.ACTION_NAME,$this-&amp;gt;interval + time()); $lock_file = RUNTIME_PATH.md5(CONTROLLER_NAME.ACTION_NAME); if (empty(S($lock_file))) { S($lock_file,1,600); $this-&amp;gt;run(); S($lock_file,null); } } } // 子类逻辑执行方法 protected function run() {} runloop runloop 其实很简单，就是一个死循环，然后去读取配置文件，通过php exec.php xxx去执行脚本任务。特别需要注意的daemon.sh脚本，是我们统一守护进程。crontab中需要进行配置如下： # 守护进程守护runloop.sh进程，去执行release环境[APP_STATUS] * * * * * daemon.sh runloop.sh release 结语 到现在，我们所有的配置和执行方案已经分析完成。根据这些配置，大家可以灵活的组织各种定时情况。 其实每个框架都有自身的强大之处和不足，只要根据它的特性和设计方案出发，一般都能找到合适的方案。自己碰到的问题，相信框架的设计者也有所涉及。本文其实就是在TP的框架中做了一些小的改动来支持定时任务。 附录 crons.conf内容格式 Agreement/CCallback Agreement/Achive Agreement/SynchronizeStatus</summary></entry><entry><title type="html">2016年终总结</title><link href="https://codecooker.cn/%E6%84%9F%E6%82%9F/2016/12/31/2016%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.html" rel="alternate" type="text/html" title="2016年终总结" /><published>2016-12-31T00:00:00+08:00</published><updated>2016-12-31T00:00:00+08:00</updated><id>https://codecooker.cn/%E6%84%9F%E6%82%9F/2016/12/31/2016%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93</id><content type="html" xml:base="https://codecooker.cn/%E6%84%9F%E6%82%9F/2016/12/31/2016%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.html">&lt;p&gt;不忘初心，以己为镜，继续努力！&lt;/p&gt;</content><author><name>codecooker</name><email>codecooker@outlook.com</email></author><category term="感悟" /><category term="心得" /><category term="感悟" /><summary type="html">不忘初心，以己为镜，继续努力！</summary></entry></feed>