云开发一旦成为常态

云开发一旦成为常态,程序员将何去何从?

有接触小程序开发的同学应该知道,云开发成为了小程序开发的标配。有了云开发,无需搭建服务器,即可使用云端能力。

现今开发互联网产品,很少不用App(或小程序)的了,而App数据是从后台数据服务接口获取的,而提供数据服务接口的,是用Springboot、nodejs等提供技术实现的。

提供一个数据接口容易,实现一个功能也容易,难的是解决数据的并发性,负载均衡,数据库吞吐量等难题,而这些恰恰是影响数据响应速度的关键点。

不是说解决不了,而是时间问题。按我当技术总监多年的经验所知,后端数据服务功能,要达到稳定、满意状态,大多是以年为单位的,而时间越久,程序员就越不稳定,企业负责人就越没有耐心,因为大家都不可避免的要面对成本问题。

如果现在有第3方(云服务)企业把这些接口都开发好了,按需提供,而且他们投入专业的团队做这一块,你说企业老板有怎么选择–如果是我,我也选择成熟,稳定、快速可支撑上线的产品。

实现后端数据服务的功能系统,在业界被称为云开发。

“云开发为移动开发者提供的一站式后端云服务,它帮助开发者统一构建和管理资源,免去了移动应用开发过程中繁琐的服务器搭建及运维、域名注册及备案、数据接口实现等繁琐流程,让开发者可以专注于业务逻辑的实现,而无需理解后端逻辑及服务器运维知识,开发门槛更低,效率更高。”

从上面所描述可知,云开发为开发者提供完整的原生云端支持,无需搭建服务器,使用平台提供的 API 进行核心业务开发,即可实现快速上线和迭代。

其实,这个技术在国外早流行并被使用多年,国内随着小程序的兴起,逐步推进当中。身为程序员的我们,除了关注云开发技术之外,是否还有更重要的问题需要考虑么?

不知以后是否还有机会,用半年或一年的时间搭建后端数据服务?不知现任老板是否会”变脸”,要求我们改用云开发

就算老板不要求,在咱们做新产品时,是否会考虑就用云开发技术?

一旦后端不用架构了,程序员是不是就少了些工作机会了,云开发一旦成为常态,程序员将何去何从

程序员学习Python只需这6本书,从入门到进阶!

程序员学习Python只需这6本书,从入门到进阶!

编程语言Python语法简单,代码可读性高,不仅适合初学者学习,而且岗位需求大,薪资一路也是水涨船高,即使是刚毕业的应届毕业生,薪资也在12500元每月。

因此,很多程序员很乐意去研究这门编程语言,那么有哪些值得收藏的Python书单呢?

Python入门

0、《“笨办法”学Python(第3版)》

%title插图%num

 

这本书结构简单,非常适合初学编程,对编程感兴趣的读者使用,通过*基础的编程技术,让你体验开发过程。

亲测有效!

1、《Python编程快速上手——让繁琐工作自动化》

程序员学习Python只需这6本书,从入门到进阶!

 

本书介绍了Python语言的基础知识,通过项目实践教会读者如何应用这些知识和技能。

这本书就好在学习完每个章节后会有相应的习题和实践项目,并且附有习题答案,帮助理解,而不是出个题目就跑。

2、《Python编程初学者指南》

程序员学习Python只需这6本书,从入门到进阶!

 

这本书让人惊喜的地方是学习起来不会过于枯燥,每章节的学习都会通过游戏来给你启发,寓教于乐,降低了学习的难度。

Python进阶

0、《Python机器学习实践指南》

程序员学习Python只需这6本书,从入门到进阶!

 

机器学习是近年来逐渐热门的领域,这本书将机器学习和编程语言Python两大热门相结合,将Python 语言在数据分析方面的优势发挥到*致。

1、《用Python写网络爬虫》

程序员学习Python只需这6本书,从入门到进阶!

 

本书讲解了如何使用Python来编写网络爬虫程序,并在*后使用本书介绍的数据抓取技术对几个真实的网站进行了抓取,旨在帮助读者活学活用书中介绍的技术。

本书适合有一定Python编程经验,而且对爬虫技术感兴趣的读者阅读。

2、《Python核心编程(第3版)》

程序员学习Python只需这6本书,从入门到进阶!

 

经典畅销图书,分为三个部分,*部分是Python的通用应用,第二部分主要是与web相关,第三部分是一些补充内容,包括文本处理。

如果你有一定的Python开发经验,它会非常适合你。

看书不可少,但是更好的学习方法是结合w3cschool上的教程进行实践。

*火的五大 python 开源项目

*火的五大 python 开源项目

python 语言易学,且开发高效,用 python 语言开发的开源项目活跃度很高,下面介绍目前*活跃的五大 python 开源项目:

 

1.) 排名*位的当然是有关机器学习的Google开源项目 TensorFlow,TensorFlow 是一个采用数据流图,用于数值统计计算的开源项目,此项目一经公布就有过万的 star 和 fork,被广泛的用于机器学习和深度神经网络方面的研究。

 

2.) 排名第二位的是python web 方向的新贵 Flask,Flask 是一个微型的 Python Web 开发框架,它基于WSGI工具箱和 jinja2模板引擎,可以*灵活*快速的配置开发属于自己的 Python Web站点。

 

3.) 排名第三位的是python web 方向的经典 Django,Django 是一个采用 M-V-T 设计模式的 Web 框架。在Django中,由于控制器接受用户输入的部分由框架自行处理,所以 Django 编程更关注的是模型(Model)、模板(Template)和视图(Views)。使用 Django,我们可以迅速创建高品质、易维护、和数据库驱动的Web服务程序,尤其对不甚熟悉数据库操作的建站同学来说,如获至宝。

 

4.)排名第四位的是功能测试框架 pytest,pytest 是一个成熟的全功能的Python测试框架,可以帮助你更好的写出自动化测试程序,它能适应从简单的单元测试到复杂的集成测试在内的全功能测试,并且支持众多的插件开发。

 

5.)排名第五位的是数据采集工具 scrapy,scrapy 是一个优秀的网络数据爬虫框架,用户只需定制开发几个模块就可以轻松的实现一个完善的网络爬虫,用其来抓取网页内容数据以及各种图片文件等非常方便;Scrapy 还提供了多种类型的爬虫基类,如 BaseSpider 和sitemap等;Scrapy被广泛的运用于数据挖掘、网络监测和自动化测试等领域。

 

需要说明的是以上的排名是根据代码submit 和 fork 的活跃度的排名,仅仅反映的是开源代码的受欢迎程度,并非就是企业市场应用的火爆度,注意区别,你要根据自己的需求选择合适你项目的python 开源框架和工具。

python命令是如何操作文件file的?

python命令是如何操作文件file的?

 

无论哪一个操作系统,亦或是哪一门语言,当使用到一些高级层次的时候,必然会和文件打交道,因为所有的内容不可能都在内存中存储和传输。用到文件,就必然会有创建文件、打开文件、写文件、复制文件、删除文件这些操作,下面我们来看看在python中是如何利用命令来操作file文件的。

 

python操作file的命令及使用

1.打开和关闭文件

file object= open(file_name [, access_mode][, buffering]),通过open命令来打开一个文件。file_name是指文件的路径,access_mode是打开的方式,包括只读r、写入w和追加a。r取readonly的首字母,w取write的首字母,a取append的首字母。buffering代表缓冲区的大小。如果取负值,寄存区的缓冲大小则为系统默认。

File 对象的 close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。在java中也是利用close来关闭文件,一般都会配合flush一起使用。

2.写文件

write()方法可将任何字符串写入一个打开的文件。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。write()方法不会在字符串的结尾添加换行符(‘\n’)。

例如:#!/usr/bin/python

# -*- coding: UTF-8 -*-

# 打开一个文件

fo = open(“welcome.txt”,”w”)

fo.write(“Welcome to Qingdao!\nHave a good rest!\n”)

# 关闭打开的文件

fo.close()

这段代码是将两行字符串写入welcome.txt文本中,写完后关闭文件。

3.读文件

读文件使用read命令,read后面跟字符的数量,如下面示例,是指从welcome.txt中读取10个字符,也就是”Welcome to”。

fo = open(“welcome.txt”,”r+”)

str = fo.read(10)

结语

本节先介绍文件的打开关闭和读写操作,下一节我们接着学习文件的查找、重命名、删除等命令的使用方法。

 

Python爬虫学前普及

爬虫篇 | Python爬虫学前普及

【摘要】*近整理一个爬虫系列方面的文章,不管大家的基础如何,我从头开始整一个爬虫系列方面的文章,让大家循序渐进的学习爬虫,小白也没有学习障碍,那么今天,环球网校的小编就来针对Python讲讲爬虫学前普及,希望今天的文章对您可以有所帮助。

恩,准备进入正题了!*近一段时间没有怎么更新公众号,主要就是在做爬虫教程的一些准备工作,看看爬虫需要用到那些技术,然后做个计划出来,确定一下学习课程中缝,这不今天就先列出一些玩爬虫需要的准备工作!

Python爬虫这门技术你可以做得很简单,你也可以玩得很深入.打比方用简单的爬虫方式爬取1000万条数据可能需要一周时间,但如果你的爬虫玩得比较厉害,你可以采用分布式爬虫技术1天就能完成了1000万条数据。虽然都是爬虫,但这就是菜鸟与大牛的区别!这就和太*拳似的,易学难精!

这里面的技术点挺多的!现在来简单聊聊爬虫需要涉及的知识点。

网页知识

html,js,css,xpath这些知识,虽然简单,但一定需要了解。你得知道这些网页是如何构成的,然后才能去分解他们.

HTTP知识

一般爬虫你需要模拟浏览器的操作,才能去获取网页的信息

如果有些网站需要登录,才能获取更多的资料,你得去登录,你得把登录的账号密码进行提交

有些网站登录后需要保存cookie信息才能继续获取更多资料

正则表达式

有了正则表达式才能更好的分割网页信息,获取我们想要的数据,所以正则表达式也是需要了解的.

一些重要的爬虫库

url,url2,requests

beautiulSoup4,re,lxml

数据库

爬取到的数据我们得有个地方来保存,可以使用文件,也可以使用数据库,这里我会使用mysql,还有更适合爬虫的MongoDB数据库,以及分布式要用到的redis 数据库

爬虫框架

PySpider和Scrapy 这两个爬虫框架是非常NB的,简单的爬虫可以使用urllib与urllib2以及正则表达式就能完成,但高级的爬虫还得用这两个框架。这两个框架需要另行安装。后面一起学习.

反爬虫

有时候你的网站数据想禁止别人爬取,可以做一些反爬虫处理操作。打比方百度上就无法去查找淘宝上的数据,这样就避开了搜索引擎的竞争,淘宝就可以搞自己的一套竞价排名

分布式爬虫

使用多个redis实例来缓存各台主机上爬取的数据。

爬虫要学的东西还是挺多的,想把爬虫玩得666,基本就是这些知识点吧!好了,上面的东西我也只是粗略整理,笔误在所难免,后面我们会一起来学习爬虫知识吧!可以为您带来帮助。以上的内容都可以为您的python学习之路带来便利,小编祝您学习之路顺利。

Python 优化提速的 8 个小技巧

Python 优化提速的 8 个小技巧

%title插图%num

Python 是一种脚本语言,相比 C/C++ 这样的编译语言,在效率和性能方面存在一些不足。但是,有很多时候,Python 的效率并没有想象中的那么夸张。本文对一些 Python 代码加速运行的技巧进行整理。

0. 代码优化原则

本文会介绍不少的 Python 代码加速运行的技巧。在深入代码优化细节之前,需要了解一些代码优化基本原则。

*个基本原则是不要过早优化。很多人一开始写代码就奔着性能优化的目标,“让正确的程序更快要比让快速的程序正确容易得多”。因此,优化的前提是代码能正常工作。过早地进行优化可能会忽视对总体性能指标的把握,在得到全局结果前不要主次颠倒。

第二个基本原则是权衡优化的代价。优化是有代价的,想解决所有性能的问题是几乎不可能的。通常面临的选择是时间换空间或空间换时间。另外,开发代价也需要考虑。

第三个原则是不要优化那些无关紧要的部分。如果对代码的每一部分都去优化,这些修改会使代码难以阅读和理解。如果你的代码运行速度很慢,首先要找到代码运行慢的位置,通常是内部循环,专注于运行慢的地方进行优化。在其他地方,一点时间上的损失没有什么影响。

1. 避免全局变量

  1. # 不推荐写法。代码耗时:26.8
  2. import math
  3. size = 10000
  4. for x in range(size):
  5.     for y in range(size):
  6.         z = math.sqrt(x) + math.sqrt(y)

许多程序员刚开始会用 Python 语言写一些简单的脚本,当编写脚本时,通常习惯了直接将其写为全局变量,例如上面的代码。但是,由于全局变量和局部变量实现方式不同,定义在全局范围内的代码运行速度会比定义在函数中的慢不少。通过将脚本语句放入到函数中,通常可带来 15% – 30% 的速度提升。

  1. # 推荐写法。代码耗时:20.6
  2. import math
  3. def main():  # 定义到函数中,以减少全部变量使用
  4.     size = 10000
  5.     for x in range(size):
  6.         for y in range(size):
  7.             z = math.sqrt(x) + math.sqrt(y)
  8. main()

2. 避免.

2.1 避免模块和函数属性访问

  1. # 不推荐写法。代码耗时:14.5
  2. import math
  3. def computeSqrt(size: int):
  4.     result = []
  5.     for i in range(size):
  6.         result.append(math.sqrt(i))
  7.     return result
  8. def main():
  9.     size = 10000
  10.     for _ in range(size):
  11.         result = computeSqrt(size)
  12. main()

每次使用.(属性访问操作符时)会触发特定的方法,如__getattribute__()__getattr__(),这些方法会进行字典操作,因此会带来额外的时间开销。通过from import语句,可以消除属性访问。

  1. # *次优化写法。代码耗时:10.9
  2. from math import sqrt
  3. def computeSqrt(size: int):
  4.     result = []
  5.     for i in range(size):
  6.         result.append(sqrt(i))  # 避免math.sqrt的使用
  7.     return result
  8. def main():
  9.     size = 10000
  10.     for _ in range(size):
  11.         result = computeSqrt(size)
  12. main()

在第 1 节中我们讲到,局部变量的查找会比全局变量更快,因此对于频繁访问的变量sqrt,通过将其改为局部变量可以加速运行。

  1. # 第二次优化写法。代码耗时:9.9
  2. import math
  3. def computeSqrt(size: int):
  4.     result = []
  5.     sqrt = math.sqrt  # 赋值给局部变量
  6.     for i in range(size):
  7.         result.append(sqrt(i))  # 避免math.sqrt的使用
  8.     return result
  9. def main():
  10.     size = 10000
  11.     for _ in range(size):
  12.         result = computeSqrt(size)
  13. main()

除了math.sqrt外,computeSqrt函数中还有.的存在,那就是调用listappend方法。通过将该方法赋值给一个局部变量,可以彻底消除computeSqrt函数中for循环内部的.使用。

  1. # 推荐写法。代码耗时:7.9
  2. import math
  3. def computeSqrt(size: int):
  4.     result = []
  5.     append = result.append
  6.     sqrt = math.sqrt    # 赋值给局部变量
  7.     for i in range(size):
  8.         append(sqrt(i))  # 避免 result.append 和 math.sqrt 的使用
  9.     return result
  10. def main():
  11.     size = 10000
  12.     for _ in range(size):
  13.         result = computeSqrt(size)
  14. main()

2.2 避免类内属性访问

  1. # 不推荐写法。代码耗时:10.4
  2. import math
  3. from typing import List
  4. class DemoClass:
  5.     def __init__(self, value: int):
  6.         self._value = value
  7.     def computeSqrt(self, size: int) -> List[float]:
  8.         result = []
  9.         append = result.append
  10.         sqrt = math.sqrt
  11.         for _ in range(size):
  12.             append(sqrt(self._value))
  13.         return result
  14. def main():
  15.     size = 10000
  16.     for _ in range(size):
  17.         demo_instance = DemoClass(size)
  18.         result = demo_instance.computeSqrt(size)
  19. main()

避免.的原则也适用于类内属性,访问self._value的速度会比访问一个局部变量更慢一些。通过将需要频繁访问的类内属性赋值给一个局部变量,可以提升代码运行速度。

  1. # 推荐写法。代码耗时:8.0
  2. import math
  3. from typing import List
  4. class DemoClass:
  5.     def __init__(self, value: int):
  6.         self._value = value
  7.     def computeSqrt(self, size: int) -> List[float]:
  8.         result = []
  9.         append = result.append
  10.         sqrt = math.sqrt
  11.         value = self._value
  12.         for _ in range(size):
  13.             append(sqrt(value))  # 避免 self._value 的使用
  14.         return result
  15. def main():
  16.     size = 10000
  17.     for _ in range(size):
  18.         demo_instance = DemoClass(size)
  19.         demo_instance.computeSqrt(size)
  20. main()

3. 避免不必要的抽象

  1. # 不推荐写法,代码耗时:0.55
  2. class DemoClass:
  3.     def __init__(self, value: int):
  4.         self.value = value
  5.     @property
  6.     def value(self) -> int:
  7.         return self._value
  8.     @value.setter
  9.     def value(self, x: int):
  10.         self._value = x
  11. def main():
  12.     size = 1000000
  13.     for i in range(size):
  14.         demo_instance = DemoClass(size)
  15.         value = demo_instance.value
  16.         demo_instance.value = i
  17. main()

任何时候当你使用额外的处理层(比如装饰器、属性访问、描述器)去包装代码时,都会让代码变慢。大部分情况下,需要重新进行审视使用属性访问器的定义是否有必要,使用getter/setter函数对属性进行访问通常是 C/C++ 程序员遗留下来的代码风格。如果真的没有必要,就使用简单属性。

  1. # 推荐写法,代码耗时:0.33
  2. class DemoClass:
  3.     def __init__(self, value: int):
  4.         self.value = value  # 避免不必要的属性访问器
  5. def main():
  6.     size = 1000000
  7.     for i in range(size):
  8.         demo_instance = DemoClass(size)
  9.         value = demo_instance.value
  10.         demo_instance.value = i
  11. main()

4. 避免数据复制

4.1 避免无意义的数据复制

  1. # 不推荐写法,代码耗时:6.5
  2. def main():
  3.     size = 10000
  4.     for _ in range(size):
  5.         value = range(size)
  6.         value_list = [x for x in value]
  7.         square_list = [x * x for x in value_list]
  8. main()

上面的代码中value_list完全没有必要,这会创建不必要的数据结构或复制。

  1. # 推荐写法,代码耗时:4.8
  2. def main():
  3.     size = 10000
  4.     for _ in range(size):
  5.         value = range(size)
  6.         square_list = [x * x for x in value]  # 避免无意义的复制
  7. main()

另外一种情况是对 Python 的数据共享机制过于偏执,并没有很好地理解或信任 Python 的内存模型,滥用 copy.deepcopy()之类的函数。通常在这些代码中是可以去掉复制操作的。

4.2 交换值时不使用中间变量

  1. # 不推荐写法,代码耗时:0.07
  2. def main():
  3.     size = 1000000
  4.     for _ in range(size):
  5.         a = 3
  6.         b = 5
  7.         temp = a
  8.         a = b
  9.         b = temp
  10. main()

上面的代码在交换值时创建了一个临时变量temp,如果不借助中间变量,代码更为简洁、且运行速度更快。

  1. # 推荐写法,代码耗时:0.06
  2. def main():
  3.     size = 1000000
  4.     for _ in range(size):
  5.         a = 3
  6.         b = 5
  7.         a, b = b, a  # 不借助中间变量
  8. main()

4.3 字符串拼接用join而不是+

  1. # 不推荐写法,代码耗时:2.6
  2. import string
  3. from typing import List
  4. def concatString(string_list: List[str]) -> str:
  5.     result = 
  6.     for str_i in string_list:
  7.         result += str_i
  8.     return result
  9. def main():
  10.     string_list = list(string.ascii_letters * 100)
  11.     for _ in range(10000):
  12.         result = concatString(string_list)
  13. main()

当使用a + b拼接字符串时,由于 Python 中字符串是不可变对象,其会申请一块内存空间,将ab分别复制到该新申请的内存空间中。因此,如果要拼接 n 个字符串,会产生 n-1 个中间结果,每产生一个中间结果都需要申请和复制一次内存,严重影响运行效率。而使用join()拼接字符串时,会首先计算出需要申请的总的内存空间,然后一次性地申请所需内存,并将每个字符串元素复制到该内存中去。

  1. # 推荐写法,代码耗时:0.3
  2. import string
  3. from typing import List
  4. def concatString(string_list: List[str]) -> str:
  5.     return .join(string_list)  # 使用 join 而不是 +
  6. def main():
  7.     string_list = list(string.ascii_letters * 100)
  8.     for _ in range(10000):
  9.         result = concatString(string_list)
  10. main()

5. 利用if条件的短路特性

  1. # 不推荐写法,代码耗时:0.05
  2. from typing import List
  3. def concatString(string_list: List[str]) -> str:
  4.     abbreviations = {‘cf.’‘e.g.’‘ex.’‘etc.’‘flg.’‘i.e.’‘Mr.’‘vs.’}
  5.     abbr_count = 0
  6.     result = 
  7.     for str_i in string_list:
  8.         if str_i in abbreviations:
  9.             result += str_i
  10.     return result
  11. def main():
  12.     for _ in range(10000):
  13.         string_list = [‘Mr.’‘Hat’‘is’‘Chasing’‘the’‘black’‘cat’‘.’]
  14.         result = concatString(string_list)
  15. main()

if 条件的短路特性是指对if a and b这样的语句, 当aFalse时将直接返回,不再计算b;对于if a or b这样的语句,当aTrue时将直接返回,不再计算b。因此, 为了节约运行时间,对于or语句,应该将值为True可能性比较高的变量写在or前,而and应该推后。

  1. # 推荐写法,代码耗时:0.03
  2. from typing import List
  3. def concatString(string_list: List[str]) -> str:
  4.     abbreviations = {‘cf.’‘e.g.’‘ex.’‘etc.’‘flg.’‘i.e.’‘Mr.’‘vs.’}
  5.     abbr_count = 0
  6.     result = 
  7.     for str_i in string_list:
  8.         if str_i[-1] == ‘.’ and str_i in abbreviations:  # 利用 if 条件的短路特性
  9.             result += str_i
  10.     return result
  11. def main():
  12.     for _ in range(10000):
  13.         string_list = [‘Mr.’‘Hat’‘is’‘Chasing’‘the’‘black’‘cat’‘.’]
  14.         result = concatString(string_list)
  15. main()

6. 循环优化

6.1 用for循环代替while循环

  1. # 不推荐写法。代码耗时:6.7
  2. def computeSum(size: int) -> int:
  3.     sum_ = 0
  4.     i = 0
  5.     while i < size:
  6.         sum_ += i
  7.         i += 1
  8.     return sum_
  9. def main():
  10.     size = 10000
  11.     for _ in range(size):
  12.         sum_ = computeSum(size)
  13. main()

Python 的for循环比while循环快不少。

  1. # 推荐写法。代码耗时:4.3
  2. def computeSum(size: int) -> int:
  3.     sum_ = 0
  4.     for i in range(size):  # for 循环代替 while 循环
  5.         sum_ += i
  6.     return sum_
  7. def main():
  8.     size = 10000
  9.     for _ in range(size):
  10.         sum_ = computeSum(size)
  11. main()

6.2 使用隐式for循环代替显式for循环

针对上面的例子,更进一步可以用隐式for循环来替代显式for循环

  1. # 推荐写法。代码耗时:1.7
  2. def computeSum(size: int) -> int:
  3.     return sum(range(size))  # 隐式 for 循环代替显式 for 循环
  4. def main():
  5.     size = 10000
  6.     for _ in range(size):
  7.         sum = computeSum(size)
  8. main()

6.3 减少内层for循环的计算

  1. # 不推荐写法。代码耗时:12.8
  2. import math
  3. def main():
  4.     size = 10000
  5.     sqrt = math.sqrt
  6.     for x in range(size):
  7.         for y in range(size):
  8.             z = sqrt(x) + sqrt(y)
  9. main()

上面的代码中sqrt(x)位于内侧for循环, 每次训练过程中都会重新计算一次,增加了时间开销。

  1. # 推荐写法。代码耗时:7.0
  2. import math
  3. def main():
  4.     size = 10000
  5.     sqrt = math.sqrt
  6.     for x in range(size):
  7.         sqrt_x = sqrt(x)  # 减少内层 for 循环的计算
  8.         for y in range(size):
  9.             z = sqrt_x + sqrt(y)
  10. main()

7. 使用numba.jit

我们沿用上面介绍过的例子,在此基础上使用numba.jitnumba可以将 Python 函数 JIT 编译为机器码执行,大大提高代码运行速度。关于numba的更多信息见下面的主页:http://numba.pydata.org/numba.pydata.org

  1. # 推荐写法。代码耗时:0.62
  2. import numba
  3. @numba.jit
  4. def computeSum(size: float) -> int:
  5.     sum = 0
  6.     for i in range(size):
  7.         sum += i
  8.     return sum
  9. def main():
  10.     size = 10000
  11.     for _ in range(size):
  12.         sum = computeSum(size)
  13. main()

8. 选择合适的数据结构

Python 内置的数据结构如strtuplelistsetdict底层都是 C 实现的,速度非常快,自己实现新的数据结构想在性能上达到内置的速度几乎是不可能的。

list类似于 C++ 中的std::vector,是一种动态数组。其会预分配一定内存空间,当预分配的内存空间用完,又继续向其中添加元素时,会申请一块更大的内存空间,然后将原有的所有元素都复制过去,之后销毁之前的内存空间,再插入新元素。

删除元素时操作类似,当已使用内存空间比预分配内存空间的一半还少时,会另外申请一块小内存,做一次元素复制,之后销毁原有大内存空间。

因此,如果有频繁的新增、删除操作,新增、删除的元素数量又很多时,list的效率不高。此时,应该考虑使用collections.dequecollections.deque是双端队列,同时具备栈和队列的特性,能够在两端进行 O(1) 复杂度的插入和删除操作。

list的查找操作也非常耗时。当需要在list频繁查找某些元素,或频繁有序访问这些元素时,可以使用bisect维护list对象有序并在其中进行二分查找,提升查找的效率。

另外一个常见需求是查找*小值或*大值,此时可以使用heapq模块将list转化为一个堆,使得获取*小值的时间复杂度是 O(1)。

下面的网页给出了常用的 Python 数据结构的各项操作的时间复杂度:https://wiki.python.org/moin/TimeComplexity

参考资料

  • David Beazley & Brian K. Jones. Python Cookbook, Third edition. O’Reilly Media, ISBN: 9781449340377, 2013.
  • 张颖 & 赖勇浩. 编写高质量代码:改善Python程序的91个建议. 机械工业出版社, ISBN: 9787111467045, 2014.

MySQL原理解析:MySQL的索引结构为什么使用B+树?

前言

在MySQL中,无论是Innodb还是MyIsam,都使用了B+树作索引结构(这里不考虑hash等其他索引)。本文将从*普通的二叉查找树开始,逐步说明各种树解决的问题以及面临的新问题,从而说明MySQL为什么选择B+树作为索引结构。

一、二叉查找树(BST):不平衡

二叉查找树(BST,Binary Search Tree),也叫二叉排序树,在二叉树的基础上需要满足:任意节点的左子树上所有节点值不大于根节点的值,任意节点的右子树上所有节点值不小于根节点的值。如下是一颗BST

 

MySQL原理解析:MySQL的索引结构为什么使用B+树?

 

 

当需要快速查找时,将数据存储在BST是一种常见的选择,因为此时查询时间取决于树高,平均时间复杂度是O(lgn)。然而,BST可能长歪而变得不平衡,如下图所示,此时BST退化为链表,时间复杂度退化为O(n)。

为了解决这个问题,引入了平衡二叉树。

 

MySQL原理解析:MySQL的索引结构为什么使用B+树?

 

 

二、平衡二叉树(AVL):旋转耗时

AVL树是严格的平衡二叉树,所有节点的左右子树高度差不能超过1;AVL树查找、插入和删除在平均和*坏情况下都是O(lgn)。

AVL实现平衡的关键在于旋转操作:插入和删除可能破坏二叉树的平衡,此时需要通过一次或多次树旋转来重新平衡这个树。当插入数据时,*多只需要1次旋转(单旋转或双旋转);但是当删除数据时,会导致树失衡,AVL需要维护从被删除节点到根节点这条路径上所有节点的平衡,旋转的量级为O(lgn)。

由于旋转的耗时,AVL树在删除数据时效率很低;在删除操作较多时,维护平衡所需的代价可能高于其带来的好处,因此AVL实际使用并不广泛。

三、红黑树:树太高

与AVL树相比,红黑树并不追求严格的平衡,而是大致的平衡:只是确保从根到叶子的*长的可能路径不多于*短的可能路径的两倍长。从实现来看,红黑树*大的特点是每个节点都属于两种颜色(红色或黑色)之一,且节点颜色的划分需要满足特定的规则(具体规则略)。红黑树示例如下:

 

MySQL原理解析:MySQL的索引结构为什么使用B+树?

 

 

与AVL树相比,红黑树的查询效率会有所下降,这是因为树的平衡性变差,高度更高。但红黑树的删除效率大大提高了,因为红黑树同时引入了颜色,当插入或删除数据时,只需要进行O(1)次数的旋转以及变色就能保证基本的平衡,不需要像AVL树进行O(lgn)次数的旋转。总的来说,红黑树的统计性能高于AVL。

因此,在实际应用中,AVL树的使用相对较少,而红黑树的使用非常广泛。例如,Java中的TreeMap使用红黑树存储排序键值对;Java8中的HashMap使用链表+红黑树解决哈希冲突问题(当冲突节点较少时,使用链表,当冲突节点较多时,使用红黑树)。

对于数据在内存中的情况(如上述的TreeMap和HashMap),红黑树的表现是非常优异的。但是对于数据在磁盘等辅助存储设备中的情况(如MySQL等数据库),红黑树并不擅长,因为红黑树长得还是太高了。当数据在磁盘中时,磁盘IO会成为*大的性能瓶颈,设计的目标应该是尽量减少IO次数;而树的高度越高,增删改查所需要的IO次数也越多,会严重影响性能。

四、B树:为磁盘而生

B树也称B-树(其中-不是减号),是为磁盘等辅存设备设计的多路平衡查找树,与二叉树相比,B树的每个非叶节点可以有多个子树。因此,当总节点数量相同时,B树的高度远远小于AVL树和红黑树(B树是一颗“矮胖子”),磁盘IO次数大大减少。

定义B树*重要的概念是阶数(Order),对于一颗m阶B树,需要满足以下条件:

  • 每个节点*多包含 m 个子节点。
  • 如果根节点包含子节点,则至少包含 2 个子节点;除根节点外,每个非叶节点至少包含 m/2 个子节点。
  • 拥有 k 个子节点的非叶节点将包含 k – 1 条记录。
  • 所有叶节点都在同一层中。

可以看出,B树的定义,主要是对非叶结点的子节点数量和记录数量的限制。

下图是一个3阶B树的例子:

 

MySQL原理解析:MySQL的索引结构为什么使用B+树?

 

 

B树的优势除了树高小,还有对访问局部性原理的利用。所谓局部性原理,是指当一个数据被使用时,其附近的数据有较大概率在短时间内被使用。B树将键相近的数据存储在同一个节点,当访问其中某个数据时,数据库会将该整个节点读到缓存中;当它临近的数据紧接着被访问时,可以直接在缓存中读取,无需进行磁盘IO;换句话说,B树的缓存命中率更高。

B树在数据库中有一些应用,如mongodb的索引使用了B树结构。但是在很多数据库应用中,使用了是B树的变种B+树。

五、B+树

B+树也是多路平衡查找树,其与B树的区别主要在于:

  • B树中每个节点(包括叶节点和非叶节点)都存储真实的数据,B+树中只有叶子节点存储真实的数据,非叶节点只存储键。在MySQL中,这里所说的真实数据,可能是行的全部数据(如Innodb的聚簇索引),也可能只是行的主键(如Innodb的辅助索引),或者是行所在的地址(如MyIsam的非聚簇索引)。
  • B树中一条记录只会出现一次,不会重复出现,而B+树的键则可能重复重现——一定会在叶节点出现,也可能在非叶节点重复出现。
  • B+树的叶节点之间通过双向链表链接。
  • B树中的非叶节点,记录数比子节点个数少1;而B+树中记录数与子节点个数相同。

由此,B+树与B树相比,有以下优势:

  • 更少的IO次数:B+树的非叶节点只包含键,而不包含真实数据,因此每个节点存储的记录个数比B数多很多(即阶m更大),因此B+树的高度更低,访问时所需要的IO次数更少。此外,由于每个节点存储的记录数更多,所以对访问局部性原理的利用更好,缓存命中率更高。
  • 更适于范围查询:在B树中进行范围查询时,首先找到要查找的下限,然后对B树进行中序遍历,直到找到查找的上限;而B+树的范围查询,只需要对链表进行遍历即可。
  • 更稳定的查询效率:B树的查询时间复杂度在1到树高之间(分别对应记录在根节点和叶节点),而B+树的查询复杂度则稳定为树高,因为所有数据都在叶节点。

B+树也存在劣势:由于键会重复出现,因此会占用更多的空间。但是与带来的性能优势相比,空间劣势往往可以接受,因此B+树的在数据库中的使用比B树更加广泛。

六、感受B+树的威力

前面说到,B树/B+树与红黑树等二叉树相比,*大的优势在于树高更小。实际上,对于Innodb的B+索引来说,树的高度一般在2-4层。下面来进行一些具体的估算。

树的高度是由阶数决定的,阶数越大树越矮;而阶数的大小又取决于每个节点可以存储多少条记录。Innodb中每个节点使用一个页(page),页的大小为16KB,其中元数据只占大约128字节左右(包括文件管理头信息、页面头信息等等),大多数空间都用来存储数据。

  • 对于非叶节点,记录只包含索引的键和指向下一层节点的指针。假设每个非叶节点页面存储1000条记录,则每条记录大约占用16字节;当索引是整型或较短的字符串时,这个假设是合理的。延伸一下,我们经常听到建议说索引列长度不应过大,原因就在这里:索引列太长,每个节点包含的记录数太少,会导致树太高,索引的效果会大打折扣,而且索引还会浪费更多的空间。
  • 对于叶节点,记录包含了索引的键和值(值可能是行的主键、一行完整数据等,具体见前文),数据量更大。这里假设每个叶节点页面存储100条记录(实际上,当索引为聚簇索引时,这个数字可能不足100;当索引为辅助索引时,这个数字可能远大于100;可以根据实际情况进行估算)。

对于一颗3层B+树,*层(根节点)有1个页面,可以存储1000条记录;第二层有1000个页面,可以存储10001000条记录;第三层(叶节点)有10001000个页面,每个页面可以存储100条记录,因此可以存储10001000100条记录,即1亿条。而对于二叉树,存储1亿条记录则需要26层左右。

七、总结

*后,总结一下各种树解决的问题以及面临的新问题:

  1. 二叉查找树(BST):解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表;
  2. 平衡二叉树(AVL):通过旋转解决了平衡的问题,但是旋转操作效率太低;
  3. 红黑树:通过舍弃严格的平衡和引入红黑节点,解决了AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多;
  4. B树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;
  5. B+树:在B树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

超全!我常用的70个数据分析网址

超全!我常用的70个数据分析网址

 

今天给大家分享的这篇文章,更像是一份数据分析常用网站字典,一共70个,可视化、词频词云、PPT模板等等面面俱到,值得收藏!

  数据可视化工具

百度ECharts http://echarts.baidu.com/
Cytoscape http://www.cytoscape.org/
图表秀 http://www.tubiaoxiu.com/
数据观 http://shujuguan.cn/
微博足迹可视化 http://vis.pku.edu.cn/weibova/weibogeo_footprint/index.html
BDP个人版 https://me.bdp.cn/home.html
魔镜 http://www.moojnn.com/
图表秀 https://www.tubiaoxiu.com
文图 https://www.wentu.io
百度图说 http://tushuo.baidu.com
infogr.am https://infogr.am/
Infographic https://venngage.com
visually https://visual.ly
Piktochart https://piktochart.com
slides https://slides.com
声享 https://ppt.baomitu.com
AntV https://antv.alipay.com/index.html

 词频分析工具、词云

Rost http://www.cncrk.com/downinfo/54638.html
图悦 http://www.picdata.cn/
语义分析系统 http://ictclas.nlpir.org/nlpir/
Tagul https://tagul.com/
腾讯文智 http://nlp.qq.com/semantic.cgi
Tagxedo词云 http://www.tagxedo.com/

 舆情分析工具

清博舆情系统 http://yuqing.gsdata.cn/
云相 http://www.weidata.cn/

  PPT模板工具

我图网 http://so.ooopic.com/
51PPT模板 http://www.51pptmoban.com/ppt/
无忧PPT http://www.51ppt.com.cn/
第1PPT http://www.1ppt.com/
站长之家 http://sc.chinaz.com/ppt/
设计师网址导航 http://www.userinterface.com.cn/
HiPPTer –  汇总PPT设计的酷站&神器 http://www.hippter.com
微软的官方网站——OFFICEPlus: http://www.officeplus.cn/
国货精品WPS公司下的稻壳儿 http://www.docer.com/
演界网 http://www.yanj.cn/

 互联网趋势分析工具

微博指数 http://data.weibo.com/index
百度指数 http://index.baidu.com/
好搜指数 http://index.so.com/#index
搜狗指数 http://zhishu.sogou.com/
百度预测 http://trends.baidu.com/

  在线调查工具

腾讯问卷调查 http://wj.qq.com/
麦客 http://www.mikecrm.com/
ICTR http://cn2.ictr.cn/
问道网 http://www.askform.cn/
问卷星 http://www.sojump.com/
调查派 http://www.diaochapai.com/
问卷网 http://www.wenjuan.com/
SurveyMonkey https://zh.surveymonkey.com/

  网站分析监测工具

H5传播分析工具 http://chuanbo.datastory.com.cn/
百度统计 http://tongji.baidu.com/web/welcome/login
腾讯云分析 http://mta.qq.com/
51.la http://www.51.la/

  社交媒体监测工具

孔明社会化媒体管理 http://www.kmsocial.cn/
企业微博管理中心 http://e.weibo.com/
知乎用户深度分析 http://www.kanzhihu.com/useranalysis

 其他数据网站

数据分析网 http://www.afenxi.com
媒体微博排行榜 http://v6.bang.weibo.com/xmt
友盟 http://www.umeng.com/
中国新闻地图 http://vis.360.cn/open/cnnews/
中国票房榜 http://www.cbooo.cn/
收视率排行 http://www.tvtv.hk/archives/category/tv
农业大数据云平台 http://www.dataagri.com/agriculture/gis.action
房价指数 http://industry.fang.com/data/datacenter.aspx
中国统计局 http://data.stats.gov.cn/
中国主要城市拥堵排名 http://report.amap.com/traffic/
中国综合社会调查 http://www.chinagss.org/
中国P2P网贷指数 http://www.p2p001.com/wdzs/wdzs_p2pline.html
Alexa http://www.alexa.com/
易车汽车指数 http://index.bitauto.com/
旅游预测 http://trends.baidu.com/tour/

万水千山总是情,点个 ???? 行不行。

  1. 推荐阅读:
  2. 入门: *全的零基础学Python的问题  | 零基础学了8个月的Python  | 实战项目 |学Python就是这条捷径
  3. 干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA*佳球员分析 |   从万众期待到口碑扑街!唐探3令人失望  | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
  4. 趣味:弹球游戏  | 九宫格  | 漂亮的花 | 两百行Python《天天酷跑》游戏!
  5. AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影

年度爆款文案

Synchronized锁在Spring事务管理下,为啥还线程不安全

开启10000个线程,每个线程给员工表的money字段【初始值是0】加1,没有使用悲观锁和乐观锁,但是在业务层方法上加了synchronized关键字,问题是代码执行完毕后数据库中的money 字段不是10000,而是小于10000 问题出在哪里?

Service层代码:

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

SQL代码(没有加悲观/乐观锁):

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

用1000个线程跑代码:

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

简单来说:多线程跑一个使用synchronized关键字修饰的方法,方法内操作的是数据库,按正常逻辑应该*终的值是1000,但经过多次测试,结果是低于1000。这是为什么呢?

一、我的思考

既然测试出来的结果是低于1000,那说明这段代码不是线程安全的。不是线程安全的,那问题出现在哪呢?众所周知,synchronized方法能够保证所修饰的代码块、方法保证有序性、原子性、可见性。

讲道理,以上的代码跑起来,问题中Service层的increaseMoney()是有序的、原子的、可见的,所以断定跟synchronized应该没关系。

(参考我之前写过的synchronize锁笔记:Java锁机制了解一下)

既然Java层面上找不到原因,那分析一下数据库层面的吧(因为方法内操作的是数据库)。在increaseMoney()方法前加了@Transcational注解,说明这个方法是带有事务的。事务能保证同组的SQL要么同时成功,要么同时失败。讲道理,如果没有报错的话,应该每个线程都对money值进行+1。从理论上来说,结果应该是1000的才对。

(参考我之前写过的Spring事务:一文带你看懂Spring事务!)

根据上面的分析,我怀疑是提问者没测试好(hhhh,逃),于是我也跑去测试了一下,发现是以提问者的方式来使用是真的有问题

首先贴一下我的测试代码:

@RestController
public class EmployeeController {
 @Autowired
 private EmployeeService employeeService;
 @RequestMapping("/add")
 public void addEmployee() {
 for (int i = 0; i < 1000; i++) {
 new Thread(() -> employeeService.addEmployee()).start();
 }
 }
}
@Service
public class EmployeeService {
 @Autowired
 private EmployeeRepository employeeRepository;
 @Transactional
 public synchronized void addEmployee() {
 // 查出ID为8的记录,然后每次将年龄增加一
 Employee employee = employeeRepository.getOne(8);
 System.out.println(employee);
 Integer age = employee.getAge();
 employee.setAge(age + 1);
 employeeRepository.save(employee);
 }
}

简单地打印了每次拿到的employee值,并且拿到了SQL执行的顺序,如下(贴出小部分):

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

从打印的情况我们可以得出:多线程情况下并没有串行执行addEmployee()方法。这就导致对同一个值做重复的修改,所以*终的数值比1000要少。

二、图解出现的原因

发现并不是同步执行的,于是我就怀疑synchronized关键字和Spring肯定有点冲突。于是根据这两个关键字搜了一下,找到了问题所在。

我们知道Spring事务的底层是Spring AOP,而Spring AOP的底层是动态代理技术。跟大家一起回顾一下动态代理:

 public static void main(String[] args) {
 // 目标对象
 Object target ;
 Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Main.class, new InvocationHandler() {
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 // 但凡带有@Transcational注解的方法都会被拦截
 // 1... 开启事务
 method.invoke(target);
 // 2... 提交事务
 return null;
 }
 
 });
 }

(详细请参考我之前写过的动态代理:给女朋友讲解什么是代理模式)

实际上Spring做的处理跟以上的思路是一样的,我们可以看一下TransactionAspectSupport类中invokeWithinTransaction():

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

调用方法开启事务,调用方法提交事务

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

在多线程环境下,就可能会出现:方法执行完了(synchronized代码块执行完了),事务还没提交,别的线程可以进入被synchronized修饰的方法,再读取的时候,读到的是还没提交事务的数据,这个数据不是*新的,所以就出现了这个问题。

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

三、解决问题

从上面我们可以发现,问题所在是因为@Transcational注解和synchronized一起使用了,加锁的范围没有包括到整个事务。所以我们可以这样做:

新建一个名叫SynchronizedService类,让其去调用addEmployee()方法,整个代码如下:

@RestController
public class EmployeeController {
 @Autowired
 private SynchronizedService synchronizedService ;
 @RequestMapping("/add")
 public void addEmployee() {
 for (int i = 0; i < 1000; i++) {
 new Thread(() -> synchronizedService.synchronizedAddEmployee()).start();
 }
 }
}
// 新建的Service类
@Service
public class SynchronizedService {
 @Autowired
 private EmployeeService employeeService ;
	
 // 同步
 public synchronized void synchronizedAddEmployee() {
 employeeService.addEmployee();
 }
}
@Service
public class EmployeeService {
 @Autowired
 private EmployeeRepository employeeRepository;
 
 @Transactional
 public void addEmployee() {
 // 查出ID为8的记录,然后每次将年龄增加一
 Employee employee = employeeRepository.getOne(8);
 System.out.println(Thread.currentThread().getName() + employee);
 Integer age = employee.getAge();
 employee.setAge(age + 1);
 employeeRepository.save(employee);
 }
}

我们将synchronized锁的范围包含到整个Spring事务上,这就不会出现线程安全的问题了。在测试的时候,我们可以发现1000个线程跑起来比之前要慢得多,当然我们的数据是正确的:

搞不懂,Synchronized锁在Spring事务管理下,为啥还线程不安全?

 

*后

可以发现的是,虽然说Spring事务用起来我们是非常方便的,但如果不了解一些Spring事务的细节,很多时候出现Bug了就百思不得其解。还是得继续加油努力呀~~~

 

filter、interceptor、aspect应如何选择

目录

  1. 前言
  2. Filter过滤器
  3. Interceptor拦截器
  4. Aspect切片
  5. 总结

前言

小伙伴们应该听说过过滤器、拦截器、切面,印象上都能够起到截断拦截的作用,在做一些业务需求时,不知道如何选择,今天就来介绍一下他们之间的区别。

Filter过滤器

过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出过滤操作。

过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次。

使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

话不多说,先上代码

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

再定义两个Controller,一个UserController,一个OrderController

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

虽然Filter过滤器和Controller请求都已经定义了,但现在过滤器是不起作用的。需要把Filter配置一下,有两个方案

*个方案在Filter上面加上@Component

@Component
public class TimeFilter implements Filter

第二个方案配置化注册过滤器

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

第二个方案的特点就是可以细化到过滤哪些规则的URL

我们来启动应用时,过滤器被初始化了,init函数被回调

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

请求http://localhost:9000/order/1

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

看看控制台的日志输出

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

请求http://localhost:9000/user/1

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

控制台日志输出

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

停止应用后,控制台输出

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。

1.启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;

2.每一次请求时都只调用方法doFilter()进行处理

3.停止服务器时调用destroy()方法,销毁实例。

我们再来看看doFilter方法

doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

从参数我们看到,filter里面是能够获取到请求的参数和响应的数据;但此方法是无法知道是哪一个Controller类中的哪个方法被执行。

还有一点需要注意的是,filter中是没法使用注入的bean的,也就是无法使用@Autowired

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

上面代码注入的值为null。这是为什么呢

其实Spring中,web应用启动的顺序是:listener->filter->servlet,先初始化listener,然后再来就filter的初始化,再接着才到我们的dispathServlet的初始化,因此,当我们需要在filter里注入一个注解的bean时,就会注入失败,因为filter初始化时,注解的bean还没初始化,没法注入。

如果一定你要使用,需要做一些处理,可以私信老顾哦

Interceptor拦截器

依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在一个方法前,调用一个方法,或者在方法后,调用一个方法。

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

在WebMvcConfigurationSupport配置一下

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

执行结果

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

我们发现拦截器中可以获取到Controller对象

preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

object handler就是controller方法对象

HandlerMethod handlerMethod = (HandlerMethod)handler;
handlerMethod.getBean().getClass().getName(); //获取类名
handlerMethod.getMethod().getName(); //获取方法名

但我们发现获取不到方法的参数值,这个是为什么呢?在DispatcherServlet类中,方法

doDispatch(HttpServletRequest request, HttpServletResponse response)

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

applyPreHandle这个方法执行,就是执行的拦截器的preHandler方法,但这个过程中,controller方法没有从request中获取请求参数,组装方法参数;而是在ha.handle这个方法的时候,才会组装参数

虽然没法得到方法的参数,但是可以获得IOC的bean哦。

再说明一点的是postHandler方法

postHandler方法的执行,当controller内部有异常,posthandler方法是不会执行的。

afterCompletion方法,不管controller内部是否有异常,都会执行此方法;此方法还会有个Exception ex这个参数;如果有异常,ex会有异常值;没有异常 此值为null

注意点如果controller内部有异常,但异常被@ControllerAdvice 异常统一捕获的话,ex也会为null

Aspect切片

AOP操作可以对操作进行横向的拦截,*大的优势在于他可以获取执行方法的参数,对方法进行统一的处理。常见使用日志,事务,请求参数安全验证

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

上面的代码中,我们是可以获取方法的参数的

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

虽然切面aop可以拿到方法参数,但拿不到response,request对象。

总结

我们这里来总结一下过滤器、拦截器、Aspect,看看区别

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

如果三者方式同时采用,那他们的执行顺序是什么呢?

filter -> interceptor -> ControllerAdvice -> aspect -> controller

返回值顺序,或异常返回顺序

controller -> aspect -> controllerAdvice -> Interceptor -> Filter

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

用一个图描述一下执行顺序

阿里二面:filter、interceptor、aspect应如何选择?很多人中招

 

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速