2021年1月31日星期日

变黑、长毛、大小不一?关于乳头你得知道的几件事

核心提示:提到"咪咪",可以说是人人都有,但是可不是人人都了解它的!爱美的你可能会在意乳头的大小、形状,但是遇到一些关于"咪咪"的问题时,也常常一知半解:"为什么我乳房大小不一样?""为什么我乳头变黑了?""我乳头周围怎么会长毛!!"

提到"咪咪",可以说是人人都有,但是可不是人人都了解它的!爱美的你可能会在意乳头的大小、形状,但是遇到一些关于"咪咪"的问题时,也常常一知半解:"为什么我乳房大小不一样?""为什么我乳头变黑了?""我乳头周围怎么会长毛!!",这时候就需要来学习关于乳头的几件事。

一、乳头的作用

乳头也就是乳房中央的圆形凸起,对女性来说,乳头有重要的意义:有哺乳功能,乳汁从乳腺管经由乳头分泌排出,肩负着养育下一代的重责;乳头上含有丰富的神经末梢,是女性很重要的性敏感区。

而另外一提,单纯是两个小点点的话,对男性来说还真除了区分正反面就没其他属性了。

二、关于乳头,你不知道的那些事

1、 乳房大小:一大一小不对称很正常!


就像找不出两片相同的叶子一样,人的两个乳头也是各有不同的,有的大有的小,对于乳头,只要不影响乳汁分泌,且无其他不适,即使不一样大也可以放任不管。但是如果是短期出现大小不一就要有所警惕,可能是乳房病变,应尽快去妇科就诊。

2、乳头颜色:乳头颜色变黑加深和性经历无关

有的人一直抱着女性乳头颜色黑就是性经历丰富的偏见,但其实决定皮肤颜色深浅的是体内黑色素细胞的数量,乳头乳晕部位的黑色素细胞较多,且受性激素影响会更容易发生色素沉着,在一定年龄后,局部皮肤的差异会表现得更明显。

青春期时,大多数人的乳头、乳晕呈玫瑰红色,少数人则偏向咖啡色。而妊娠和哺乳以后,由于体内雌激素和孕激素的变化,孕妇皮肤中的黑色素细胞产生的黑色素会有所增加。除了色素沉积、激素影响外,乳头乳晕的颜色还受到种族差异、遗传因素、以及外界坏境、个人肤色等因素的影响。

3、乳头有毛发很正常

女性的乳晕区域长出一些纤细的绒毛,一般属于正常现象,并不说明你的乳头有问题。如果你不喜欢这些毛毛,可以进行蜡膜脱毛。但如果长得太茂盛,甚至还伴有长痘、肥胖、月经不调等等症状,就要小心多囊卵巢综合症了。

4、乳头凹陷是常见的症状

大约有10%~20%的女性有各种程度的乳头凹陷,患有乳头凹陷不必难于启齿。

轻度乳头内陷的女性,平时也可以自己用拇指和食指在乳晕处揉捏;中重度的乳头内陷,在哺乳时会比较麻烦,也可能造成积乳,可利用一些哺乳工具改善;重度的乳头内陷,则需要去看看医生,必要时可采取手术纠正。

5、乳头敏感度因人而异

都知道了乳头是比较敏感的部位,可能有的人稍微被刺激一下就有反应,顿时欲火焚身,但是并不是所有人的乳头都敏感的。(乳头凸起不一定代表性兴奋,还有可能是冷空气刺激、衣物摩擦等等因素。)

有一种性高潮叫"乳头高潮",即女性没有和丈夫进行实质性生活,丈夫仅仅刺激了她的乳头,就能让她达到性高潮。这是因为大脑对乳头受刺激的兴奋方式,和阴 道感觉皮层受刺激的兴奋方式是一样的。而还有一种"伤心乳头综合征",患上的人碰到乳头不禁不会兴奋,反而会出现难过、忧伤等情绪。

6、乳头能挤出"水",要及时就医

女性在非怀孕期或非哺乳期时出现渗出液,就可能代表了一些问题需要及时就诊。

  • 浆液性:见于妊娠早期、正常月经期、囊性增生症;

  • 黄色、棕色或绿色:多见于慢性乳腺炎;

  • 乳白色:常见于哺乳期前后、积乳囊肿;


  • 脓性:见于急性乳腺炎或乳腺脓肿等。

  • 血水:常见于乳管内乳头状瘤、乳管内乳头状癌、乳管扩张症、慢性乳腺炎等。

39健康网()原创内容,未经授权不得转载,违者必究。内容合作请联系:020-85501999-8819或39media@mail.39.net

原文转载:http://health.shaoqun.com/a/148989.html

跨境电商:https://www.ikjzd.com/

手机trademanager:https://www.ikjzd.com/w/730

trax:https://www.ikjzd.com/w/1489


核心提示:提到"咪咪",可以说是人人都有,但是可不是人人都了解它的!爱美的你可能会在意乳头的大小、形状,但是遇到一些关于"咪咪"的问题时,也常常一知半解:"为什么我乳房大小不一样?""为什么我乳头变黑了?""我乳头周围怎么会长毛!!" 提到"咪咪",可以说是人人都有,但是可不是人人都了解它的!爱美的你可能会在意乳头的大小、形状,但是遇到一些关于"咪咪"的问题时,也常常一知半解:"为什么我乳房大小不
mav:mav
垂直电商:垂直电商
圣诞爆款玩具预测!连续三年蝉联玩具销量冠军的竟是它:圣诞爆款玩具预测!连续三年蝉联玩具销量冠军的竟是它
印度TEC认证:印度TEC认证
平台欢迎"退款无须退货"!卖家又要被薅羊毛了?:平台欢迎"退款无须退货"!卖家又要被薅羊毛了?

258斤锡安多强壮?单挑字母哥推土机上篮,两届MVP被碾压力量完败_鹈鹕

原标题:258斤锡安多强壮?单挑字母哥推土机上篮,两届MVP被碾压力量完败

北京时间1月30日,鹈鹕爆了一个不小的冷门,他们掀翻了东部夺冠热门雄鹿。这一战,锡安和字母哥上演了NBA顶级的天赋对决,状元郎表现不输两届MVP,两人在场上的单挑镜头,锡安还欺负了字母哥。

这场比赛锡安得到了21分9篮板7助攻,是球队爆冷的大工程。字母哥下半场复苏,拿到了38分11篮板5助攻2抢断2封盖,无奈在落后29分的情况下,没能大逆转。

比赛第三节还剩1分14秒,两人上演了直接对话。锡安低位单挑字母哥,他持球面筐,随后转身背打字母哥。体重达到了258斤的锡安利用身体顶住了字母哥,他直接扛着字母哥往篮下走。

就像是一辆推土机,锡安把字母哥顶到了篮下。众所周知,字母哥也是这个联盟的天赋怪,有着超长的臂展和惊人的弹跳,但是,在力量对抗上,字母哥根本顶不住锡安。

杀到篮下,锡安强起,顶开了字母哥,拉杆上篮。字母哥被彻底压制,甚至无法起跳封盖,只能举着双手,但是形同虚设。

相比上赛季,锡安还是让人看到了进步。上赛季两队的交手,字母哥没少封盖锡安,但现在鹈鹕状元打法更加丰富了,而且,他还会传球,没有一味蛮干,7次助攻就是最好的说明。如果锡安能持续进步,鹈鹕还是有机会触底反弹,打进季后赛,毕竟现在他们距离西部第八的开拓者也只有2个胜场的差距。返回搜狐,查看更多

责任编辑:

原文转载:http://sport.shaoqun.com/a/389636.html

跨境电商:https://www.ikjzd.com/

易趣:https://www.ikjzd.com/w/210

腾邦:https://www.ikjzd.com/w/1382


原标题:258斤锡安多强壮?单挑字母哥推土机上篮,两届MVP被碾压力量完败北京时间1月30日,鹈鹕爆了一个不小的冷门,他们掀翻了东部夺冠热门雄鹿。这一战,锡安和字母哥上演了NBA顶级的天赋对决,状元郎表现不输两届MVP,两人在场上的单挑镜头,锡安还欺负了字母哥。这场比赛锡安得到了21分9篮板7助攻,是球队爆冷的大工程。字母哥下半场复苏,拿到了38分11篮板5助攻2抢断2封盖,无奈在落后29分的情况
雨果:雨果
五洲会海购:五洲会海购
亚马逊更新时尚类商品名称要求 / 亚马逊将推出八月银行假日促销:亚马逊更新时尚类商品名称要求 / 亚马逊将推出八月银行假日促销
亚马逊信用卡海淘选择(刷单):亚马逊信用卡海淘选择(刷单)
做亚马逊侵权,可不是闹着玩的:做亚马逊侵权,可不是闹着玩的

2021年1月30日星期六

作为一名合格的开发者,必须了解的编程原则有哪些?

目录

通用

  • KISS (Keep It Simple Stupid)

  • YAGNI

  • 做最简单的事情

  • 关注点分离

  • 保持事情不再重复

  • 为维护者写代码

  • 避免过早优化

  • 童子军军规

  • 2021Java面试宝典

模块间/类

  • 最小化耦合

  • 迪米特法则

  • 组合优于继承

  • 正交性

  • 稳健性原则

  • 控制反转

模块/类

  • 最大化聚合

  • 里氏代换原则

  • 开放/封闭原则

  • 单一职责原则

  • 隐藏实现细节

  • 科里定律

  • 封装经常修改的代码

  • 接口隔离原则

  • 命令查询分离

KISS

大多数系统如果保持简单而不是复杂,效果最好。

为什么

  • 更少的代码可以花更少的时间去写,Bug更少,并且更容易修改。

  • 简单是复杂的最高境界。

  • 完美境地,非冗杂,而不遗。

YAGNI

YAGNI的意思是"你不需要它":在必要之前不要做多余的事情。

为什么

  • 去做任何仅在未来需要的特性,意味着从当前迭代需要完成的功能中分出精力。

  • 它使代码膨胀;软件变得更大和更复杂。

怎么做

  • 在当你真正需要它们的时候,才实现它们,而不是在你预见到你需要它们的时候。

做最简单的事情

为什么

  • 仅有当我们只解决问题本身时,才能最大化地解决实际问题。

怎么做

  • 扪心自问:"最简单的事情是什么?"。

关注点分离

关注点分离是一种将计算机程序分离成不同部分的设计原则,以便每个部分专注于单个关注点。例如,应用程序的业务逻辑是一个关注点而用户界面是另一个关注点。更改用户界面不应要求更改业务逻辑,反之亦然。

引用Edsger W. Dijkstra (1974)所说:

我有时将其称为"关注点分离",即使这不可能完全做到,但它也是我所知道的唯一有效的思维整理技巧。这就是我所说的"将注意力集中在某个方面"的意思:这并不意味着忽略其他方面,只是对于从某一方面的视角公正地来看,另一方面是不相关的事情。

为什么

  • 简化软件应用程序的开发与维护。

  • 当关注点很好地分开时,各个部分可以被重用,并且可以独立开发和更新。

怎么做

  • 将程序功能分成联系部分尽可能少的模块。

保持事情不再重复

在一个系统内,每一项认识都必须有一个单一的、明确的、权威的表示。

程序中的每一项重要功能都应该只在源代码中的一个地方实现。相似的函数由不同的代码块执行的情况下,抽象出不同的部分,将它们组合为一个函数通常是有益的。

为什么

  • 重复(无意或有意的重复)会造成噩梦般的维护,保养不良和逻辑矛盾。

  • 对系统中任意单个元素的修改不需要改变其他逻辑上无关的元素。

  • 此外,相关逻辑的元素的变化都是可预测的和均匀的,因此是保持同步的。

怎么做

  • 只在一个处编写业务规则、长表达式、if语句、数学公式、元数据等。

  • 确定系统中使用的每一项认识的唯一来源,然后使用该源来生成该认识的适用实例(代码、文档、测试等)。

  • 使用三法则(Rule of three).

为维护者写代码

为什么

  • 到目前为止,维护是任何项目中最昂贵的阶段。

怎么做

  • _成为_维护者。

  • 不论何时编写代码,要想着最后维护代码的人是一个知道自己住在哪里的暴力精神病人。

  • 如果某个入门的人掌握了代码,他们就会从阅读和学习代码中获得乐趣,以这样的想法去编写代码和注释。

  • 别让我想(Don't make me think).

  • 使用最少惊讶原则(Principle of Least Astonishment).

避免过早优化

引用Donald Knuth所说:

程序员浪费大量的时间来思考或担心程序的非关键部分的速度,而考研尝试这些优化实际上在调试和维护时有很强的负面影响。比如说在97%的开发时间,我们应该忽略低效率:过早的优化是万恶之源。然而,我们不应该在关键的3%中放弃我们的机会。

当然,需要理解什么是"过早"什么不是"过早"。

为什么

  • 瓶颈在哪是未知的。

  • 优化后,阅读和维护可能会更困难。

怎么做

  • 使它运作,使它正确,使它更快(Make It Work Make It Right Make It Fast)

  • 不要在你不需要的时候优化,只有在你发现一个瓶颈之后才能优化它。

最小化耦合

模块/组件之间的耦合是它们互相依赖的程度,较低的耦合更好。换句话说,耦合是代码单元"B"在未知的代码单元"A"更改后"被破坏"的几率。

为什么

  • 一个模块的更改通常会导致其他模块的更改,产生涟漪效益。

  • 由于模块间的依赖性增加,模块装配可能需要更多的工作和/或时间。

  • 特定的模块可能难以重用和/或测试,因为必须包含相关模块。

  • 开发人员可能害怕更改代码,因为他们不确定什么会收到影响。

怎么做

  • 消除,最小化和降低必要关联的复杂性。

  • 通过隐藏实现细节,减少耦合。

  • 使用迪米特法则。

迪米特法则

不要和陌生人说话。

为什么

  • 这通常会导致更紧密的耦合。

  • 可能会暴露过多的实现细节。

怎么做

对象的方法只能调用以下方法:

  1. 对象自身的方法。

  2. 方法参数中的方法。

  3. 方法中创建的任何对象的方法。

  4. 对象的任何直接属性或字段的方法。

组合优于继承

为什么

  • 类之间的耦合减少。

  • 使用继承,子类很容易做出假设,并破坏里氏代换原则(LSP)。

怎么做

  • 测试LSP(可替换性)以决定何时继承。

  • 当存在"有"(或"使用")的关系时使用组合,当存在"是"的关系时使用继承。

正交性

正交性的基本概念是,概念上不相关的东西在系统中不应该相关。

来源:Be Orthogonal

它越简单,设计越正交,异常就越少。这使得用编程语言学习、读写程序变得更容易。正交特征的含义是独立于环境;关键参数是对称性与一致性。

来源:Orthogonality

稳健性原则

坚持保守自己的作为,自由接受他人的作为。

合作的服务依赖于彼此的接口。通常,接口需要提升,导致另一端接收未指定的数据。如果接收到的数据没有严格遵守规范,那么简单的实现将仅拒绝合作。更复杂的实现却可以忽略它无法识别的数据。

为什么

  • 为了能够提高服务,你需要确保提供者可以进行更改以支持新的需求,同时对现有客户端造成最小的破坏。

怎么做

  • 向其他机器(或同一机器上的其他程序)发送指令或数据的代码应该完全符合规范,但接受输入的代码应接受不一致的输入,只要其意义明确。

控制反转

控制反转又被称为好莱坞原则,"不要打电话给我们,我们会打电话给你"。它是一种设计原则,计算机程序的自定义编写部分从通用框架接收控制流。控制反转具有强烈的含义,即可重用代码和特定于问题的代码是独立开发的,即使它们在应用程序中一同工作。

为什么

  • 控制反转用于提高程序的模块性,使其具有可扩展性。

  • 将任务的执行与实现分离。

  • 将模块集中在其设计任务上。

  • 使模块不受关于其他系统如何执行其任务的假设约束,而是依赖于约定。

  • 以防止模块更换时出现副作用。

怎么做

  • 使用工厂模式

  • 使用服务定位器模式

  • 使用依赖注入

  • 使用依赖查找

  • 使用模板方法模式

  • 使用策略模式

最大化聚合

单个模块/组件的聚合性是其职责形成有意义的单元的程度,越高的聚合性越好。

为什么

  • 增加了理解模块的难度。

  • 增加了维护系统的难度,因为域中逻辑的更改会影响多个模块,并且一个模块的更改需要相关模块的更改。

  • 由于大多数应用程序不需要模块提供的随机操作集,因此重用模块的难度增加。

怎么做

  • 与组相关的功能共享一项职责(例如在一个类中)。

里氏代换原则

里氏代换原则(LSP)完全是关于对象的预期行为:

程序中的对象应该可以替换为其子类型的实例,而不会改变该程序的正确性。

开放/封闭原则

软件实体(例如类)应对扩展是开放的,但对修改是封闭的。也就是说,这样的实体可以允许在不改变其源代码的情况下修改其行为。

为什么

  • 通过最小化对现有代码的修改来提高可维护性和稳定性

怎么做

  • 编写可以扩展的类(而不是可以修改的类)

  • 只暴露需要更换的活动部分,隐藏其他所有部分。

单一职责原则

一个类不应该有多个修改的原因。

长话版:每个类都应该有一个单独的职责,并且该职责应该完全由该类封装。职责可以定义为修改的原因,一次类或模块应该有且仅有一个修改的原因。

为什么

  • 可维护性:仅有一个模块或类中需要修改。

怎么做

  • 使用 科里定律.

隐藏实现细节

软件模块通过提供接口来隐藏信息(即实现细节),而不泄露任何不必要的信息。

为什么

  • 当实现更改时,客户端使用的接口不必更改。

怎么做

  • 最小化类和成员的可访问性。

  • 不要公开成员数据。

  • 避免将私有实现细节放入类的接口中。

  • 减少耦合以隐藏更多实现细节。

科里定律

科里定律是关于为任何特定代码选择一个明确定义的目标:仅做一件事。

  • Curly's Law: Do One Thing

  • The Rule of One or Curly's Law

封装经常修改的代码

一个好的设计可以辨别出最有可能改变的热点,并将它们封装在API之后。当预期的修改发生时,修改会保持在局部。

为什么

  • 在发生更改时,最小化所需的修改。

怎么做

  • 封装API背后不同的概念。

  • 将可能不同的概念分到各自的模块。

接口隔离原则

将臃肿的接口减少到多个更小更具体的客户端特定接口中。接口应该比实现它的代码更依赖于调用它的代码。

为什么

  • 如果类实现了不需要的方法,则调用方需要了解该类的方法实现。例如,如果一个类实现了一个方法,但只是简单的抛出异常,那么调用方将需要知道实际上不应该调用这个方法。

怎么做

  • 避免臃肿的接口。类不应该实现任何违反单一职责原则的方法。

童子军军规

美国童子军有一条简单的军规,我们可以使用到我们的职业中:"离开营地时比你到达时更干净"。根据童子军军规,我们应该至终保持代码比我们看到时更干净。

为什么

  • 当对现有代码库进行更改时,代码质量往往会降低,从而积累技术债务。根据童子军军规,我们应该注意每一个提交(Commit)的质量。无论规模有多小,技术债务都会受到不断重构的抵制。

怎么做

  • 每次提交都要确保它不会降低代码库的质量。

  • 任何时候,如果有人看到一些代码不够清楚,他们就应该抓住机会在那里修复它。

命令查询分离

命令查询分离原则规定,每个方法都应该是执行操作的命令,或者是向调用者返回数据但不能同时做两件事的查询。提问不应该改变答案。

利用这个原则,程序员可以更加自信地进行编码。查询方法可以在任何地方以任何顺序使用,因为它们不会改变状态。而使用命令,你必须更加小心。

为什么

  • 通过将方法清晰地分为查询和命令,程序员可以在不了解每个方法的实现细节的情况下,更加自信地编码。

怎么做

  • 将每个方法实现为查询或命令。2021Java面试宝典

  • 对方法名使用命名约定,该方法名表示该方法是查询还是命令。












原文转载:http://www.shaoqun.com/a/521207.html

跨境电商:https://www.ikjzd.com/

跨境通网站:https://www.ikjzd.com/w/1329

识货:https://www.ikjzd.com/w/1745


目录通用KISS(KeepItSimpleStupid)YAGNI做最简单的事情关注点分离保持事情不再重复为维护者写代码避免过早优化童子军军规2021Java面试宝典模块间/类最小化耦合迪米特法则组合优于继承正交性稳健性原则控制反转模块/类最大化聚合里氏代换原则开放/封闭原则单一职责原则隐藏实现细节科里定律封装经常修改的代码接口隔离原则命令查询分离KISS大多数系统如果保持简单而不是复杂,效果最好
深诺互动:深诺互动
modcloth:modcloth
旺季物流:意大利稳了,欧洲4国运费有调整! :旺季物流:意大利稳了,欧洲4国运费有调整!
销量猛涨3倍,屠榜品类TOP?卖家私藏亚马逊日本站时尚品类爆单秘诀!:销量猛涨3倍,屠榜品类TOP?卖家私藏亚马逊日本站时尚品类爆单秘诀!
国务院:关于印发6个新设自由贸易试验区总体方案的通知:国务院:关于印发6个新设自由贸易试验区总体方案的通知

极光之谜:神秘激光与太阳风暴有关?!

       极光(Aurora),是一种绚丽多彩的发光现象,其发生是由于太阳带电粒子流(太阳风)进入地球磁场,在地球南北两极附近地区的高空,夜间出现的灿烂美丽的光辉。在南极被称为南极光,在北极被称为北极光。地球的极光是来自地球磁层或太阳的高能带电粒子流(太阳风)使高层大气分子或原子激发(或电离)而产生。

       在南北两极附近地区的夜晚,偶尔可以看到几条光带横跨天空,把黑暗的夜空照得一片光亮,这种壮丽动人的景象就是极光。极光是人们能看到的最美丽、最不可思议的自然现象,也是太阳风暴期间在地面唯一可以用肉眼看到的日地空间现象。极光越壮观,往往标志着太阳爆发越剧烈。

  神秘的极光

   极光是一种神秘的天象,自古以来为人所瞩目。许多世纪以来,不同地方的人都有自己的极光记载和传说。

  “触龙”下凡 

   人们所知最早的极光记录于公元前2600年。在我国古书《河图稽命征》中说道:“附宝(黄帝的母亲)见大电光绕北斗权星,照耀郊野,感而孕二十五月,而生黄帝轩辕于青邱。”在先秦古书《山海经》中提到,极光是一位名叫“触龙”的神仙,形貌如一条红色的蛇:“人面蛇身,赤色,身长千里,钟山之神也”。

   “极光”这一术语来源于拉丁文“伊欧斯”这个词。传说“伊欧斯”是希腊神话中“黎明”的化身,是希腊神“泰坦”的女儿,太阳神和月亮女神的姐姐。“极光”这一名称却来源于罗马神话中的织架女神“奥罗拉”(Aurora), 她掌管极光,代表旭日东升前的黎明。那么,如此美丽迷人的光辉是如何发出的呢?

  极光女神“厄俄斯”

  极光是怎么产生的?

   极光这一天象之谜,直到最近这些年才逐渐有了合理的解释。其实,这还要从这些光能的来源——太阳说起。从太阳上喷发出来的大量带电粒子,以每秒几百公里的速度吹向行星际空间,形成太阳风。到达地球附近的粒子不断撞击地球磁场,并环绕地球流动。在太阳风的吹动下,地球磁场不再是对称的,已经变成某种“流线型”。由于与行星际磁场的相互作用,变形的地球磁场的两极外各形成一个磁力线集中的“漏斗区”。当磁层出现扰动时,磁尾的带电粒子被加速,沿磁力线运动,如流水般顺着漏斗边缘倒入“漏斗区”,并撞击高层大气中的气体分子和原子,使后者被激发——退激而发光,于是便产生了这种“鬼怪之光”。

粒子沉降在两极漏斗区

   当太阳风暴发生时,磁层扰动变得剧烈起来,发生地磁暴。这时,就会激发出更多种颜色的单色光。这些光混合在一起,便出现了五颜六色、奇异壮观的极光,就像五彩的霓虹灯一样。

 

  极光的产生

   用一个形象的比喻,可以说极光活动就像地球磁层活动的实况电视画面。地球磁层是一个巨大无比的电视机显像管,将进入高空大气的太阳风粒子流汇聚成束,聚焦到地球的两极地区。沉降粒子为电视机的电子束,地球磁场为电子束导向磁场,地球极区大气为荧光屏,极光则是电视屏幕上移动的图像。住在南北两极地区的人们每天都可以收看到这天然的巨幅电视。

 

  电子束打击荧光屏发光



原文转载:http://tech.shaoqun.com/a/317684.html

跨境电商:https://www.ikjzd.com/

airwallex:https://www.ikjzd.com/w/1011

拍怕网:https://www.ikjzd.com/w/2205


极光(Aurora),是一种绚丽多彩的发光现象,其发生是由于太阳带电粒子流(太阳风)进入地球磁场,在地球南北两极附近地区的高空,夜间出现的灿烂美丽的光辉。在南极被称为南极光,在北极被称为北极光。地球的极光是来自地球磁层或太阳的高能带电粒子流(太阳风)使高层大气分子或原子激发(或电离)而产生。在南北两极附近地区的夜晚,偶尔可以看到几条光带横跨天空,把黑暗的夜空照得一片光亮,这种壮丽动人的景象就是极光
auditor:auditor
ideal:ideal
亚马逊中国成为被执行人 执行标的23420:亚马逊中国成为被执行人 执行标的23420
2020黑五网一在即!船长BI带你解锁旺季制胜大招:2020黑五网一在即!船长BI带你解锁旺季制胜大招
解读亚马逊CPC广告打法:解读亚马逊CPC广告打法

2021最新 Spring面试题精选(附刷题小程序)

推荐使用小程序阅读

为了能让您更加方便的阅读
本文所有的面试题目均已整理至小程序《面试手册
可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验!

ah_xct


目录
  • 推荐使用小程序阅读
  • 1. Spring基础
    • 1.1 什么是 spring?
    • 1.2 Spring框架的设计目标,设计理念,和核心是什么?
    • 1.3 Spring 框架中都用到了哪些设计模式?
    • 1.4 Spring Framework 中有多少个模块,它们分别是什么?
    • 1.5 spring context应用上下文有什么作用?
    • 1.6 Spring 应用程序有哪些不同组件?
    • 1.7 使用 Spring 有哪些方式?
    • 1.8 Spring的优缺点是什么?
    • 1.9 Spring框架中有哪些不同类型的事件?
  • 2. Spring IoC
    • 2.1 什么是 Spring IOC 容器?
    • 2.2 控制反转(IoC)有什么作用?
    • 2.3 可以通过多少种方式完成依赖注入?
    • 2.4 Spring 如何设计容器的,BeanFactory和ApplicationContext的关系详解?
    • 2.5 IoC 的好处有哪些?
    • 2.6 Spring IoC 的实现机制
    • 2.7 Spring 的 IoC支持哪些功能?
    • 2.8 BeanFactory 和 ApplicationContext有什么区别?
    • 2.9 ApplicationContext通常的实现是什么?
    • 2.10 什么是Spring的依赖注入?
    • 2.11 依赖注入的基本原则是什么?
    • 2.12 依赖注入有什么优势?
    • 2.13 构造器依赖注入和 Setter方法注入有什么区别?
  • 3. Spring Beans
    • 3.1 什么是 spring bean?
    • 3.2 spring 提供了哪些配置方式?
    • 3.3 spring 支持几种 bean scope?
    • 3.4 spring bean 容器的生命周期是什么样的?
    • 3.5 什么是 spring 的内部 bean?
    • 3.6 什么是bean装配?什么是bean的自动装配?
    • 3.7 自动装配有哪些方式?
    • 3.8 自动装配有什么局限?
    • 3.9 使用@Autowired注解自动装配的过程是怎样的?
    • 3.10 Spring配置文件包含了哪些信息?
    • 3.11 Spring基于
    • 3.12 Spring框架中的单例bean是线程安全的吗?
    • 3.13 Spring如何处理线程并发问题?
    • 3.14 bean生命周期方法有哪些? 你能重载它们吗?
    • 3.15 在 Spring中如何注入一个java集合?
    • 3.16 你可以在Spring中注入一个null 和一个空字符串吗?
  • 4. Spring注解
    • 4.1 什么是基于Java的Spring注解配置?
    • 4.2 怎样开启注解装配?
    • 4.3 @Component, @Controller, @Repository, @Service 有何区别?
    • 4.4 @Required 注解有什么作用?
    • 4.5 @Autowired 注解有什么作用?
    • 4.6 @Autowired和@Resource有什么区别?
    • 4.7 @Qualifier注解有什么作用?
    • 4.8 @RequestMapping注解有什么用?
  • 5. Spring数据访问
    • 5.1 解释对象/关系映射集成模块
    • 5.2 在Spring框架中如何更有效地使用JDBC?
    • 5.3 解释JDBC抽象和DAO模块的作用是什么?
    • 5.4 Spring DAO 有什么用?
    • 5.5 Spring JDBC API 中存在哪些类?
    • 5.6 JdbcTemplate是什么?
    • 5.7 Spring通过什么方式访问Hibernate?
    • 5.8 如何通过HibernateDaoSupport将Spring和Hibernate结合起来?
    • 5.9 Spring支持的事务管理类型是什么?spring 事务实现方式有哪些?
    • 5.10 Spring事务的实现方式和实现原理是什么?
    • 5.11 Spring的事务传播行为有那些?
    • 5.12 说一下 Spring 的事务隔离?
    • 5.13 Spring框架的事务管理有哪些优点?
    • 5.14 你更倾向用那种事务管理类型?
  • 6. Spring AOP
    • 6.1 什么是AOP?
    • 6.2 Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?
    • 6.3 JDK动态代理和CGLIB动态代理的区别是什么?
    • 6.4 如何理解 Spring 中的代理?
    • 6.5 请解释一下Spring AOP核心的名称分别是什么意思?
    • 6.6 为什么Spring只支持方法级别的连接点?
    • 6.7 Spring AOP 中,关注点和横切关注的区别是什么?
    • 6.8 Spring通知有哪些类型?
    • 6.9 什么是切面 Aspect?
    • 6.10 什么是基于
    • 6.11 什么是基于注解的切面实现?
    • 6.12 有几种不同类型的自动代理?


1. Spring基础

1.1 什么是 spring?

Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。

Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发

Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。

为了降低Java开发的复杂性,Spring采取了以下4种关键策略

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。

1.2 Spring框架的设计目标,设计理念,和核心是什么?

  • Spring设计目标
    Spring为开发者提供一个一站式轻量级应用开发平台;

  • Spring设计理念
    在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;Spring通过IoC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;

  • Spring框架的核心
    IoC容器和AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。

IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

1.3 Spring 框架中都用到了哪些设计模式?

  1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
  2. 单例模式:Bean默认为单例模式。
  3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
  4. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

1.4 Spring Framework 中有多少个模块,它们分别是什么?

Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器(Core Container)AOP(Aspect Oriented Programming)和设备支持(Instrmentation)数据访问与集成(Data Access/Integeration)Web消息(Messaging)Test等 几 个模块中。 以下是 Spring 5 的模块结构图:

  • Spring 核心容器
    提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。 它包含以下模块:

    • Spring Core
    • Spring Bean
    • SpEL (Spring Expression Language)
    • Spring Context
  • AOP
    该层支持面向切面编程

  • 数据访问/集成
    该层提供与数据库交互的支持。它包含以下模块:

    • JDBC (Java DataBase Connectivity)
    • ORM (Object Relational Mapping)
    • OXM (Object
    • JMS (Java Messaging Service)
    • Transaction
  • Web
    该层提供了创建 Web 应用程序的支持 。 它包含以下模块:

    • Web
    • Servlet
    • WSocket
    • Portlet
  • Instrumentation
    该层为类检测和类加载器实现提供支持。

  • Test
    该层为使用 JUnit 和 TestNG 进行测试提供支持。

  • 其他模块

    • Messaging
      该模块为 STOMP 提供支持 。 它还支持注解编程模型,该模型用于从
    • WebSocket
      客户端路由和处理 STOMP 消息。
    • Aspects
      该模块为与AspectJ的集成提供支持 。

1.5 spring context应用上下文有什么作用?

这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。

Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.

1.6 Spring 应用程序有哪些不同组件?

Spring 应用一般有以下组件:

  • 接口
    定义功能。

  • Bean 类
    它包含属性,setter 和 getter 方法,函数等。

  • Spring 面向切面编程(AOP)
    提供面向切面编程的功能。

  • Bean 配置文件
    包含类的信息以及如何配置它们。

  • 用户程序
    它使用接口

1.7 使用 Spring 有哪些方式?

  • 作为一个成熟的 Spring Web 应用程序。

  • 作为第三方 Web 框架,使用 Spring Frameworks 中间层。

  • 用于远程使用。

  • 作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。

1.8 Spring的优缺点是什么?

  • 优点

    • 方便解耦,简化开发
      Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。

    • AOP编程的支持
      Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。

    • 声明式事务的支持
      只需要通过配置就可以完成对事务的管理,而无需手动编程。

    • 方便程序的测试
      Spring对Junit4支持,可以通过注解方便的测试Spring程序。

    • 方便集成各种优秀框架
      Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。

    • 降低JavaEE API的使用难度
      Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。

  • 缺点

    • Spring明明一个很轻量级的框架,却给人感觉大而全
    • Spring依赖反射,反射影响性能
    • 使用门槛升高,入门Spring需要较长时间

1.9 Spring框架中有哪些不同类型的事件?

Spring 提供了以下5种标准的事件:

  1. 上下文更新事件(ContextRefreshedEvent):
    在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):
    当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):
    当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):
    当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):
    在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

2. Spring IoC

2.1 什么是 Spring IOC 容器?

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的"控制反转"概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期

2.2 控制反转(IoC)有什么作用?

  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

2.3 可以通过多少种方式完成依赖注入?

通常,依赖注入可以通过三种方式完成,即:

  • 构造函数注入

  • setter 注入

  • 接口注入(由于在灵活性和易用性比较差,现在从Spring4开始已被废弃)

2.4 Spring 如何设计容器的,BeanFactory和ApplicationContext的关系详解?

Spring 作者 Rod Johnson 设计了两个接口用以表示容器。

  • BeanFactory
  • ApplicationContext

BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 "低级容器"

ApplicationContext 可以称之为 "高级容器"。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 "应用上下文", 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。

当然,除了这两个大接口,还有其他的辅助接口,这里就不介绍他们了。

BeanFactory和ApplicationContext的关系

为了更直观的展示 "低级容器" 和 "高级容器" 的关系,这里通过常用的 ClassPath
最上面的是 BeanFactory,下面的 3 个绿色的,都是功能扩展接口,这里就不展开讲。

看下面的隶属 ApplicationContext 粉红色的 "高级容器",依赖着 "低级容器",这里说的是依赖,不是继承哦。他依赖着 "低级容器" 的 getBean 功能。而高级容器有更多的功能:支持不同的信息源头,可以访问文件资源,支持应用事件(Observer 模式)。

通常用户看到的就是 "高级容器"。 但 BeanFactory 也非常够用啦!

左边灰色区域的是 "低级容器", 只负载加载 Bean,获取 Bean。容器其他的高级功能是没有的。例如上图画的 refresh 刷新 Bean 工厂所有配置,生命周期事件回调等。

小结

说了这么多,不知道你有没有理解Spring IoC? 这里小结一下:IoC 在 Spring 里,只需要低级容器就可以实现,2 个步骤:

  1. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
  2. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。

上面就是 Spring 低级容器(BeanFactory)的 IoC。

至于高级容器 ApplicationContext,他包含了低级容器的功能,当他执行 refresh 模板方法的时候,将刷新整个容器的 Bean。同时其作为高级容器,包含了太多的功能。一句话,他不仅仅是 IoC。他支持不同信息源头,支持 BeanFactory 工具类,支持层级容器,支持访问文件资源,支持事件发布通知,支持接口回调等等。

2.5 IoC 的好处有哪些?

  1. 它将最小化应用程序中的代码量。
  2. 它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例
  3. 或JNDI 查找机制。
  4. 它以最小的影响和最少的侵入机制促进松耦合。
  5. 它支持即时的实例化和延迟加载服务。

2.6 Spring IoC 的实现机制

Spring 中的 IoC 的实现原理就是工厂模式加反射机制 。
示例:

interface Fruit { public abstract void eat();}class Apple implements Fruit { public void eat(){ System.out.println("Apple"); }}class Orange implements Fruit { public void eat(){ System.out.println("Orange"); }}class Factory { public static Fruit getInstance(String ClassName) {  Fruit f=null; try {  f=(Fruit)Class.forName(ClassName).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; }}class Client { public static void main(String[] a) { Fruit f=Factory.getInstance("com.xxx.xxx.Apple"); if(f!=null){  f.eat(); } }}

2.7 Spring 的 IoC支持哪些功能?

Spring 的 IoC 设计支持以下功能:

  • 依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)

其中,最重要的就是依赖注入,从

对于 IoC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。

2.8 BeanFactory 和 ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

  • 依赖关系
    BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
    ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

    • 继承MessageSource,因此支持国际化。
    • 统一的资源文件访问方式。
    • 提供在监听器中注册bean的事件。
    • 同时加载多个配置文件。
    • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
  • 加载方式
    BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
    ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
    相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

  • 创建方式
    BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

  • 注册方式
    BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

2.9 ApplicationContext通常的实现是什么?

  • FileSystem :
    此容器从一个

  • ClassPath:
    此容器也从一个

  • Web:
    此容器加载一个

2.10 什么是Spring的依赖注入?

控制反转IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入依赖查找

依赖注入:相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

2.11 依赖注入的基本原则是什么?

依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,"查找资源"的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。

2.12 依赖注入有什么优势?

依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

2.13 构造器依赖注入和 Setter方法注入有什么区别?

构造函数注入setter 注入
没有部分注入有部分注入
不会覆盖 setter 属性会覆盖 setter 属性
任意修改都会创建一个新实例任意修改不会创建一个新实例
适用于设置很多属性适用于设置少量属性

两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。

3. Spring Beans

3.1 什么是 spring bean?

  1. 它们是构成用户应用程序主干的对象。
  2. Bean 由 Spring IoC 容器管理。
  3. 它们由 Spring IoC 容器实例化,配置,装配和管理。
  4. Bean 是基于用户提供给容器的配置元数据创建。

3.2 spring 提供了哪些配置方式?

  • 基于bean 所需的依赖项和服务在

    <bean id="studentbean" > <property name="name" value="Edureka"></property></bean>
  • 基于注解配置
    您可以通过在相关的类,方法或字段声明上使用注解,将 bean 配置为组件类本身,而不是使用

    <beans><context:annotation-config/><!-- bean definitions go here --></beans>
  • 基于 Java API 配置
    Spring的Java配置是通过使用@Bean和@Configuration来实现 。

    • @Bean
      注解扮演与元素相同的角色 。
    • @Configuration 类
      允许通过简单地调用同一个类中的其他@Bean方法来定义bean间依赖关系。

    例如:

    @Configurationpublic class StudentConfig { @Bean public StudentBean myStudent() { return new StudentBean(); }}

3.3 spring 支持几种 bean scope?

Spring bean 支持5种scope:

  • Singleton
    每个 Spring IoC 容器仅有一个单实例 。
  • Prototype
    每次请求都会产生一个新的实例。
  • Request
    每一次 HTTP 请求都会产生一个新的实例,并且该 bean 仅在当前 HTTP 请求内有效。
  • Session
    每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效。
  • Global-session
    类似于标准的 HTTP Session 作用域,不过它仅仅在基于 portlet 的 web 应用中才有意义 。 Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。 在 global session 作用域中定义的 bean 被限定于全局 portlet Session 的生命周期范围内。 如果你在 web 中使用 global session 作用域来标识 bean,那么 web 会自动当成 session 类型来使用。

仅当用户使用支持Web的ApplicationContext时,最后三个才可用 。

3.4 spring bean 容器的生命周期是什么样的?

spring bean 容器的生命周期流程如下:

1、Spring 容器根据配置中的 bean 定义中实例化 bean。
2、Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。
3、如果 bean 实现 BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用setBeanName()。
4、如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。
5、如果存在与 bean 关联的任何BeanPostProcessors,则调用 preProcessBeforeInitialization() 方法 。
6、如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。
7、最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法。
8、如果 bean 实现 DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。
9、如果为 bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。

3.5 什么是 spring 的内部 bean?

只有将 bean 用作另一个 bean 的属性时,才能将 bean 声明为内部 bean。为了定义 bean, Spring 的基于 或 中提供了 元素的使用 。 内部 bean 总是匿名的,它们总是作为原型。

例如,假设我们有一个 Student 类,其中引用了 Person 类。 这里我们将只创建一个 Person 类实例并在 Student 中使用它。

Student.java

public class Student { private Person person; //Setters and Getters}

Person.java

public class Person { private String name; private String address; //Setters and Getters}

bean.

<bean id="StudentBean" > <property name="person"> <!--This is inner bean --> <bean >  <property name="name" value="Scott"></property> <property name="address" value="Bangalore"></property> </bean> </property></bean>

3.6 什么是bean装配?什么是bean的自动装配?

装配,或bean装配是指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。

在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring 容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

3.7 自动装配有哪些方式?

Spring容器能够自动装配bean。也就是说,可以通过检查BeanFactory的内容让Spring自动解析bean的协作者。

自动装配的不同模式:

  • no
    这是默认设置,表示没有自动装配 。 应使用显式 bean 引用进行装配 。
  • byName
    它根据 bean 的名称注入对象依赖项 。 它匹配并装配其属性与
  • byType
    它根据类型注入对象依赖项。如 果属性的类型与
  • 构造函数
    它通过调用类的构造函数来注入依赖项 。它有大量的参数 。
  • autodetect
    首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。

3.8 自动装配有什么局限?

  • 覆盖的可能性
    您始终可以使用 设置指定依赖项,这将覆盖自动装配。
  • 基本元数据类型
    简单属性(如原数据类型,字符串和类)无法自动装配。
  • 模糊特性
    自动装配不如显式装配精确,如果有可能,建议使用显式装配。

3.9 使用@Autowired注解自动装配的过程是怎样的?

使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。

在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

  • 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
  • 如果查询的结果不止一个,那么@Autowired会根据名称来查找;
  • 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

3.10 Spring配置文件包含了哪些信息?

Spring配置文件是个

3.12 Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把"singleton"变更为"prototype",这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 有状态就是有数据存储功能。
  • 无状态就是不会保存数据。

3.13 Spring如何处理线程并发问题?

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了"时间换空间"的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了"空间换时间"的方式。

ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

3.14 bean生命周期方法有哪些? 你能重载它们吗?

有两个重要的bean 生命周期方法:

  • 第一个是setup :
    它是在容器加载bean的时候被调用。
  • 第二个方法是 teardown :
    它是在容器卸载类的时候被调用。

bean 标签有两个重要的属性(init-methoddestroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct@PreDestroy)。

3.15 在 Spring中如何注入一个java集合?

Spring提供以下几种集合的配置元素:

  1. 类型用于注入一列值,允许有相同的值。
  2. 类型用于注入一组值,不允许有相同的值。
  3. 类型用于注入一组键值对,键和值都可以为任意类型。
  4. 类型用于注入一组键值对,键和值都只能为String类型。

3.16 你可以在Spring中注入一个null 和一个空字符串吗?

可以。

4. Spring注解

4.1 什么是基于Java的Spring注解配置?

基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过

以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。

另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。

@Configurationpublic class StudentConfig { @Bean public StudentBean myStudent() {  return new StudentBean(); }}

4.2 怎样开启注解装配?

注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 <context:annotation-config/>元素。

4.3 @Component, @Controller, @Repository, @Service 有何区别?

  • @Component:
    这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。

  • @Controller:
    这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。

  • @Service:
    此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。

  • @Repository:
    这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

4.4 @Required 注解有什么作用?

这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。示例:

public class Employee { private String name; @Required public void setName(String name){  this.name=name; } public string getName(){  return name; }}

4.5 @Autowired 注解有什么作用?

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。

public class Employee { private String name; @Autowired public void setName(String name) {  this.name=name; } public string getName(){  return name; }}

4.6 @Autowired和@Resource有什么区别?

@Autowired可用于:构造函数、成员变量、Setter方法

@Autowired和@Resource之间的区别

  • @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
  • @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

4.7 @Qualifier注解有什么作用?

当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。

4.8 @RequestMapping注解有什么用?

@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:

  • 类级别:
    映射请求的 URL
  • 方法级别:
    • 映射 URL
    • HTTP 请求方法

5. Spring数据访问

5.1 解释对象/关系映射集成模块

Spring 通过提供ORM模块,支持我们在直接JDBC之上使用一个对象/关系映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS,JPA,TopLink,JDO,OJB 。Spring的事务管理同样支持以上所有ORM框架及JDBC。

5.2 在Spring框架中如何更有效地使用JDBC?

使用Spring JDBC 框架,资源管理和错误处理的代价都会被减轻。所以开发者只需写statements 和 queries从数据存取数据,JDBC也可以在Spring框架提供的模板类的帮助下更有效地被使用,这个模板叫JdbcTemplate

5.3 解释JDBC抽象和DAO模块的作用是什么?

通过使用JDBC抽象和DAO模块,保证数据库代码的简洁,并能避免数据库资源错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一的异常访问层。它还利用Spring的AOP 模块给Spring应用中的对象提供事务管理服务。

5.4 Spring DAO 有什么用?

Spring DAO(数据访问对象) 使得 JDBC,Hibernate 或 JDO 这样的数据访问技术更容易以一种统一的方式工作。这使得用户容易在持久性技术之间切换。它还允许您在编写代码时,无需考虑捕获每种技术不同的异常。

5.5 Spring JDBC API 中存在哪些类?

  1. JdbcTemplate
  2. SimpleJdbcTemplate
  3. NamedParameterJdbcTemplate
  4. SimpleJdbcInsert
  5. SimpleJdbcCall

5.6 JdbcTemplate是什么?

JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理。

5.7 Spring通过什么方式访问Hibernate?

在Spring中有两种方式访问Hibernate:

  • 使用 Hibernate 模板和回调进行控制反转
  • 扩展 HibernateDAOSupport 并应用 AOP 拦截器节点

5.8 如何通过HibernateDaoSupport将Spring和Hibernate结合起来?

用Spring的 SessionFactory 调用 LocalSessionFactory。集成过程分三步:

  • 配置the Hibernate SessionFactory
  • 继承HibernateDaoSupport实现一个DAO
  • 在AOP支持的事务中装配

5.9 Spring支持的事务管理类型是什么?spring 事务实现方式有哪些?

Spring支持两种类型的事务管理:

  • 编程式事务管理
    这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。

  • 声明式事务管理
    这意味着你可以将业务代码和事务管理分离,你只需用注解和

5.10 Spring事务的实现方式和实现原理是什么?

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。

5.11 Spring的事务传播行为有那些?

Spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

  1. PROPAGATION_REQUIRED:
    如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  2. PROPAGATION_SUPPORTS:
    支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  3. PROPAGATION_MANDATORY:
    支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW:
    创建新事务,无论当前存不存在事务,都创建新事务。
  5. PROPAGATION_NOT_SUPPORTED:
    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER:
    以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:
    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

5.12 说一下 Spring 的事务隔离?

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

  1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
  2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
  3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
  4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
  5. ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

不可重复读 :是指在一个事务内,多次读同一数据。

幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

5.13 Spring框架的事务管理有哪些优点?

  • 为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
  • 为编程式事务管理提供了一套简单的API而不是一些复杂的事务API
  • 支持声明式事务管理。
  • 和Spring各种数据访问抽象层很好得集成。

5.14 你更倾向用那种事务管理类型?

大多数Spring框架的用户选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

6. Spring AOP

6.1 什么是AOP?

OOP(Object-Oriented Programming)面向对象编程,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。

AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为"切面"(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。

6.2 Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

1. AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。2. Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

6.3 JDK动态代理和CGLIB动态代理的区别是什么?

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。

6.4 如何理解 Spring 中的代理?

将 Advice 应用于目标对象后创建的对象称为代理。在客户端对象的情况下,目标对象和代理对象是相同的。

Advice + Target Object = Proxy

6.5 请解释一下Spring AOP核心的名称分别是什么意思?

  1. 切面(Aspect):
    切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
  2. 连接点(Join point):
    指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
  3. 通知(Advice):
    在AOP术语中,切面的工作被称为通知。
  4. 切入点(Pointcut):
    切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
  5. 引入(Introduction):
    引入允许我们向现有类添加新方法或属性。
  6. 目标对象(Target Object):
    被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
  7. 织入(Weaving):
    织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
    • 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
    • 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
    • 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。

6.6 为什么Spring只支持方法级别的连接点?

因为Spring基于动态代理,所以Spring只支持方法连接点。Spring缺少对字段连接点的支持,而且它不支持构造器连接点。方法之外的连接点拦截功能,我们可以利用Aspect来补充。

6.7 Spring AOP 中,关注点和横切关注的区别是什么?

关注点(concern)是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。

横切关注点(cross-cutting concern)是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。

6.8 Spring通知有哪些类型?

在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。

Spring切面可以应用5种类型的通知:

  1. 前置通知(Before):在目标方法被调用之前调用通知功能;
  2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

同一个aspect,不同advice的执行顺序:

  • 没有异常情况下的执行顺序:
    • around before advice
    • before advice
    • target method 执行
    • around after advice
    • after advice
    • afterReturning
  • 有异常情况下的执行顺序:
    • around before advice
    • before advice
    • target method 执行
    • around after advice
    • after advice
    • afterThrowing:异常发生
    • java.lang.RuntimeException: 异常发生

6.9 什么是切面 Aspect?

aspect 由 pointcount 和 advice 组成,切面是通知和切点的结合。 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:

  • 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
  • 如何在 advice 中编写切面代码.

可以简单地认为, 使用 @Aspect 注解的类就是切面.

6.11 什么是基于注解的切面实现?

在这种情况下(基于@AspectJ的实现),涉及到的切面声明的风格与带有java5标注的普通java类一致。

6.12 有几种不同类型的自动代理?

  1. BeanNameAutoProxyCreator
  2. DefaultAdvisorAutoProxyCreator
  3. Metadata autoproxying

感谢您的点赞、评论、关注;
您还可以扫码关注"公众号"获取粉丝福利。









原文转载:http://www.shaoqun.com/a/521204.html

跨境电商:https://www.ikjzd.com/

noon:https://www.ikjzd.com/w/259

自贸区跨境通网站:https://www.ikjzd.com/w/1329


推荐使用小程序阅读为了能让您更加方便的阅读本文所有的面试题目均已整理至小程序《面试手册》可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验!目录推荐使用小程序阅读1.Spring基础1.1什么是spring?1.2Spring框架的设计目标,设计理念,和核心是什么?1.3Spring框架中都用到了哪些设计模式?1.4SpringFramework中有多少个模块,它们分别是什么?1.5spri
e淘网:e淘网
askme:askme
口述:斯文老公爱爱时爆粗口让我冷淡男人爱爱骂脏话:口述:斯文老公爱爱时爆粗口让我冷淡男人爱爱骂脏话
2018年12月亚马逊美国站加湿器(Aromatherapy Diffusers) 品类数据选品报告:2018年12月亚马逊美国站加湿器(Aromatherapy Diffusers) 品类数据选品报告
站外推广:5种提升你网站的流量的​Pinterest技巧!:站外推广:5种提升你网站的流量的​Pinterest技巧!

尼日利亚的另一种疫病,在新冠的阴霾下悄然暴发|非洲|新冠肺炎

  来源:我是科学家iScientist

  提到2020年,很多人最先想到的大概是新冠疫情。的确,新冠肺炎是历史上死亡最多的流行病之一,全球疫情的暴发也很大程度改变了人们的生活轨迹。然而,这世界上其他疾病的传播,并没有因为新冠肺炎而停住脚步。

  2020年11月,南尼日利亚的三角洲与埃努古地区突然集中出现了几例相似的死亡病例。紧接着,当地新增了500多例的疑似病例与170多例死亡,引起人们的不安——尼日利亚五个州暴发了黄热病疫情 。


  不容小觑的热带病

  黄热病(Yellow Fever)是由一种叫黄热病毒的单链RNA病毒引起的急性传染病。之所以叫黄热病,是因为它的发病症状之一是黄疸,除此之外,多数感染者还有发烧、头痛等症状。大约15%的感染者会发展成重症,伴随着内出血、内脏衰竭及呕吐物带血等症状,死亡率高达50% ,并且尚无特效疗法,只能简单补充电解质、进行物理降温,靠病人自身的免疫力渡过难关 。

  黄热病,又是如何传染的呢?它的传染途径与疟疾类似,都是通过蚊虫叮咬传播。大部分黄热病病例来自非洲、美洲和亚洲的热带与亚热带地区,这些地区也是埃及伊蚊(Aedes aegypti)的主要分布地区,而黄热病毒则来源于非洲森林中的灵长类动物。

埃及伊蚊埃及伊蚊

  雌蚊在叮咬受感染的灵长类动物后,血液中的病毒开始复制,并转移到蚊子的唾液腺。当蚊子叮咬人类时,这些病毒便会随着蚊子的唾液一起进入到被叮咬人的血液中,然后继续复制。等人类再一次被蚊子叮咬,就又开始新一轮的传播。

  黄热病分为城市型与丛林型,前者是人传人,后者则是动物传人。正是因为存在着大量感染了黄热病毒的野生灵长动物,至今我们仍然无法彻底根除病毒[1]。通过对黄热病病毒分子的进化分析,科学家们推测,黄热病在大约1500年前于非洲起源,并在300~400年前因奴隶贸易而传入美洲[2]。

黑猩猩易感黄热病毒黑猩猩易感黄热病毒

  较为复杂的防控现状

  在科学的发展下,黄热病疫苗已研发上市多年,价格不贵、安全有效。但是,为什么黄热病在尼日利亚还引起了如此严重的疫情呢?可能大家已经隐隐约约猜到了,是因为当地医疗与经济的不发达。

  黄热病的防控现状是较为复杂的。一方面,疫苗接种在部分国家尚未广泛推行,疫苗覆盖人群的分布存在不均匀性;另一方面,则是多个环境因素的变化,都可能助长黄热病疫情的发展。

  举个例子,南美洲的许多国家,例如巴西的黄热病疫苗覆盖率几乎是百分之百,而许多非洲中部和东部国家的疫苗覆盖率则趋近于零,不同国家接种率存在巨大差别,这主要是因为在非洲,强制性的婴儿接种方案还没被广泛采用[3]。

  黄热疫苗的接种情况尚不乐观黄热疫苗的接种情况尚不乐观

  1940年至1960年间,法国殖民者在非洲推行大规模疫苗接种政策,令当时多个非洲国家的接种覆盖率达到了80%。然而,随着法国的撤退,这个项目也终止了,越来越多的孩子打不上疫苗,感染人群的比例也逐渐上升。1985年至2005年间,非洲的黄热病病例逐年增加,期间甚至造成了三次疫情暴发。

  1980年后期,部分非洲国家其实已经陆续把黄热病加入到了新生儿免疫计划中,然而,只给新生儿打疫苗带来的效果,比大规模接种带来的效果要缓慢很多。2006年,14个非洲国家开始了新一轮的接种计划,颇有成效,减少了黄热病带给政府的财政负担,贝宁共和国等国的疫苗覆盖率已经高达94%[4],这与世界卫生组织、当地政府所做的努力密不可分。

还需继续提高疫苗接种率还需继续提高疫苗接种率

  另一方面来说,许多因素都可能助长黄热病疫情的发展。由于这是一种虫媒传播的疾病,全球气候的变化,尤其是气温和降雨量,都会很大程度影响疾病传播和病毒的进化。

  除了黄热病,地球上还有五百多种虫媒传播疾病影响着公众健康,因此在广泛推行疫苗接种之外,我们还可以做的,就是蚊虫控制。比如减少蚊虫栖息地,使用杀虫药与蚊帐,甚至是采用基因改造的方法来控制携带疾病的蚊虫,从根源上解决问题[5][6]。

 黄热病的防控工作还需继续开展 黄热病的防控工作还需继续开展

  我们从未放弃努力

  新冠肺炎带来的影响,对于原本就不乐观的疾病防控情况而言,无疑是雪上加霜。虽然非洲国家的医疗条件一直在提升,卫生环境也在逐步改善,但是疾病防控资源不足的核心问题,一直没有得到解决。除了新冠,他们还同时经历着埃博拉、霍乱等危险疾病的暴发。

  幸运的是,人们从未放弃努力。就拿这次的新冠疫情来说,非洲多国的反应相当迅速——为了让各项医疗技术能在当地快速推广,医护工作者和科学家们采用了许多创新方法,比如用无人机给通行困难的地区运输检测包,利用合并检测法(Pool Testing)一次性检测多人样品来加快新冠检测速度,还有在当地迅速推广自制洗手液和口罩[7]。

 努力加速医疗服务的推广 | Pixabay 努力加速医疗服务的推广 | Pixabay

  这次在尼日利亚发生的黄热病疫情,也被科学家们视为一个当地测试疫苗运输系统的机会,为之后的新冠疫苗接种提前铺路。虽然非洲地区的医疗资源依旧紧张,但在多方的帮助和他们的努力下,相信一定可以渡过这次难关。

  为了呼吁关注包括黄热病在内等被忽视的热带病(NTD,Neglected Tropical Diseases),世界卫生组织将每年的1月30日定为被忽视的热带病日(World NTD Day),希望肆虐于热带地区的疫情,能够获得更广泛的关注,早日得到更有效的防控。

原文转载:http://tech.shaoqun.com/a/317681.html

跨境电商:https://www.ikjzd.com/

taofenba:https://www.ikjzd.com/w/1725

刘军:https://www.ikjzd.com/w/1835


来源:我是科学家iScientist  提到2020年,很多人最先想到的大概是新冠疫情。的确,新冠肺炎是历史上死亡最多的流行病之一,全球疫情的暴发也很大程度改变了人们的生活轨迹。然而,这世界上其他疾病的传播,并没有因为新冠肺炎而停住脚步。  2020年11月,南尼日利亚的三角洲与埃努古地区突然集中出现了几例相似的死亡病例。紧接着,当地新增了500多例的疑似病例与170多例死亡,引起人们的不安——尼
catch:catch
promoted:promoted
出单全靠PS?秒杀莫名被取消?这届"黑五"卖家经历了什么?:出单全靠PS?秒杀莫名被取消?这届"黑五"卖家经历了什么?
布谷:布谷
亚马逊SP广告竞价调整,背后竟藏了这么多猫腻?:亚马逊SP广告竞价调整,背后竟藏了这么多猫腻?

迎着风口逆流而上,WEBUY或成东南亚版“拼多多”

但是如果卖家把目光放在东南亚的社交电商上,能否擦出不一样的火花?


新加坡的WEBUY就是东南亚典型的社交电子商务平台。当初,ezbuy的前联合创始人Vincent Xue就是受拼多多成功故事的启发,才决定创办WEBUY


WEBUY作为基于团购模式的电商平台,目标是开发以人为本的技术,为以社区为中心的企业和消费者提供优质的餐饮产品


它的工作方式是,几个人可以在平台上注册并相互联系,以联系特定商品的供应商以更低的价格共同议价。


彼此接近的消费者可以集体购买食品和杂货等餐饮产品,在指定地点接收货物消费者可以通过批量购买和较低的交付成本节省资金。


卖家称,这是买卖双方互利共赢的发展模式,因为购物者可以花更少的钱买到同样的商品,而卖家可以通过向购物者出售更多商品而受益,双方也不必担心物流问题。


去年10月份,WEBUY还宣布已在由风险投资公司Wavemaker Partners领投的A轮融资中筹集了600万美元。


加上GFC支持的种子轮融资,WeBuy迄今已筹集了900万美元


其实从WEBUY的扩张步伐也可以看出东南亚消费者对社交电商的欢迎程度。通常,团购模式在价格敏感的消费群体中很受欢迎,而且他们不仅限于中低收入群体。


而且,考虑到越南和印度尼西亚等国家的人口规模和以社区为中心的人口分布特征,可以看出,WEBUY的发展市场不仅限于新加坡,它在东南亚其他国家可能会创造出更大的可能性。


据了解,WEBUY已经计划2021年通过扩展到越南和菲律宾等国家来扩大市场份额以进一步扩大其在东南亚的业务。


除了新加坡的WEBUY东南亚的社交电商市场也引起了不少卖家的注意。数据显示,2019年同期相比,2020年上半年东南亚社交电商订单总额翻了一番多,整个地区的商品总值(GMV)增长了三倍。


不过东南亚产品和服务的电子商务渗透率仍徘徊在10%—15%左右的低水平,该数据在中国则超过40%,从另一个角度来看,东南亚社交电商市场或将有更大的发展空间。


而且,在东南亚市场,社区互动性供应链和物流的融合都将为消费者带来新的购物体验。

 




原文转载:http://www.shaoqun.com/a/521187.html

跨境电商:https://www.ikjzd.com/

鸥鹭:https://www.ikjzd.com/w/2568

出口易:https://www.ikjzd.com/w/1317


但是如果卖家把目光放在东南亚的社交电商上,能否擦出不一样的火花?新加坡的WEBUY就是东南亚典型的社交电子商务平台。当初,ezbuy的前联合创始人VincentXue就是受拼多多成功故事的启发,才决定创办WEBUY。WEBUY作为基于团购模式的电商平台,目标是开发以人为本的技术,为以社区为中心的企业和消费者提供优质的餐饮产品。它的工作方式是,几个人可以在平台上注册并相互联系,以联系特定商品的供应商
kili:kili
克雷格:克雷格
最高50万!2019年商标资助新政大盘点:最高50万!2019年商标资助新政大盘点
Lazada背后的技术力量,抵住每次零点的"血拼洪流"!:Lazada背后的技术力量,抵住每次零点的"血拼洪流"!
亚马逊将面向品牌所有者推出虚拟捆绑商品:亚马逊将面向品牌所有者推出虚拟捆绑商品

Java异常处理(观察常见异常)

一:观察异常

在一上一节我们讲解了常见的异常类型,这次可我们通过代码来观察这些异常是如何出现的。

DEMO:算术异常

 



此时出现的是算术异常。

DEMO:数组越界异常

 



发现了出现异常的之后的代码也执行了

DEMO:类型转换异常

 



DEMO:无属性异常

 



 



这个代码我们使用到了反射的知识,你暂时看见这个异常就行了,不理解没关系,因为反射是我们第四章的内容。

DEMO:无方法异常

 



 



这个代码我们使用到了反射的知识,你暂时看见这个异常就行了,不理解没关系,因为反射是我们第四章的内容。

DEMO:空指向异常

 



 



●访问一个空对象的方法或者属性

●访问了-一个值为null的数组的长度时候会出现该异常

DEMO:数字格式化异常

 



 



这种异常是将一个字符串类型的数据转换一个数字类型的数据时候可能出现该异常。

DEMO:类加载异常

 



 



这个demo也要使用到反射的知识,你暂时不需要完全明白,但是你要知道程序要运行那么加载器要将需要的class加载内存中。

上面的demo中异常在开发中你会经常发现,比如说你要使用到第三方的jar,可能没有下载到就会出现这样的异常。

 

详细的java系统视频免费学习地址

 

有完整的Java初级,高级对应的学习路线和资料!专注于java开发。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式、分布式及面试资料、开源项目,助力开发者成长!

欢迎关注微信公众号:码邦主 









原文转载:http://www.shaoqun.com/a/521181.html

跨境电商:https://www.ikjzd.com/

刘小东:https://www.ikjzd.com/w/1853

鸥鹭:https://www.ikjzd.com/w/2568


一:观察异常在一上一节我们讲解了常见的异常类型,这次可我们通过代码来观察这些异常是如何出现的。DEMO:算术异常此时出现的是算术异常。DEMO:数组越界异常发现了出现异常的之后的代码也执行了DEMO:类型转换异常DEMO:无属性异常这个代码我们使用到了反射的知识,你暂时看见这个异常就行了,不理解没关系,因为反射是我们第四章的内容。DEMO:无方法异常这个代码我们使用到了反射的知识,你暂时看见这个异
parenthood:parenthood
蜜芽:蜜芽
全球最大自贸区!非洲迈向一体化,中国企业的新机遇:全球最大自贸区!非洲迈向一体化,中国企业的新机遇
亚马逊课程:亚马逊课程
口述:老公深爱小三却将她继母压身下小三老公朋友:口述:老公深爱小三却将她继母压身下小三老公朋友

Spring循环依赖三级缓存是否可以减少为二级缓存?

-     前言     -

提问:

我们都知道Spring通过三级缓存来解决循环依赖的问题,那么是不是必须是三级缓存?二级缓存不能解决吗?

要分析是否能够去掉其中一级缓存,我们需要先过一遍Spring是如何通过三级缓存来解决循环依赖的。2021Java面试宝典

 

-     循环依赖     -

所谓的循环依赖,就是两个或者两个以上的bean互相依赖对方,最终形成闭环

比如"A对象依赖B对象,而B对象也依赖A对象",或者"A对象依赖B对象,B对象依赖C对象,C对象依赖A对象";类似以下代码:

public class A {
private B b;
}

public class B {
private A a;
}

常规情况下,会出现以下情况:

1、通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。

2、A对象需要注入B对象,发现对象池(缓存)里还没有B对象(对象在创建并且注入属性和初始化完成之后,会放入对象缓存里)。

3、通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。

4、B对象需要注入A对象,发现对象池里还没有A对象。

5、创建A对象,循环以上步骤。

 

-     三级缓存     -

Spring解决循环依赖的核心思想在于提前曝光:

1、通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。

2、A对象需要注入B对象,发现缓存里还没有B对象,将半成品对象A放入半成品缓存。

3、通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。

4、B对象需要注入A对象,从半成品缓存里取到半成品对象A。

5、B对象继续注入其他属性和初始化,之后将完成品B对象放入完成品缓存。

6、A对象继续注入属性,从完成品缓存中取到完成品B对象并注入。

7、A对象继续注入其他属性和初始化,之后将完成品A对象放入完成品缓存。

其中缓存有三级:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);


/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

 

要了解原理,最好的方法就是阅读源码,从创建Bean的方法AbstractAutowireCapableBeanFactor.doCreateBean入手。

一. 在构造Bean对象之后,将对象提前曝光到缓存中,这时候曝光的对象仅仅是构造完成,还没注入属性和初始化。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// 是否提前曝光
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
……
}
}

 

二. 提前曝光的对象被放入Map<String, ObjectFactory<?>> singletonFactories缓存中,这里并不是直接将Bean放入缓存,而是包装成ObjectFactory对象再放入。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 一级缓存
if (!this.singletonObjects.containsKey(beanName)) {
// 三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
}
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}

 

三. 为什么要包装一层ObjectFactory对象?

如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:

1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。

2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。

Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?

Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects。 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
}

 

为了防止对象在后面的初始化(init)时重复代理,在创建代理时,earlyProxyReferences缓存会记录已代理的对象。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
}

 

四. 注入属性和初始化

提前曝光之后:

1、通过populateBean方法注入属性,在注入其他Bean对象时,会先去缓存里取,如果缓存没有,就创建该对象并注入。

2、通过initializeBean方法初始化对象,包含创建代理。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
……
}
}
// 获取要注入的对象
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}

 

五. 放入已完成创建的单例缓存

在经历了以下步骤之后,最终通过addSingleton方法将最终生成的可用的Bean放入到单例缓存里。

1、

AbstractBeanFactory.doGetBean ->

2、

DefaultSingletonBeanRegistry.getSingleton ->

3、

AbstractAutowireCapableBeanFactory.createBean ->

4、

AbstractAutowireCapableBeanFactory.doCreateBean ->

5、

DefaultSingletonBeanRegistry.addSingleton

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

 

 

-     二级缓存     -

上面第三步《为什么要包装一层ObjectFactory对象?》里讲到有两种选择:

1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。

2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。

Sping选择了第二种,如果是第一种,就会有以下不同的处理逻辑:

1、在提前曝光半成品时,直接执行getEarlyBeanReference创建到代理,并放入到缓存earlySingletonObjects中。

2、有了上一步,那就不需要通过ObjectFactory来延迟执行getEarlyBeanReference,也就不需要singletonFactories这一级缓存。

这种处理方式可行吗?

这里做个试验,对AbstractAutowireCapableBeanFactory做个小改造,在放入三级缓存之后立刻取出并放入二级缓存,这样三级缓存的作用就完全被忽略掉,就相当于只有二级缓存。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// 是否提前曝光
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 立刻从三级缓存取出放入二级缓存
getSingleton(beanName, true);
}
……
}
}

 

测试结果是可以的,并且从源码上分析可以得出两种方式性能是一样的,并不会影响到Sping启动速度。

那为什么Sping不选择二级缓存方式,而是要额外加一层缓存?

如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。

Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。

如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。2021Java面试宝典









原文转载:http://www.shaoqun.com/a/521168.html

跨境电商:https://www.ikjzd.com/

空中云汇:https://www.ikjzd.com/w/2684

barclays:https://www.ikjzd.com/w/2775


-前言-提问:我们都知道Spring通过三级缓存来解决循环依赖的问题,那么是不是必须是三级缓存?二级缓存不能解决吗?要分析是否能够去掉其中一级缓存,我们需要先过一遍Spring是如何通过三级缓存来解决循环依赖的。2021Java面试宝典-循环依赖-所谓的循环依赖,就是两个或者两个以上的bean互相依赖对方,最终形成闭环。比如"A对象依赖B对象,而B对象也依赖A对象",或者"A对象依赖B对象,B对象
新单:新单
败欧洲运费:败欧洲运费
亚马逊或将增设瑞典站!:亚马逊或将增设瑞典站!
海外仓和自贸仓的区别是什么,海外仓和自贸仓哪个好?:海外仓和自贸仓的区别是什么,海外仓和自贸仓哪个好?
亚马逊澳洲站将在悉尼开设第二个配送中心:亚马逊澳洲站将在悉尼开设第二个配送中心

2021年1月29日星期五

抖音数据采集教程,Unicorn 模拟 CPU 执行 JNI_Onload 动态注册

抖音数据采集教程,Unicorn 模拟 CPU 执行 JNI_Onload 动态注册

短视频、直播数据实时采集接口,请查看文档: TiToData


免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。

Unicorn 模拟 JNI_Onload

目标 调用 JNI_OnLoad

JNI_OnLoad 如图


步骤:

先实现 javavm 中的 GetEnv, (与模拟 jni 过程类似)一共八个函数

初始化

# 1. 开始映射 mu.mem_map(0, 0x1000) # 初始化映射 参数1:地址 参数2:空间大小 默认初始化后默认值:0 # 1.1 初始化 java vm 中的每一个函数 java_vm_base = 700*4 # 从 700*4 开始 for i in range(0, 10, 1): # 一共8个函数(5个+3个预留) 这里我预留了10个多写几个预备,也就是 10*4   mu.mem_write(i*4+java_vm_base, b'\x00\xb5\x00\xbd') # 先随便填充,保持堆栈平衡 push {lr} pop {pc} # 1.2 初始化填充 JNIInvokeInterface 结构体 for i in range(0, 10, 1):  mu.mem_write(i*4+java_vm_base+40, struct.pack("I", i*4+java_vm_base+1)) # 注意第二个参数,要 pack 一下为 bytes, 而且是 thmob 指令集都要+1 # 1.3 初始化 Java vm 指针 javavm_pointer=700*4+80 mu.mem_write(javavm_pointer,struct.pack("I",java_vm_base+40)) # 内容指针,页就是 JNIInvokeInterface 的第一个位置所以要加 40

**
然后添加 Hook 代码,模拟cpu 执行

  • 注意: 想要直接通过 R2 的地址读出函数的信息,是有问题的
  • 因为: linker 对加载的时候并不是直接映射的,而是分不通的段进行加载的!!所以位置是不通的。
  • 解决: 要模拟加载 和 重定位
  • 涉及: 依赖库加载,符号的解析等等工作
  • 便捷解决: AndroidNativeEmu 已经封装好了 linker, 并且可以模拟 syscall 的执行,还提供了对函数的 hook 功能

代码如下
tool

import unicornimport capstoneimport structclass Tool: """工具类""" def __init__(self):  self.CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)   def capstone_print(self, code, offset, total=20):  """  code: 代码  offset: 偏移位置  total: 最大打印行  """  for i in self.CP.disasm(code[offset:], 0, total):   print('\033[1;32m地址: 0x%x | 操作码: %s | %s\033[0m'%(offset + i.address, i.mnemonic, i.op_str))  def readstring(self, mu,address):  """读字符串"""  result=''  tmp=mu.mem_read(address,1)  while(tmp[0]!=0):   result=result+chr(tmp[0])   address=address+1   tmp = mu.mem_read(address, 1)  return result  def printArm32Regs(self, mu, end=78):  """打印寄存器"""  for i in range(66, end):   print("\033[1;30m【R%d】, value:%x\033[0m"%(i-66,mu.reg_read(i)))  print("\033[1;30mSP->value:%x\033[0m" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)))  print("\033[1;30mPC->value:%x\033[0m" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_PC)))tl = Tool()if __name__ == "__main__": with open("so/testcalljni.so",'rb') as f:  CODE=f.read()  # tl.capstone_print(CODE, 0x0B58, 10)

**
_ _
core

import unicornimport structimport capstonefrom arm_tool import tldef init_java_vm(mu): """初始化 java vm java vm 5+3 个函数 """ # 1. 开始映射 mu.mem_map(0, 0x1000) # 初始化映射 参数1:地址 参数2:空间大小 默认初始化后默认值:0 """注意:要模拟 JNI_OnLoad 同样也需要先初始化 JNI""" # 0.1 初始化填充 jni 函数 JniFuntionListbase=0x0 for i in range(0, 300): # 接近 300 个jni函数 (指针是 4 个字节)  mu.mem_write(i*4+JniFuntionListbase, b'\x00\xb5\x00\xbd') # 先随便填充,保持堆栈平衡 push {lr} pop {pc} # 0.2 初始化填充 JNINaviteInterface 结构体, 每一项都是,jni函数的地址 # JniNativeInterFace=301 # 前面300个用于指针了,从301个开始 for i in range(300, 600): # 4 个字节都是地址  mu.mem_write(i*4, struct.pack("I", (i-300)*4+1)) # 注意第二个参数,要 pack 一下为 bytes, 而且是 thmob 指令集都要+1 # 0.3 初始化 jnienv 指针 jnienv_pointer = 601*4 mu.mem_write(jnienv_pointer, struct.pack("I", 300*4)) # 内容指针,页就是 JniNativeInterFace 的第一个 300  """初始化 java vm""" # 1.1 初始化 java vm 中的每一个函数 java_vm_base = 700*4 # 从 700*4 开始 for i in range(0, 10, 1): # 一共8个函数(5个+3个预留) 这里我预留了10个多写几个预备,也就是 10*4   mu.mem_write(i*4+java_vm_base, b'\x00\xb5\x00\xbd') # 先随便填充,保持堆栈平衡 push {lr} pop {pc} # 1.2 初始化填充 JNIInvokeInterface 结构体 for i in range(0, 10, 1):  mu.mem_write(i*4+java_vm_base+40, struct.pack("I", i*4+java_vm_base+1)) # 注意第二个参数,要 pack 一下为 bytes, 而且是 thmob 指令集都要+1 # 1.3 初始化 Java vm 指针 javavm_pointer=700*4+80 mu.mem_write(javavm_pointer,struct.pack("I",java_vm_base+40)) # 内容指针,页就是 JNIInvokeInterface 的第一个位置所以要加 40 # 2. 将代码片段映射到模拟器的虚拟地址 ADDRESS = 0x1000 # 映射开始地址  SIZE = 1024*1024*10 # 分配映射大小(多分一点) # 3. 开始映射 mu.mem_map(ADDRESS, SIZE) # 初始化映射 参数1:地址 参数2:空间大小 默认初始化后默认值:0 mu.mem_write(ADDRESS, CODE) # 写入指令 参数1: 写入位置 参数2:写入内容 # 4. 寄存器初始化 函数2个参数 (JNI_OnLoad 有两个参数) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, javavm_pointer) # 参数 javavm 指针 mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x0) # 0 # 5. 初始化堆栈,因为要对内存进行操作 设置 SP SP = ADDRESS+SIZE-16 # 多减点,预留 sp 剩下两个参数的位置 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP,SP) # 6. 添加 hook 代码 """注意: hook 的时候加上区间可以极大的提升hook效率!!""" mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) # mu.hook_add(unicorn.UC_HOOK_MEM_WRITE, hook_mem) # 跟踪 cpu 执行内存操作, 需要自写回调函数 # mu.hook_add(unicorn.UC_HOOK_INTR,hook_syscall) # hook 系统调用函数 # mu.hook_add(unicorn.UC_HOOK_BLOCK,hook_block) # hook 基本块  # 7. 开始运行 add_satrt = ADDRESS+0xc00+1 # 偏移位置 ida 查看 THUMB 指令集奇数所以要 ADDRESS +1,  add_end = ADDRESS+0xC66 # 调用完 registnative 返回即可 try:   mu.emu_start(add_satrt, add_end) # 参数1:起始位置,参数2:结束位置  print('-------- unicorn 执行后--------')  r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0)  print('执行结果: ', tl.readstring(mu, r0value)) except unicorn.UcError as e:  print('\033[1;31mError: %s \033[0m' % e)def hook_code(mu,address,size,user_data): """定义回调函数, 在进入汇编指令之前就会先运行这里 mu: 模拟器 address: 执行地址 size: 汇编指令大小 user_data: 通过 hook_add 添加的参数 """ code=mu.mem_read(address,size) # 读取 if address>=700*4 and address<=710*4:  index=(address-700*4)/4  print('进入 Javavm 函数: '+str(index))  if index==6:   print("调用 javavm->GetEnv---------------:" + str(index))   # jint (*GetEnv)(JavaVM*, void**, jint); 第二个参数才是,返回的值,所以要用 R1 !!!!!   """第二个参数才是,返回的值,所以要用 R1 !!!!!"""   r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) # 将 ENV 指针 写入 jni 第一个参数中,即可   mu.mem_write(r1value,struct.pack("I",601*4)) # 也就是我们初始化 jni 的时候的指针地址  CP=capstone.Cs(capstone.CS_ARCH_ARM,capstone.CS_MODE_THUMB)  for i in CP.disasm(code,0,len(code)):   print("\033[1;32mHook jni | 地址:0x%x | 指令:%s | 内容:%s\033[0m"%(address,i.mnemonic,i.op_str))   tl.printArm32Regs(mu) elif address>=0 and address<=300*4: # 返回属于我们自己写的 jni 函数的区域的时候   index=(address-0)/4 # 拿到第几个 jni 函数  if index==6: # 676/4 6 = FindClass 就可以捕获到,类的完整类名   print("------[jnienv] FindClass-------")   # jclass  (*FindClass)(JNIEnv*, const char*); 是第二个参数返回的值 所以是 R1   r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1)   classname=tl.readstring(mu,r1value)   #666 com/example/unicorncourse05/MainActivity   print("\033[1;33mjnienv FindClass: %s\033[0m" %classname)   mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0,666) # 随便写一个值来代表这个引用!  elif index == 215: # 第二部,调用 注册函数    # jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);   print("------[jnienv] RegisterNatives-------")   r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) # 也就是我们前面写好的 601 * 4 = 2404   r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) # 也就是上面随便写的一个值   r2value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R2) # JNINativeMethod 地址 (数组)   """   注意: 想要直接通过 R2 的地址读出函数的信息,是有问题的   因为: linker 对加载的时候并不是直接映射的,而是分不通的段进行加载的!!所以位置是不通的。   解决: 要模拟加载 和 重定位   涉及: 依赖库加载,符号的解析等等工作   便捷解决: AndroidNativeEmu 已经封装好了 linker, 并且可以模拟 syscall 的执行,还提供了对函数的 hook 功能   """   # funcname_bytearray=mu.mem_read(r2value,4)   # funcname_addr=struct.unpack("I",funcname_bytearray);   # print(tl.readstring(mu,funcname_addr))   r3value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R3)   print("\033[1;33menv地址:"+str(r0value)+" | 函数jclass:"+str(r1value)+" | JNINativeMethod[数组]地址:"+str(r2value)+" | 注册个数:"+str(r3value)+"\033[0m")  CP=capstone.Cs(capstone.CS_ARCH_ARM,capstone.CS_MODE_THUMB)  for i in CP.disasm(code,0,len(code)):   print("\033[1;32mHook jni | 地址:0x%x | 指令:%s | 内容:%s\033[0m"%(address,i.mnemonic,i.op_str)) returndef hook_mem(mu, type, address, size, value, user_data): """ 读和写内存的 mem hook 回调 """ msg = None print('\033[1;32m=== Hook cpu ===\033[0m') if type==unicorn.UC_MEM_WRITE:  msg = """\033[1;32m内存操作 %s 地址: 0x%x | hook_mem 类型: %s| 大小: %s | 值: 0x%x\033[0m"""%('写入',address,type,size,value) if type==unicorn.UC_MEM_READ:  msg = """\033[1;32m内存操作 %s 地址: 0x%x | hook_mem 类型: %s| 大小: %s | 值: 0x%x\033[0m"""%('读取',address,type,size,value) print(msg) returndef hook_syscall(mu,intno,user_data): print("\033[1;36mhook 系统调用 系统调用号: 0x%d"%intno) if intno==2: # 例子 2 是退出  print("系统调用退出!!") # print_result(mu) print("\033[0m") returndef hook_block(mu, address, size, user_data): # code = mu.mem_read(address,size) print("\033[1;36mhook 基本块") # print_result(mu) print("\033[0m") returnif __name__ == "__main__": with open("so/unicorn05.so",'rb') as sofile:  CODE=sofile.read() mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) tl.capstone_print(CODE, 0xc00) init_java_vm(mu) # 初始化 java vm

**
_ _
运行效果









原文转载:http://www.shaoqun.com/a/521160.html

跨境电商:https://www.ikjzd.com/

google correlate:https://www.ikjzd.com/w/1887

paipaiwang:https://www.ikjzd.com/w/2205


抖音数据采集教程,Unicorn模拟CPU执行JNI_Onload动态注册短视频、直播数据实时采集接口,请查看文档:TiToData免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。Unicorn模拟JNI_Onload目标调用JNI_OnLoadJNI_OnLoad如图步骤:先实现javavm中的GetEnv,(与模拟jni过程类似)一共八个函数初始化#1.开始映射mu.mem
孙琦:孙琦
拍拍网服装:拍拍网服装
喜讯_喜讯新闻资讯_喜讯报道:喜讯_喜讯新闻资讯_喜讯报道
Shopee分享跨境卖家故事,有你的影子吗?:Shopee分享跨境卖家故事,有你的影子吗?
亚马逊广告又添新指标 / 亚马逊推出'Robotic Tech Vest':亚马逊广告又添新指标 / 亚马逊推出'Robotic Tech Vest'

《王者荣耀》背后的数据秘密

从找不到需求险些被叫停,到支撑亿级DAU的数据库行业标杆,这款支撑了《王者荣耀》多个系统的腾讯云数据库TcaplusDB在风雨中走过了整整10年。辉映日月破风浪,十年一剑破九天。百万行代码就像淙淙流淌的数据溪流,终于在十年后汇成不可逾越的护城河。

出发

2010年前后,QQ空间很火,带动了基于SNS互动页游(WebSNS)的火爆,腾讯内部开始考虑怎么做页游。也开始建设页游基础技术体系,其中最重要的产出是研发自己的分布式数据库TcaplusDB。与MMOG游戏不同,通常WebSNS游戏是全区全服的,数据集中存储;而其游戏逻辑服务器是对等的,web客户端通过短连接与服务器进行通信,也就是说玩家游戏过程,后端交互的逻辑服务器随时会变动;这些特性导致在逻辑层不方便对用户数据做缓存。

此外,WebSNS游戏基于玩家之间社会关系的互动场景非常多,玩家的一个互动操作,需要读取和修改与此关联的其他多个玩家的数据;这意味着web游戏中数据读写访问频率高于同时在线数,其访问频率实际上是在线数的N倍;假如某个100万在线的web游戏,平均每个玩家有50个好友,如果同时有1%的玩家触发一个好友动作,则可能会触发50万次数据访问。也就是说,基于这样的场景,web游戏的数据库层需要支持比传统MMOG游戏大的多的访问频率,如果仍然采用传统的数据库管理系统(如MySQL),要支持这么大的访问频率,其代价非常大。

因此,在游戏逻辑层和传统数据库管理系统之间架设高速的缓存系统,对web游戏来说至关重要。

当时业界已经有比较多的提供高速数据访问的NoSQL产品,不可否认,这些NoSQL产品在一定场景下使用是很优秀的,但在部分场景或需求下存在不支持或不够出色的情况——比如全内存带来的成本问题或对异步数据读写支持不够等。

2011年起,腾讯内部开始着手研发一款自己的分布式游戏数据库系统——TcaplusDB。这是个很美好的愿景。但从零开始自己研发一款数据库又谈何容易?2009 年开始,大量新的NoSQL数据库涌现,在整个行业掀起了一场NoSQL 革命,如今赫赫有名的 Redis、MongoDB 皆诞生于那一年。研发TcaplusDB是高楼万丈平地起的事情,我们一边调研友商的产品,一边抓住游戏行业特定的场景和需求,做最小化的产品验证。经过不断的验证,第一个版本在2012年初终于出炉。

时机

2012年4月,TcaplusDB首次正式提供服务,支持腾讯自研的页游《夜店之王》。《夜店之王》是腾讯自主研发的一款以时尚夜店为题材,以夜店经营为核心,融入吸血鬼元素的模拟经营类社交游戏。

当时的游戏,大部分是"分区分服"的形式,每个区域内的玩家自己玩。但基于社交场景的WebSNS需要所有的好友在一起玩。《夜店之王》就是这样典型的场景,TcaplusDB的高性能和低成本赢得了项目组的青睐。由于采用了包括 TcaplusDB 数据库在内的新技术,《夜店之王》通过"全区全服",让玩家在一个池子里"大乱斗",并通过实时派对邀请好友体验。《夜店之王》通过QQgame游戏大厅、空间、游戏人生、朋友等诸多渠道进入,取得了相当不错的成绩。

发展过程中,挫折并不是没有,2013年9月腾讯移动游戏《天天酷跑》火爆公测,开启QQ、微信双平台登陆,随时与QQ、微信好友一起玩。上线后,在不到一天的时间内就迅速登上了苹果App Store畅销榜第一位。

当初公测的时候,内部霸气地给《天天酷跑》配了100台服务器,对于一款新游戏,这显然是一个很充裕的配置。然而谁都没想到《天天酷跑》会这么火爆,准备的100台服务器很快就要高容量了。最后,在多部门的协同下,扩容的50台服务器快上架。硬件上架、网络打通、操作系统安装妥当,TcaplusDB的系统软件也很快部署完成。

但接下来就要通过部分数据的搬迁来实现数据库承载能力的扩容。按照正常逻辑,哪怕是停服维护,通常的扩容都有一个提前准备的过程,这次来得实在太快了,眼看旧数据库在逼近存储极限,那边不断涌入的玩家还在不断地制造新数据,搬迁的速度必须足够快,才能避免数据库爆仓。当时在业内深耕多年的大牛苦笑着说:"按照当前的情况,24小时后数据库就会崩溃,游戏只能暂时停服。"

谁都不敢面对这样的结局。

24小时很短,也能做很多事情,一个字,干!

于是,我们和当时酷跑的业务团队一起挺身而出,背水一战,连夜调试搬迁工具,在凌晨紧急上线,硬是抗住了一波又一波海量的用户访问冲击。有了这次的经历,我们下定决心,我们要做得更好。之后的几个月,我们都在重复一件事——一行一行地码代码,看得最多的是星夜、喝得最多的是咖啡,终于把这套数据库的自动扩容系统打磨得稳定高效。

成长

2014年2月,TcaplusDB已经深度优化了存储引擎,推出全托管的分布式存储服务。

但还不够,远远不够。

还记得上面说的《夜店之王》吗?经过2年多的发展,此时的《夜店之王》已经是当时排名第一的经营类社交游戏。而《夜店之王》使用的却还是TcaplusDB的老版本。TcaplusDB的全托管版本,性能得到了较大的提升,在服务上给项目组带来了较大的便利性。因此,将《夜店之王》TcaplusDB切换到全托管版本被提升日程。

由于《夜店之王》是线上业务,且此次切换TcaplusDB不能停服,还得解决新老协议不同、落地数据格式不同、数据分布不同等问题。对于任何一个团队,都是一个很大的挑战。

最终,在跟《夜店之王》团队的联合攻坚之下,我们通过数据双写、增加协议转换层、数据一致性校验工具等工作。最终完美实施了服务的迁移,在这个时期积累的数据迁移方案,也为后续TcaplusDB在无损水平扩展、无损数据迁移能力的产品化打下了坚实的基础。

2015年年底《王者荣耀》正式公测。这款游戏的火爆程度又一次突破了大家的想象力,这个游戏就像一个永远不能满足的巨兽,势不可挡地吞噬着各种后台资源。在打破各种游戏记录的同时,这个逆天的游戏每一秒钟都在创造数据库的新纪录。最疯狂的游戏浪潮,一般出现在周末。回想那个时候,一到周末,我们就在家盯着电脑,看着各种数字疯狂飙升。前期的技术积累终于派上了用场,严阵以待的技术人也终究见证了历史。几千台服务器在预先设定好的统一调度机制下不停服自动扩容,完美扛过流量高峰。同年,TcaplusDB又相继推出故障自动恢复、不停服升级、不停服扩缩容功能,成功支持了《天天爱消除》、《全民飞机大战》、《全民突击》、《CF手游》和《火影忍者手游》等游戏。

2016年,TcaplusDB陆续推出细粒度备份回档、软硬件升级、机房裁撤搬迁、软硬件故障对业务无损等核心能力。同时优化单业务海量数据访问能力,提供稳健高性能的数据存储服务。

2017年元宵晚会,在春晚小品《回家》中,主持人提到了《王者荣耀》,这让团队既紧张又兴奋。接着,又是一路不断飙升的QPS陪伴着我们度过一个不眠之夜,好在,最后又是有惊无险。

同年,TcaplusDB支持protobuf数据格式定义及访问,协议更加开放,兼容行业使用习惯。

2018年,TcaplusDB将高可用、无损扩缩容做到极致,深度优化性能降低成本,提升Api多语言、多平台、多模式、易使用的能力,陆续为《绝地求生-刺激战场》、《绝地求生-全军出击》、《QQ飞车》、《无限法则》等游戏提供高品质数据存储服务。

开放

2019年凭借多年的积累和在游戏分布式系统情景中适配能力,TcaplusDB做为腾讯完全自研的NoSQL数据库,正式成为腾讯云Tcaplus,通过腾讯云对外提供服务。结合了腾讯云的优势后,腾讯云Tcaplus又在多租户、安全性、开放API和多语言SDK等方面有了极大的提升。

2020年NEXON、上海盛趣等游戏厂商开始选择腾讯云TcaplusDB做为游戏的核心数据库支撑业务。根据客户的测试与使用反馈,TcaplusDB的毫秒级时延、千万级QPS、无限水平扩展无需分库分表、细粒度回档、合服和无损弹性变配等能力能够实实在在的帮助到游戏业务。

腾讯云TcaplusDB的核心能力得到了客户的认可的同时,不得不提一提我们强大的迁移服务。当时上海盛趣使用的是DynamoDB,上海盛趣的技术团队需要解决开发者不熟悉腾讯云TcaplusDB的用法和已有代码迁移的问题。通过沟通,腾讯云TcaplusDB的接口的易用性得到了客户的充分认可,重点转移到了已有代码如何迁移到腾讯云TcaplusDB 接口上。经过双方技术团队的沟通,上海盛趣的架构中有一个数据访问适配层,所有的数据库访问均通过该适配层,问题一下变得清晰和简单了起来。腾讯云派出技术专家驻场,仅仅用了两周时间就和上海盛趣的研发团队一起完成了代码迁移的工作。至此,TcaplusDB开始全面为公有云客户提供服务。

2020年12月14日,中国信息通信研究院官方权威公布第十一批大数据产品能力评测结果,腾讯云TcaplusDB成为首批通过键值型内存数据库功能评测的分布式NoSQL数据库产品。

2021年1月,TcaplusDB社区上线(地址https://tcaplusdb.tencent.com/),这标志着TcaplusDB将通过和广大的用户及行业合作伙伴进行交流和共建,持续推动国产分布式NoSQL数据库生态的繁荣。

企业级定制的内核下一个十年:为更多行业助力

自诞生以来,腾讯云TcaplusDB就以服务更多开发者为目标,面向拥有使用高性能数据库的研发人员,分享经过腾讯内部检验的存储研发经验、工具和行业资源。而在未来,腾讯云TcaplusDB还将以国产数据库领航者的身份,在这条道路上走得更远,根据行业动态为平台引入更多元化的功能。同时,腾讯云TcaplusDB将和行业合作伙伴一起,继续分享腾讯分布式数据库方面的经验,并将积极投入基于多模和多负载能力的一站式低成本数据处理能力的研发;满足基于全球分布式能力,助力企业解决业务出海、全球同服/多活、跨域数据迁移等关键业务领域需求。

在下一个十年,诞生于游戏的TcaplusDB,还将继续为更多行业优化数据服务能力,贡献自己的力量。

什么数据库能抗住《王者荣耀》的1亿DAU?

带妹上分,团战五杀,光有技术可不行

本文由博客一文多发平台 OpenWrite 发布!









原文转载:http://www.shaoqun.com/a/521158.html

跨境电商:https://www.ikjzd.com/

名人堂是什么:https://www.ikjzd.com/w/1082

灰色清关:https://www.ikjzd.com/w/1409


从找不到需求险些被叫停,到支撑亿级DAU的数据库行业标杆,这款支撑了《王者荣耀》多个系统的腾讯云数据库TcaplusDB在风雨中走过了整整10年。辉映日月破风浪,十年一剑破九天。百万行代码就像淙淙流淌的数据溪流,终于在十年后汇成不可逾越的护城河。出发2010年前后,QQ空间很火,带动了基于SNS互动页游(WebSNS)的火爆,腾讯内部开始考虑怎么做页游。也开始建设页游基础技术体系,其中最重要的产出
jpgoodbuy:jpgoodbuy
grab:grab
reddit:reddit
评分常规算法:评分常规算法
Wish再发误导性产品政策提醒!(附常见问题解答):Wish再发误导性产品政策提醒!(附常见问题解答)