精华内容
下载资源
问答
  • 当你的才华撑不起的野心的时候当你能力还驾驭不了的目标时候就踏踏实实的静下心来吧,多学习,多历练。身处高不能成、低不想就的尴尬境地,有太多的不甘心,那就赶紧化不甘心为动力,坚持做好下面...

     

    当你的才华还撑不起你的野心的时候,当你的能力还驾驭不了你的目标时候,你就踏踏实实的静下心来吧,多学习,多历练。身处高不能成、低不想就的尴尬境地,你有太多的不甘心,那就赶紧化不甘心为动力,坚持做好下面这些事儿,你的能力就足够支撑起你的"野心"啦!

    鲁采鲁采

    1、知识就是力量:花更多的时间学习

    能力不够,那就勤奋学习来弥补!不管是读对自己有启发的书,还是看一些用于增长知识的专业类书籍,或者什么方面的都可以,相比于只是静静地等待时间的经过,选择主动行动努力前进会更好。

    这个时代,稍微有点儿上进心的人都在奋起直追、力争上游,你要知道:不断学习并且不放弃,一直是成功人士的终身诺言。

    2、做喜欢的事情:干自己热爱的工作

    有人常常抱怨现在的工作不是自己喜欢干的,找不到乐趣。确实,一个人凭着责任心,虽然可以把自己喜欢的工作干好,但是总会觉没意思;但是若是干自己爱好的事,再加上责任心这副强有力的翅膀,就会干得更加出色,甚至是事半功倍。

    石油大王洛克菲勒说:"如果你视工作为一种乐趣,人生就天堂。如果你视工作为一种义务,人生就是地狱。"可不是嘛,你待工作如初恋,工作哪能舍得虐你呢!

     

    鲁采鲁采

    3、选择正确的人生方向:明确目标

    美国一个研究机构,曾经做过一项关于"成功"的调查:长期追踪100个年轻人,直到他们年满65岁,结果发现,只有一个人很富有,其中5个人有经济保障,剩下94个人情况都不太好,晚年生活拮据,可以算是失败者。研究结果显示:这94人之所以晚年拮据,并非年轻时不够努力,主要是因为没有选定清晰的目标。

    知道了吧!有一个明确的目标是多么重要。没有明确目标的人,总是在为有清晰目标的人打工。而你,想成为哪一类人呢?

    4、珍惜时间:做好时间管理

    这年头,生活节奏越来越快,我们都在和时间赛跑。每个人看起来都很忙,忙的身心俱疲,忙的不知所终,但是你是瞎忙还是真有正经事?怎样管理和安排好自己的时间,绝对是个脑力活!

    要高效工作慢节奏生活,果断摒弃处女座模式,把每天24小时碎片化成N多个五分钟,你会发现这些个五分钟你能做的事好多事儿啊!

    进步来得太快了,你可不要晕哦!

    鲁采鲁采

     

    5、做受欢迎的人: 以人格魅力取胜

    你身边一定有这样的人,他能口吐莲花、妙笔生花,你和他相处时间长了会对他产生认同感、信服和崇拜,和他相处不用处处提防,不怕穿小鞋。他是团队里的主心骨,家庭里的顶梁柱。

    曾经创下美国收视率最高的脱口秀节目《欧普拉脱口秀》的主持人欧普拉温弗瑞,一个白人社会中的黑人女性,她的名字传遍全球,成为当今世界上影响力最大的妇女之一,也是美国历史上第一位黑人女亿万富翁。但让人们认识她、了解她的主要原因,却不是她的财富和名声,而是她无可比拟的人格魅力和她给世人不断的惊喜。

    6、你比想象中优秀:潜能开发

    哈佛毕业生的一个共同的特点,就是都有着非凡的心态和自信!"世界最优秀的人才是我们!"、"我能成为世界上最大、最好的公司的CEO!"这种自信,成为哈佛的宝贵财富,造就了一批又一批政治家、科学家和工商管理精英。

    人的潜能是无限的,我们不可能都到哈佛受到良好的教育,但我们却能保持和哈佛毕业生一样的心态。So,给你一个支点,你要相信你没准真的可以翘起地球的!

    鲁采鲁采

     

    7、在团队中成长:协作与沟通很重要

    狮子和老虎之间爆发了一场激烈的战争,到了最后,两败俱伤。狮子快要断气的时候对老虎说:"如果不是你非要抢我的底盘,我们也不会弄成这样。"老虎吃惊地说:"我从未想过抢你的地盘,我一直以为是你想要侵略我。"

    尔虞我诈的职场,相互沟通与交流是维系团队和谐的关键因素。有什么话不要憋在肚子里,多与同事、员工交流,也让同事、员工多了解自己,这样可以避免很多无谓的误会与矛盾。

    8、把路越走越宽:扩大你的人际圈子

    没有交际能力的人,就像陆地上的船,永远到不了人生的大海。人际关系是每个社会人都无法逃避的问题。人际关系的修炼,其实就是人际圈子的经营。

    在职场打拼的我们有三个人际圈子必须要经营:一是领导的圈子;二是所属团队的圈子;三是同僚的圈子,这三个圈子经营得不好,都可能给我们带来灭顶之灾。

     

    当你的才华还撑不起你的野心的时候,你就应该静下心来,学习;

    当你的能力还驾驭不了你的目标时,就应该沉下心来,历练;

    梦想,不是浮躁,而是沉淀和积累,

    只有拼出来的美丽,没有等出来的辉煌,

    机会永远是留给最渴望的那个人,

    学会与内心深处的你对话,

    问问自己,想要怎样的人生,

    静心学习,耐心沉淀,送给自己,共勉。

    山东采风文化传媒有限公司,擅长留学文书写作,帮助上千学子成功拿到offer。鲁采代写网,以留学文书为根基,发展了各类中英文业务。欢迎垂询。

    展开全文
  • 轻松学,听说你还没有搞懂 Dagger2

    万次阅读 多人点赞 2017-07-20 22:14:00
    Dagger2 确实比较难学,我想每个开发者学习的时候总是经历...Dagger2 是有门槛的,这样不同水平能力的开发者去学习这一块的时候,感受到的压力是不一样的。 我个人总结了大家在学习 Dagger2 时,为什么感觉难于理...

    Dagger2 确实比较难学,我想每个开发者学习的时候总是经历了一番痛苦的挣扎过程,于是就有了所谓的从入门到放弃之类的玩笑,当然不排除基础好的同学能够一眼看穿。本文的目的尝试用比较容易理解的角度去解释 Dagger2 这样东西。

    Dagger2 是有门槛的,这样不同水平能力的开发者去学习这一块的时候,感受到的压力是不一样的。

    我个人总结了大家在学习 Dagger2 时,为什么感觉难于理解的一些原因。

    1. 对于 Java 注解内容不熟悉。
    2. 对于依赖注入手段不熟悉。
    3. 对于 Java 反射不熟悉。
    4. 对于 Dagger2 与其它开源库的使用方法的不同之处,没有一个感性的认知。
    5. 对于 Dagger2 中极个别的概念理解不够。
    6. 对于 Dagger2 的用途与意义心生迷惑。

    其实以上几点,都可以归类到基础技能不扎实这个范畴内,但正如我所说的,学习 Dagger2 时开发者的水平是不一样的,所以困扰他们的原因就不一样。下面,我针对这些情况,一一给出自己的建议。

    对于 Java 注解不熟悉

    这一部分的开发者基础知识确实薄弱,那么怎么办呢?当然是学习了。就算不为 Dagger2,注解的知识内容也应该好好值得学习,虽然在平常开发中,我们自己编写注解的机会很少,但是我们运用第三方开源库的时候,应该会经常看见注解的身影,所以熟悉注解不是为了自己编写注解代码,而是为了开发过程中更加高效从容而已。

    如果,对 Java 注解一无所知,我可以给大家一个感性的认知。

    一般,我们评价某人会说,这是一个好人、坏人、男神、女神、大神、单身狗等等,这是我们人为贴得标签,这些标签有助于我们自己或者其他人去获取被评价的人的基本信息。

    而在 Java 软件开发中,我们也可以给某些类,某些字段贴上作用类似的标签,这种标签的名字就叫做注解,只不过这种标签是给代码看的。

    这里写图片描述

    标签只对特定的人起作用,比如小张被人贴了一个小气鬼的标签,所以小红认为小张是一个小气鬼,但是小张本人不会因为这个标签而改变自己变得不是小张,也许本质上小张是个大方的人。

    所以,注解本身也不会影响代码本身的运行,它只会针对特定的代码起到一定的用处,用来处理注解的代码被称作 APT(Annotation Processing Tool)。

    更详细的内容请阅读这篇文章《秒懂,Java 注解 (Annotation)你可以这样学》

    对依赖注入手段不熟悉

    这一块而言,如果让很多人慌张的原因,我觉得可能是依赖注入这个词过于学术化了。而从小到大,10 多年的应试教育让绝大部分的同学对于这些枯燥无味的概念产生了恐惧与绝望。其实,没有那么夸张的,不要被这些东西吓倒。

    因为,Java 学习的时候,我们一直写这样的代码。

    class B{}
    
    
    class A {
        B b;
    
        public A() {
            b = new B();
        }
    }
    

    这样的代码,一点问题都没有,类 A 中有一个成员变量 b,b 的类型是类 B。所以,在软件开发中,可以称 A 依赖 B,B 是 A 的依赖,显然,A 可以依赖很多东西,B 也可以依赖很多东西。

    通俗地讲,依赖这个概念也没有什么神奇的,只是描述了一种需求关系。

    我们再来看一种情况,现在,业务需要,代码越来越复杂。

    class B{}
    
    class C{
        int d;
        public C (int value) {
            this.d = value;
        }
    }
    
    
    class A {
        B b;
        C c;
    
        public A() {
            b = new B();
            c = new C(3);
        }
    }
    

    现在,A 有了一个新的依赖 C。不过,由于业务的演进,C 这个类经常发生变化,最明显的变化就是它的构造方法经常变动。

    class C{
        int d;
        String e;
        public C (String value) {
            this.e = value;
        }
    }
    
    
    class A {
        B b;
        C c;
    
        public A() {
            b = new B();
            //c = new C(3);
            c = new C("hello");
        }
    }
    

    C 变动的时候,由于 A 依赖于它,A 不得不修改自己的代码。但是,事情还没有完。C 还会变动,C 把 B 也带坏了节奏。

    class B{
        int value;
    
        public B(int value) {
            this.value = value;
        }
    
    }
    
    class C{
        int d;
        String e;
        public C (int index,String value) {
            this.d = index;
            this.e = value;
        }
    }
    
    
    class A {
        B b;
        C c;
    
        public A() {
            b = new B(110);
    //      b = new B();
            //c = new C(3);
    //      c = new C("hello");
            c = new C(12,"hello");
        }
    }
    

    可以想像的是,只要 B 或者 C 变动一次,A 就可能需要修改自己的代码,用专业术语描绘就是A 与依赖模块太过于耦合,这个可是犯了软件设计的大罪,

    我们再可以想像一下,A 是领导,B 和 C 是小兵,如果因为 B 和 C 自身的原因,导致领导 A 一次次地改变自己,那么以现在流行的话来说就是,“你良心不会痛吗?”。所以我们需要的就是进行一些变化来进行解耦,也就是解除这种耦合的关系。让 A 不再关心 B 和 C 的变化,而只要关心自身就好了。

    class A {
        B b;
        C c;
    
        public A(B b, C c) {
            this.b = b;
            this.c = c;
        }
    
    }  
    

    在上面代码中,A 不再直接创建 B 与 C,它把依赖的实例的权力移交到了外部,所以无论 B 和 C 怎么变化,都不再影响 A 了。这种实例化依赖的权力移交模式被称为控制反转(IoC),而这种通过将依赖从构造方法中传入的手段就是被传的神乎其乎的依赖注入(DI)。其实,本质上也没有什么神奇的地方,只是起了一个高大上的名字而已,好比东北的马丽,在国际化上的大舞台,宣称自己是来自神秘东方的 Marry 一样。

    依赖注入有 3 种表现形式。
    构造方法注入

    class A {
        B b;
        C c;
    
        public A(B b, C c) {
            this.b = b;
            this.c = c;
        }
    
    } 

    Setter 注入

    class A {
        B b;
        C c;
    
    
        public void setB(B b) {
            this.b = b;
        }
    
    
        public void setC(C c) {
            this.c = c;
        }
    
    }
    

    接口注入

    interface Setter {
        void setB(B b);
        void setC(C c);
    }
    
    
    class A implements Setter{
        B b;
        C c;
    
        public void setB(B b) {
            this.b = b;
        }
    
    
        public void setC(C c) {
            this.c = c;
        }
    
    }
    

    大家肯定会想,依赖注入的引进,使得需求方不需要实例化依赖,但总得有地方去实例化这些依赖啊。确实,依赖注入引进了第三方,你可以称它为 IoC 容器,也可以称它为注射器(injector),为了便于理解,我们之后都有注射器来指代吧,通过注射器可以将依赖以上面 3 种注入方式之一注入到需求方。

    这里写图片描述

    病人需要的是药水,所以病人是需求者,药水是病人的依赖,注射器把药水注射给病人。

    更多细节,请阅读《轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)》

    而 Dagger2 就是一个依赖注入框架,你也可以想像它是一位非常智能化的服务员,用来处理大量的顾客的各种订餐需求,然后针对不同的菜单提供给不同的顾客不同类型的餐具。

    对于 Java 反射不熟悉

    对于这一块不熟悉的同学同样是基础知识太薄弱,需要补强。

    相对于正常流程开发,Java 反射是非常规化手段。如果正常流程开发是司机驾驶一辆汽车,那么反射的运用就是采用无人驾驶的手段。

    Dagger2 中也应用了反射,不过开发者本身不需要运用反射,Dagger2 是自身框架通过反射处理注解。

    学习反射内容可以阅读这篇文章《细说反射,Java 和 Android 开发者必须跨越的坎》

    Dagger2 与其它开源库略有不同

    开源软件的出现,大大造福了程序员,所以,大家都说不要重复创造轮子

    但是,我个人一直认为,不重复创造轮子,不代表可以不去深入了解这些轮子。

    我把 Android 开发中所应用到的开源库当作武装

    武装与两部分构成,武器装备

    那么,在 Android 中什么样的库可以当作是武器呢?什么样的库可以当作是装备呢?

    大家想一下,武器什么用途?战斗进行中,用来杀敌的

    装备呢?战斗开始时,就要穿上或者安装好的物件。
    这里写图片描述

    刀、枪、棍、棒是武器,盔甲是装备。
    武器拿来就用,盔甲等却要在开始战斗前就装备上。

    Java 软件代码是在虚拟机中运行的,所以在这里可以把 jvm 当作战场。

    Piccso、Logger、sweet-alert-dialog 等等,这些开源库都是在程序运行过程中拿来就用的。

    而 GreenDao、Butterknife、Dagger2 这些因为涉及到了反射处理,而反射处理相对于正常开发速度很慢,所以它们通常在编译时产生一些新的代码,然后才能在程序运行过程中使用,也就是说它们都把反射处理移动到编译器编译代码时的阶段,而程序运行时并不涉及到反射,这就是这些框架运用了反射技术,但是仍然高效的秘诀所在。

    所以,Dagger2 会产生中间代码,不少同学应该会有迷惑,为什么引进了 Dagger2 时,要先编译一次代码,不然就会报错。现在,可以解释了,编译代码是为了生成中间代码,然后在中间代码的基础上按照正常的流程开发。

    Dagger2 并非横空出世

    都说要站在巨人的肩膀上,Dagger2 其实也算站在巨人的肩膀上。

    Dagger2 是一款依赖注入的框架,但依赖注入的框架有 ,所以 Dagger2 也并不算是一款新鲜事物,大家觉得新奇不过是因为对于依赖注入框架本身了解过少罢了。

    Dagger2 是在 Dagger 的基础上来的,Dagger 是由 Square 公司开发的,Dagger2 基于 Dagger 由 Google 公司开发并维护。
    Square 是一家伟大的公司,Android 大神 JakeWoton 之前就在它任职,不久前才离职。而我们熟悉的 RxJava、Butterknife、Retrofit、OKHttp 等等都是 Square 提供的,外号 Square 全家桶。
    这里写图片描述

    当然,Google 公司更是一家伟大的公司,这个无需多言。

    最后,有个重要的地方就是 Dagger2 是基于注解开发的,而 Dagger2 中所涉及到的注解其实是基于 javax.inject 上开发的,它出自 JSR330
    这里写图片描述

    JSR330 是规范,建议大家怎么做,而 Dagger2 则实现了这个规范。

    因此,对于普通开发者而言,学习 Dagger2 其实就是学习相关的注解的意义与用途。

    Dagger2 的引进

    Dagger2 是适应于 Java 和 Android 开发的依赖注入框架,记住得是它不仅仅对 Android 开发有效。

    Dagger2 官网地址是 https://google.github.io/dagger//

    对于 Eclipse 开发而言,需要下载相应的 jar 包。

    对于 AndroidStudio 开发而言,只需要在相应的 build.gradle 引入对应的依赖就好了。

    如果你 AndroidStudio 的 gradle build tool 版本在 2.2 以上,直接在引进就好了

    dependencies {
      compile 'com.google.dagger:dagger:2.4'
      annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
    }

    如果你的 gradle build tool 版本在 2.2 以下,则需要引进 apt 插件。
    首先需要在 Project 层级的 build.gradle 文件中引入依赖

    buildscript {
        repositories {
          mavenCentral()
        }
        dependencies {
            // replace with the current version of the Android plugin
            classpath 'com.android.tools.build:gradle:2.1.0'
            // the latest version of the android-apt plugin
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }
    }
    

    然后在 Module 层级的 build.gradle 引入相应的插件和依赖

    apply plugin: 'com.android.application'
    apply plugin: 'com.neenbedankt.android-apt'
    
    
    dependencies {
         apt 'com.squareup.dagger:dagger-compiler:2.4'
         compile 'com.squareup.dagger:dagger:2.4'
         //java注解
         compile 'org.glassfish:javax.annotation:10.0-b28'
    }
    

    Dagger2 的基本概念

    前面讲到过 Dagger2 基于 JSR330 注解,在普通开发者视角中,就是这些注解构成了 Dagger2 的全部。

    前面文章我提到过,注解如同标签,给一个人贴标签有助于自己去理解这个人,而给代码贴标签,有助于 APT 程序去处理相应的代码,Dagger2 有自己的注解,而这些注解也有特定的意义,它们大体上都是为了实现依赖注入。

    我们说依赖注入有 3 种手段:
    - 构造方法注入
    - Setter 注入
    - 接口注入

    但是,如果不借助于框架的话,我们就必须自己编写相应的代码,这些代码充当了注射器的角色。

    B b = new B(5);
    C c = new C(110,"110");
    
    A a = new A();
    a.setB(b);
    a.setC(c);
    

    A 将内部的依赖 B 和 C 的实例化的权力移交到了外部,通过外部的注入。

    Dagger2 这类依赖注入框架的出现进一步解放了我们的双手,Dagger2 有一套自己的依赖注入机制,我们不再手动编写注射器,而只要按照规则配置好相应的代码就好了,Dagger2 会自动帮我们生成注射器,然后在适当的时候进行依赖注入。

    什么意思呢?意思就是我们不需要调用 a.setB() 和 a.setC() 方法,只需对代码添加一些注解就好了。

    class B{
        int value;
        @Inject
        public B(int value) {
            this.value = value;
        }
    
    }
    
    class C{
        int d;
        String e;
        @Inject
        public C (int index,String value) {
            this.d = index;
            this.e = value;
        }
    }
    
    class A {
        @Inject
        B b;
        @Inject
        C c;
    }
    

    看起来,不可思议,不是吗?@Inject 是一个注解,只要按照 Dagger2 的配置,就能颠覆我们之前的编码习惯。

    但不管看起来怎么神奇,任何事都有一个本质。

    因此这个本质就是,Dagger2 是一个依赖注入框架,依赖注入的目的就是为了给需求方在合适的时候注入依赖。

    对 Dagger2 学习过程如果感到不适与难以理解,回过头来想想它的本质好了。

    这里写图片描述
    Dagger2 的使命就是为了给需求者注射依赖。

    @Inject 注解就如同一个标签,或者说它是一个记号,它是给 Dagger2 看的。它运用的地方有两处。

    1. @Inject 给一个类的相应的属性做标记时,说明了它是一个依赖需求方,需要一些依赖。

    2. @Inject 给一个类的构造方法进行注解时,表明了它能提供依赖的能力。

    就这样,通过 @Inject 注解符号,就很容易标记依赖和它的需求方。但是,单单一个 @Inject 是不能让 Dagger2 正常运行的。还需要另外一个注解配合。这个注解就是 @Component。

    而 @Component 相当于联系纽带,将 @inject 标记的需求方和依赖绑定起来,并建立了联系,而 Dagger2 在编译代码时会依靠这种关系来进行对应的依赖注入。

    @Inject 和 @Component

    我们来编写代码,验证一下。

    假设有这么一个场景:

    一个宅男,他喜欢在家玩游戏,所以饿了的时候,他不想自己煮饭吃,也不愿意下楼去餐厅,他选择了外卖。

    public class ZhaiNan {
    
        @Inject
        Baozi baozi;
    
        @Inject
        Noodle noodle;
    
        @Inject
        public ZhaiNan() {
    
        }
    
        public String eat() {
            StringBuilder sb = new StringBuilder();
            sb.append("我吃的是 ");
            if ( baozi != null ) {
                sb.append(baozi.toString());
            }
    
            if (noodle != null) {
                sb.append("  ");
                sb.append(noodle.toString());
            }
            return sb.toString();
        }
    }
    
    public class Baozi {
    
        @Inject
        public Baozi() {
        }
    
        @Override
        public String toString() {
            return "小笼包";
        }
    }
    
    public class Noodle {
    
        @Inject
        public Noodle() {
        }
    
        @Override
        public String toString() {
            return "面条";
        }
    }
    

    上面代码可以看到,@Inject 注解的身影,需求方是 ZhaiNan 这个类,而 Baozi 和 Noodle 是它的依赖。前面说过,光有 @Inject 的话还不行,需要 @Component 配合。

    @Component 怎么使用呢?

    很简单,它只需要注解在一个接口上就好了。

    @Component()
    public interface Platform {
        ZhaiNan waimai();
    }

    Platform 是一个接口,它代表着外卖平台,它内部有一个 waimai() 的方法,返回 ZhaiNan 的类型。

    这个接口特别的地方就是它的方法中的返回类型。如果一个方法返回了一个类型,那么其实也算是一种依赖的提供,我们可以在后续的代码中感受。

    既然是接口,那么它就需要实现类,但是 Dagger2 会自动帮我们生成一个实现类,前提是使用这个类的时候,要先对工程进行编译。前面用装备解释过 Dagger2 这种类型的库,它会在编译阶段产生中间代码,这些中间代码就包括自动实现了被 @Component 注解过的接口实现类。

    所以,我们如果要使用 Dagger2 为了我们自动生成的类时,我们就应该先 Build->Make Project 编译一次代码。生成的代码位置在 app 模块 build 文件夹中,在 AndroidStudio 切换 Project 视角就可以看到。
    这里写图片描述

    这个目录下都是 Dagger2 产生的中间产物,DaggerPlatform 就是 Dagger2 为我们自动实现的 Platform 这个接口的实现类,注意它的名字都是 Dagger+接口名称

    有了 DaggerPlatform,我们就能够使用 Dagger2 进行代码的依赖注入了。

    public class MainActivity extends AppCompatActivity {
    
        Button mButton;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mButton = (Button) findViewById(R.id.btn_test);
    
            final ZhaiNan zainan = DaggerPlatform.builder()
                    .build()
                    .waimai();
    
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this,zainan.eat(),Toast.LENGTH_LONG).show();
                }
            });
    
        }
    }
    

    然后,测试效果是:
    这里写图片描述

    需要注意的地方是,Component 的实现类是由 Dagger2 自动生成的,它的名字前面说了是 Dagger+接口名称。但这是通常情况,因为 @Component 注解的都是顶级类。但还有一种情况是。

    class Foo {
      static class Bar {
        @Component
        interface BazComponent {}
      }
    }
    

    它只是一个内部类的接口,Dagger2 针对这种情况需要把外部的类的名字加下划线的形式拼接起来,所以上例中 Dagger2 生成的 Component 实现类类名是 DaggerFoo_Bar_BazComponent。

    我们并没有在任何地方用 new 关键字亲自创建 ZhaiNan 这个类的实例,但是它确实有效,而且它的内部依赖 Baozi 和 Noodle 都被实例化了,也就是说依赖被正确地注入到了 ZhaiNan 的实例对象当中。

    所以,@Component 和 @Inject 的配合就能够使用 Dagger2 了,但这里面存在一个局限,@Inject 只能标记在我们自己编写的类的构造方法中,如果我们使用第三方的库或者标准库的话,是不是代表我们对于这些就无能为力了呢?

    答案显然是否定的,Dagger2 作为一款优秀的框架必须考虑到开发过程中的方方面面,不然谈何优秀呢?

    Dagger2 为了能够对第三方库中的类进行依赖注入,提供了 @Provides 和 @Module 两个注解。

    @Provides 和 @Module

    Provide 本身的字面意思就是提供,显然在 Dagger2 中它的作用就是提供依赖。
    Module 是模块的意思,Dagger2 中规定,用 @Provides 注解的依赖必须存在一个用 @Module 注解的类中。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
        @Provides
         public Noodle provideNoodle() {
            return new Noodle();
        }
    }
    
    public class Baozi {
    
        String name;
    
        @Inject
        public Baozi() {
            name = "小笼包";
        }
    
        public Baozi(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return name;
        }
    }
    

    值得注意的地方有
    - @Provides 修饰的方法一般用 provide 作用方法名前缀。
    - @Module 修饰的类一般用 Module 作为后缀。

    前面有讲过,@Component 是依赖双方的联系纽带,现在多了一个 @Module 注解,怎么配合使用呢?方法,很简单。只要在 @component 注解后面的括号中取值就是。

    @Component(modules = ShangjiaAModule.class)
    public interface WaimaiPingTai {
        ZhaiNan waimai();
    }
    

    然后编写测试代码

    mBtnTestModule = (Button) findViewById(R.id.btn_test_module);
    
    final ZhaiNan zainan1 = DaggerWaimaiPingTai.builder()
            .build()
            .waimai();
    
    mBtnTestModule.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,zainan1.eat(),Toast.LENGTH_LONG).show();
        }
    });
    

    演示效果如下:
    这里写图片描述

    我们再看看 @Provides 用法。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
        @Provides
         public Noodle provideNoodle() {
            return new Noodle();
        }
    }
    

    @Provides 注解的方法中直接用 new 创建了依赖,其实还有另外一种方式。我们先对 Noodle 进行重构,让它作为面条的基类,然后编写它一个继承类。

    public  class Noodle {
        @Inject
        public Noodle() {
        }
    }
    
    public class Tongyi extends Noodle{
    
        @Inject
        public Tongyi() {
        }
    
        @Override
        public String toString() {
            return "统一方便面";
        }
    }
    

    ZhanNan 这个类不用改变,然后,用另外一种方式编写 @Provides 注解的方法。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
        @Provides
         public Noodle provideNoodle(Tongyi noodle) {
            return noodle;
        }
    }
    

    测试代码也不需要更改,演示效果如下:
    这里写图片描述
    那么,两种方式有什么区别呢?

    @Provides
    public Noodle provideNoodle(Tongyi noodle) {
        return noodle;
    }
    
    @Provides
    public Noodle provideNoodle(Tongyi noodle) {
        return noodle;
    }
    

    什么时候用 new 关键字?什么时候直接返回传入进来的参数?
    我们不妨再创建一个类 Kangshifu,同样继承自 Noodle 这个基类。

    public class Kangshifu extends Noodle{
    
        public Kangshifu() {
        }
    
        @Override
        public String toString() {
            return "康师傅方便面";
        }
    }
    

    与 Tongyi 这个类不同的地方是,它并没有用 @Inject 注解构造方法。
    我们再尝试更改 @Provides 注解的相应方法。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
        @Provides
         public Noodle provideNoodle(Kangshifu noodle) {
            return noodle;
        }
    //    @Provides
    //     public Noodle provideNoodle(Tongyi noodle) {
    //        return noodle;
    //    }
    }
    

    再进行编译的时候,会发现 IDE 报错了。

    Error:(10, 13) 错误: com.frank.dagger2demo.Kangshifu cannot be provided without an @Inject constructor or from an @Provides-annotated method.
    com.frank.dagger2demo.Kangshifu is injected at
    com.frank.dagger2demo.ShangjiaAModule.provideNoodle(noodle)
    com.frank.dagger2demo.Noodle is injected at
    com.frank.dagger2demo.ZhaiNan.noodle
    com.frank.dagger2demo.ZhaiNan is provided at
    com.frank.dagger2demo.WaimaiPingTai.waimai()
    

    Log 提示的错误信息是 Kangshifu 这个类代码中没有被 @Inject 注解过的构造方法,也没有办法从一个被 @Provides 注解过的方法中获取。

    所以,什么时候用 new 创建对象,什么时候可以直接返回传入的参数就很明显了。对于被 @Inject 注解过构造方法或者在一个 Module 中的被 @Provides 注解的方法提供了依赖时,就可以直接返回传入的参数,而第三方的库或者 SDK 自带的类就必须手动创建了。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
    
    
        @Provides
         public Kangshifu provideKangshifu() {
            return new Kangshifu();
        }
    //    @Provides
    //     public Noodle provideNoodle(Tongyi noodle) {
    //        return noodle;
    //    }
    }
    

    这段代码,是可以正常运行的。
    这里写图片描述

    现在,我有一个新的需求更改。我在 ZhaiNan 类中 eat() 方法中要把餐厅名字打印出来。

    public class ZhaiNan {
    
        @Inject
        Baozi baozi;
    
        @Inject
        Noodle noodle;
    
        @Inject
        public ZhaiNan() {
    
        }
    
        @Inject
        String resturant;
    
        public String eat() {
            StringBuilder sb = new StringBuilder();
            sb.append("我从 ");
            sb.append(resturant.toString());
            sb.append("订的外卖,");
            sb.append("我吃的是 ");
            if ( baozi != null ) {
                sb.append(baozi.toString());
            }
    
            if (noodle != null) {
                sb.append("  ");
                sb.append(noodle.toString());
            }
            return sb.toString();
        }
    }
    

    注意的是,我要新增了一个 String 类型的字段 resturant 来代表餐厅,并且用 @Inject 注解它。
    于是,相应的 Module 也要更改。

    @Module
    public class ShangjiaAModule {
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
        @Provides
         public Noodle provideNoodle(Kangshifu noodle) {
            return noodle;
        }
    
        @Provides
         public Kangshifu provideKangshifu() {
            return new Kangshifu();
        }
    
        @Provides
         public String provideResturant() {
            return "王小二包子店";
        }
    //    @Provides
    //     public Noodle provideNoodle(Tongyi noodle) {
    //        return noodle;
    //    }
    }
    

    然后,再编译测试。
    这次的编译报错了。

    Error:(10, 13) 错误: java.lang.String cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
    java.lang.String is injected at
    com.frank.dagger2demo.ZhaiNan.resturant
    com.frank.dagger2demo.ZhaiNan is provided at
    com.frank.dagger2demo.Platform.waimai()
    

    报错的原因是之前编写的接口 Platform 没有办法获取 ZhanNan 中 resturant 的依赖。因为之前并没有为 Platform 指定 Module。
    那么现在我们就为它指定 ShangjiaAModule 吧。

    编译后,进行测试,程序正常运行。
    这里写图片描述

    现在,我把代码再重构。在 ShangjiaAModule 中提供餐厅名字的时候,直接返回了“王小二包子店”,这个过于直接,缺少变动,现在针对这个进行变化。

    @Module
    public class ShangjiaAModule {
    
        String restaurant;
    
        public ShangjiaAModule(String restaurant) {
            this.restaurant = restaurant;
        }
    
        ......
    
        @Provides
         public String provideResturant() {
            return restaurant;
        }
    
    }
    

    restaurant 在 ShangjiaAModule 创建的时候被赋值,但我们之前的代码好像并没有处理 ShangjiaAModule 的创建,那么它如何创建呢?
    我们先尝试重新编译代码并运行。

    编译没有出错,但运行的时候出错了。

    Caused by: java.lang.IllegalStateException: com.frank.dagger2demo.ShangjiaAModule must be set
    
    at com.frank.dagger2demo.DaggerPlatform$Builder.build(DaggerPlatform.java:64)
    at com.frank.dagger2demo.MainActivity.onCreate(MainActivity.java:21)
    

    Log 提示的是调用 DaggerPlatform中的Builder.build() 方法时出错了,因为 ShangjiaAModule 并没有出错。我们定位代码。

    final ZhaiNan zainan = DaggerPlatform.builder()
                    .build()
                    .waimai();
    

    我之前没有讲的是,如果一个 Module 没有实现任何构造方法,那么在 Component 中 Dagger2 会自动创建,如果这个 Module 实现了有参的构造方法,那么它需要在 Component 构建的时候手动传递进去。怎么传呢?Component 中生成的 Builder 构造器有与 Module 名字相同的方法,并且参数类型就是 Module 类型。大家细细体会下面代码就明白了。

    final ZhaiNan zainan = DaggerPlatform.builder()
                    .shangjiaAModule(new ShangjiaAModule("王小二包子店"))
                    .build()
                    .waimai();
    
    
    final ZhaiNan zainan1 = DaggerWaimaiPingTai.builder()
                    .shangjiaAModule(new ShangjiaAModule("衡阳鱼粉店"))
                    .build()
                    .waimai();
    

    再编译运行。
    这里写图片描述

    另外,还有一种特殊情况就是,像在 Android 中,MainActivity 这样的代码是我们自己编写的,所以我们可以给相应的属性添加 @Inject 注解,但是 MainActivity 对象的创建却是由 Android Framework 框架决定的,那么,Dagger2 有没有针对这种内部拥有 @Inject 标注的属性,但还没有进行依赖绑定的类的对象进行依赖注入呢?答案是肯定的。

    我们知道,Component 是一个接口,它里面可以定义很多方法。方法的返回值可以提供一种类型的对象,前提是这个类的对象被 @Inject 注解过构造方法或者在 Module 中被 @Provides 注解过的方法提供。

    @Component(modules = ShangjiaAModule.class)
    public interface WaimaiPingTai {
        ZhaiNan waimai();
    }
    

    ZhaiNan 能够在一个 Component 中的方法中作为类型返回是因为它符合我上面说的条件,它的构造方法被 @Inject 注解过。

    需要注意的是,Component 中方法除了可以返回类型,还可以在方法中传入类型参数。目的是针对这个参数对象进行依赖注入。
    比如

    @Component(modules = ShangjiaAModule.class)
    public interface WaimaiPingTai {
        ZhaiNan waimai();
    
        void zhuru(ZhaiNan zhaiNan);
    }
    

    我新增了一个方法,zhuru() 中的参数就是 ZhaiNan 类型,代表 DaggerWaimaiPingTai 调用这个方法时能够对一个 ZhaiNan 对象进行依赖注入。

    可以编写代码验证。

    mBtnTestZhuru = (Button) findViewById(R.id.btn_test_zhuru);
    final ZhaiNan zhaiNan = new ZhaiNan();
    WaimaiPingTai daggerWaimaiPingTai = DaggerWaimaiPingTai.builder()
            .shangjiaAModule(new ShangjiaAModule("常德津市牛肉粉"))
            .build();
    // 通过调用接口中的方法给 zhaiNan 进行依赖注入
    daggerWaimaiPingTai.zhuru(zhaiNan);
    
    mBtnTestZhuru.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,zhaiNan.eat(),Toast.LENGTH_LONG).show();
        }
    });
    

    运行结果如下:
    这里写图片描述

    所以,我们可以给接口方法参数传值的形式来给 Activity 进行依赖注入。

    @Module
    public class ActivityModule {
    
        @Provides
        public int provideActivityTest(){
            return 1234567890;
        }
    }
    
    
    @Component(modules = {ShangjiaAModule.class,ActivityModule.class})
    public interface WaimaiPingTai {
        ZhaiNan waimai();
    
        void zhuru(ZhaiNan zhaiNan);
    
        void inject(MainActivity mainActivity);
    }  
    
    

    我们编写了新的 Module,然后把它放时 WaimaiPingTai 这个 Component 中去,再添加了 inject() 方法,为的是能够给 MainActivity 实例进行依赖注入。

    现在我们添加测试代码,首先在 MainActivity 中添加一个 int 类型的成员变量。

    @Inject
    int testvalue;
    

    然后要调用相关注入方法

    mBtnTestActivity = (Button) findViewById(R.id.btn_test_inject_act);
    final ZhaiNan zhaiNan = new ZhaiNan();
    WaimaiPingTai daggerWaimaiPingTai = DaggerWaimaiPingTai.builder()
            .shangjiaAModule(new ShangjiaAModule("常德津市牛肉粉"))
            .build();
    
    daggerWaimaiPingTai.inject(this);
    
    mBtnTestActivity.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this,"testvalue is "+ testvalue,Toast.LENGTH_LONG).show();
        }
    });
    

    运行结果:
    这里写图片描述

    Component 的创建方式

    我们可以看到,创建 Component 都是通过它的 Builder 这个类来进行构建的。其实还有另外一种方式。那就是直接调用 Component 实现类的 create() 方法。

    public class Test {}
    
    @Component(modules = TestCreate.class)
    public interface TestCreateComponent {
        Test ceshi();
    }
    
    TestCreateComponent testCreateComponent = DaggerTestCreateComponent.create();
    Test test = testCreateComponent.ceshi();
    

    上面代码中创建 TestCreateComponent 并没有借助于 Builder,而是直接调用了 DaggerTestCreateComponent 的 create() 方法,但是它有一个前提,这个前提就是 Component 中的 module 中被 @Provides 注解的方法都必须是静态方法,也就是它们必须都被 static 修饰。

    @Module
    public class TestCreate {
    
        @Provides
        public static int provideTest1() {
            return 1;
        }
    
        @Provides
        public static String provideTest2() {
            return "test component create()";
        }
    
        @Provides
        public static Test provideTest(){
            return new Test();
        }
    }
    

    因为不需要创建 Module 对象实例,所以 Builder 自然就可以省去了。

    @Inject 和 @Provides 的优先级

    可能有心思细腻的同学会问,同样是提供依赖,如果一个类被 @Inject 注解了构造方法,又在某个 Module 中的 @Provides 注解的方法中提供了依赖,那么最终 Dagger2 采用的是哪一个?

    public class Baozi {
    
        String name;
    
        @Inject
        public Baozi() {
            name = "小笼包";
        }
    
        public Baozi(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return name;
        }
    }
    
    @Module
    public class ShangjiaAModule {
    
        String restaurant;
    
        public ShangjiaAModule(String restaurant) {
            this.restaurant = restaurant;
        }
    
        @Provides
         public Baozi provideBaozi() {
            return new Baozi("豆沙包");
        }
    
    }
    

    Baozi 这个类就符合我上面给的情景,一方面它确实拥有被 @Inject 注解过的构造方法,另一方面在 Module 中它又通过 @Provides 提供了依赖。那么,最终,Dagger2 采取了哪一种呢?

    答案是 Module,其实现象我们在之前的测试时已经可以观察到了,最终屏幕显示的是豆沙包选项。

    Dagger2 依赖查找的顺序是先查找 Module 内所有的 @Provides 提供的依赖,如果查找不到再去查找 @Inject 提供的依赖。

    到这里,我们讲解了 Dagger2 中最常见的 4 个注解:@Inject、@Component、@Module、@Provides。

    正常情况下,这 4 个注解能够很好的完成一般的代码开发了。但是,这都是基础功能,Dagger2 提供了更多的一些特性。

    Dagger2 中的单例 @Singleton

    我们在平常开发中经常要涉及到各种单例。比如在 Android 中开发,数据库访问最好要设计一个单例,网络访问控制最好设计一个单例。我们经常编写这样的代码。

    public class DBManager {
    
        private static DBManager instance;
    
        private DBManager() {
        }
    
        public static DBManager getInstance() {
            if ( instance == null ) {
                synchronized ( DBManager.class ) {
                    if ( instance == null ) {
                        instance = new DBManager();
                    }
                }
            }
    
            return instance;
        }
    }
    

    这种代码手段千遍一律,而 Dagger2 提供了另外一种可能。那就是利用 @Singleton 注解解决它。@Singleton 怎么使用呢?我们用代码来说明。

    @Singleton
    public class TestSingleton {
    
        @Inject
        public TestSingleton() {
        }
    }
    
    @Singleton
    @Component
    public interface ActivityComponent {
        void inject(SecondActivity activity);
    }
    

    用 @Singleton 标注在目标单例上,然后用 @Singleton 标注在 Component 对象上。

    编写测试代码

    public class SecondActivity extends AppCompatActivity {
        @Inject
        TestSingleton testSingleton1;
        @Inject
        TestSingleton testSingleton2;
    
        Button mBtnTestSingleton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sencond);
    
            mBtnTestSingleton = (Button) findViewById(R.id.btn_test_singleton);
    
            DaggerActivityComponent.builder()
                    .build()
                    .inject(this);
    
            mBtnTestSingleton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(SecondActivity.this,"test1 hashcode:"+testSingleton1.toString()
                        +" test2 hashcode:"+testSingleton2.toString(),Toast.LENGTH_LONG).show();
                }
            });
    
        }
    }
    

    编译后,测试结果往下:
    这里写图片描述

    可以看到,两个对象的 hashcode 是一样的,说明 TestSingleton 这个类实现了单例。
    另外,如果要以 @Provides 方式提供单例的话,需要用 @Singleton 注解依赖提供的方法。如

    @Module
    public class SecondActivityModule {
    
        @Provides
        @Singleton
        public TestSingleton provideTestSingleton(){
            return new TestSingleton();
        }
    }
    

    @Singleton 引出 @Scope

    我们在上一节的内容可以看到,通过 @Singleton 注解就可以实现一个单例了。本节的目标就是深入分析一下 @Singleton。

    @Scope
    @Documented
    @Retention(RUNTIME)
    public @interface Singleton {}
    

    @Singleton 是一个注解,但是它被一个元注解 @Scope 注解了,所以,可以猜测到的是 @Scope 是真正厉害的角色。而实际上 @Singleton 只是 @Scope 一个默认的实现而已,但是因为它更具可读性,能够让开发者一眼就明白它的作用是为了单例。但是,单例也是有范围限制的。

    分析 @Singleton 其实就等同于分析 @Scope 。Scope 的字面意思是作用域,也就是表达一种能力的范围。那么在 Dagger2 中它表达了一种什么样的能力范围呢?

    大家有没有想过,为什么要用 @Singleton 同时标注 @Provides 和 @Component ?

    文章一开始就讲过,Component 是联系需求与依赖的纽带,所以用 @Singleton 确定的单例作用域应该也是在 Component 的范围内。也就是说 @Scope 的作用范围其实就是单例能力范围,这个范围在单个的 Component 中。

    在上面的代码中,MainActivity 和 SecondActivity 运用了不同的 Component 现在我们可以测试一下它们所获取的 TestSingleton 会不会是同一个对象。

    public class MainActivity extends AppCompatActivity {
    
        @Inject
        public  TestSingleton testSingleton;
    
        onCreate() {
    
            mBtnJumpToSecond.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                    startActivity(intent);
    
                    Toast.makeText(MainActivity.this,"testsingleton is "+ testSingleton,Toast.LENGTH_LONG).show();
                }
            });
        }
    
    }
    
    @Module
    public class ActivityModule {
    
        @Provides
        public int provideActivityTest(){
            return 1234567890;
        }
    
        @Provides
        @Singleton
        public TestSingleton provideSingleton(){
            return new TestSingleton();
        }
    }
    
    @Singleton
    @Component(modules = {ShangjiaAModule.class,ActivityModule.class})
    public interface WaimaiPingTai {
        ZhaiNan waimai();
    
        void zhuru(ZhaiNan zhaiNan);
    
        void inject(MainActivity mainActivity);
    }
    

    我们改写了 MainActivity 和它涉及到的 Module、Component。
    现在,我们检测 MainActivity 和 SecondActivity 中的 TestSingleton 是不是同一个。
    这里写图片描述
    可以发现,它们并不是同一个。也就是说 @Singleton 所拥有的单例能力是以 Component 为范围的限定的。

    @Singleton 起作用是因为它被 @Scope 注解,所以,如果可能,我们也可以自己定义 Scope。

    @Scope
    @Documented
    @Retention(RUNTIME)
    public @interface PageScope {}
    

    我自己定义一个 @PageScope 注解,我的想法是一个 Activity 有不同的 Fragment,所以以 @PageScope 标注的依赖对象在这些 Fragment 之间是同一个对象,也就是说在这个 Activity 中实现了单例。而在另外一个 Activity 中因为采取了不同的 Component 对象,所以它们的 Fragment 也共用了同一个依赖对象,但是两个 Activity 中各自的依赖确不是同一个对象。

    大家细细体会。

    @Qualifiers 和 @Name

    Qualifiers 是修饰符的意思,那么它修饰的是什么呢?不知道大家有没有察觉到,前面的演示代码其实很简单,经不起太多推敲。

    在一个 Module 中 @Provides 提供的依赖是由返回值决定的。这样就会出现问题,同一种类型不同实例,怎么去区别?比如

    public class SecondActivity extends AppCompatActivity {
    
    
        @Inject
        String phone;
    
        @Inject
        String computer;
    
    }

    phone 和 computer 应该要对应不同的字符串。但是,我们该如何在 Module 中进行编码呢?

    @Module
    public class SecondActivityModule {
    
        @Provides
        @Singleton
        public TestSingleton provideTestSingleton(){
            return new TestSingleton();
        }
    
        @Provides
        public String providePhone() {
            return "手机";
        }
    
        @Provides
        public String providePhone() {
            return "电脑";
        }
    
    }
    

    大家可能会想到这样编码,但是这样的代码根本编译不过。因为 Dagger2 是根据返回的类型来进行依赖关系确定的。如果存在两个方法返回一样的类型,那么正常情况下 Dagger2 显然就没有办法处理了。

    不过,Dagger2 给出了解决方案。用 @Name 注解就好了,配合 @Inject 和 @Provides 一起使用。例如

     @Inject
    @Named("phone")
    String phone;
    
    @Inject
    @Named("computer")
    String computer;
    
    @Module
    public class SecondActivityModule {
    
        @Provides
        @Singleton
        public TestSingleton provideTestSingleton(){
            return new TestSingleton();
        }
    
        @Provides
        @Named("phone")
        public String providePhone() {
            return "手机";
        }
    
        @Provides
        @Named("computer")
        public String provideComputer() {
            return "电脑";
        }
    
    }
    

    当然,如果你嫌每次给 @Name 麻烦,你可以自定义注解。

    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Named {
    
        /** The name. */
        String value() default "";
    }
    

    @Name 只是被 @Qualifier 注解的一个注解。所以,它能够有效完全是因为 @Qualifier。

    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Phone {
    }
    
    
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Computer {
    }
    

    通过 @Qualifier 建立了 @Phone 和 @Computer 注解。

    @Inject
    @Phone
    String phone;
    
    @Inject
    @Computer
    String computer;
    
    @Module
    public class SecondActivityModule {
    
        @Provides
        @Singleton
        public TestSingleton provideTestSingleton(){
            return new TestSingleton();
        }
    
        @Provides
        @Phone
        public String providePhone() {
            return "手机";
        }
    
        @Provides
        @Computer
        public String provideComputer() {
            return "电脑";
        }
    
    }
    

    这样的效果是一样的。但是好处在于 @Name() 中要传入字符串,一不小心就容易将单词拼错,容易出错。

    Dagger2 中的延迟加载

    有些时候,我们希望依赖只有在我们使用的时候再去实例化,这样的机制叫做延迟加载。
    比如

    public class TestLazy {
    
        String name;
    
        public String getName() {
            if ( name == null ) {
                name = "TestLazy";
            }
    
            return name;
        }
    }
    

    只有调用 TestLazy 实例的 getName() 方法时,name 才会被初始化。

    Dagger2 提供了延迟加载能力。只需要通过 Lazy 就好了,Lazy 是泛型类,接受任何类型的参数。

    public class TestLazy {
    
        @Inject
        @Named("TestLazy")
        Lazy<String> name;
    
        public String getName() {
            return name.get();
        }
    
    }
    
    @Module
    public class SecondActivityModule {
    
        @Provides
        @Singleton
        public TestSingleton provideTestSingleton(){
            return new TestSingleton();
        }
    
        @Provides
        @Phone
        public String providePhone() {
            return "手机";
        }
    
        @Provides
        @Computer
        public String provideComputer() {
            return "电脑";
        }
    
        @Provides
        @Named("TestLazy")
        public String provideTestLazy() {
            return "TestLazy";
        }
    
    }
    

    这样,只有第一次调用 TestLazy 的 getName() 方法时,name 都会被注入。

    Provider 强制重新加载

    应用 @Singleton 的时候,我们希望每次都是获取同一个对象,但有的时候,我们希望每次都创建一个新的实例,这种情况显然与 @Singleton 完全相反。Dagger2 通过 Provider 就可以实现。它的使用方法和 Lazy 很类似。

    
    public class TestProvider {
        @Inject
        Provider<Integer> randomValue;
    
        public int getRandomValue () {
            return randomValue.get().intValue();
        }
    }
    
    
    @Module
    public class SecondActivityModule {
    
        ......
    
        @Provides
        public int provideRandomValue(){
            return (int) Math.random();
        }
    
    }
    

    但是,需要注意的是 Provider 所表达的重新加载是说每次重新执行 Module 相应的 @Provides 方法,如果这个方法本身每次返回同一个对象,那么每次调用 get() 的时候,对象也会是同一个。

    Dagger2 中 Component 之间的依赖。

    在程序开发中,可以存在多个 Component,而且 Component 之间还可以有依赖关系。比如

    public class Guazi {}
    
    public class Huotuichang {}
    
    @Module
    public class XiaoChiModule {
    
        @Provides
        public Guazi provideGuazi() {
            return new Guazi();
        }
    
        @Provides
        public Huotuichang provideHuotuichang() {
            return new Huotuichang();
        }
    }
    
    @Component(modules = XiaoChiModule.class)
    public interface XiaoChiComponent {
        Guazi provideGuazi();
    
        Huotuichang provideHuotuichang();
    }
    
    

    XiaoChiComponent 这个 Component 主要作用是提供瓜子和火腿肠这些依赖。

    现在,我要新建立一个 Component 代表食物类,并且食物类包括小吃,因此,我们得想办法利用 XiaoChiCompoent 这个现成的 Component。

    @Module
    public class FoodModule {
    
        @Provides
        public Baozi provideBaozi() {
            return new Baozi();
        }
    
        @Provides
        public Noodle provideNoodle() {
            return new Kangshifu();
        }
    }
    
    @Component(modules = XiaoChiModule.class
            ,dependencies = XiaoChiComponent.class)
    public interface FoodComponent {
        void inject(ThirdActivity activity);
    }
    

    只需要在 @Component 中的 dependencies 属性取值为相应的依赖就可以了。这样,FoodComponent 也提供了瓜子和火腿肠的依赖。我们再看如何使用?

    public class ThirdActivity extends AppCompatActivity {
    
        Button mBtnTest;
    
        @Inject
        Guazi guazi;
        @Inject
        Huotuichang huotuichang;
        @Inject
        Baozi baozi;
        @Inject
        Noodle noodle;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_third);
    
            mBtnTest = (Button) findViewById(R.id.test_dependency);
    
            XiaoChiComponent xiaoChiComponent = DaggerXiaoChiComponent.builder()
                    .build();
    
            DaggerFoodComponent.builder()
                    .xiaoChiComponent(xiaoChiComponent)
                    .build()
                    .inject(this);
    
            mBtnTest.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(ThirdActivity.this,
                            baozi+" "+
                            noodle+" "
                            +guazi+""+huotuichang,Toast.LENGTH_LONG).show();
                }
            });
    
        }
    }
    

    只需要在 DaggerFoodComponent 构建的时候,将所依赖的 Component 传递进去就是了,但前提是要先创建 XiaochiComponent ,然后创建 FoodComponent 的时候将它传递进去。如上面调用了 DaggerFoodComponent.Builder 的 xiaochiComponent() 方法。

    继续查看结果。
    这里写图片描述

    Dagger2 中的 SubComponent

    在 Java 软件开发中,我们经常面临的就是“组合”和“继承”的概念。它们都是为了扩展某个类的功能。
    前面的 Component 的依赖采用 @Component(dependecies=othercomponent.class) 就相当于组合。
    那么在 Dagger2 中,运用 @SubComponent 标记一个 Component 的行为相当于继承。

    @Subcomponent(modules = FoodModule.class)
    public interface SubComponent {
        void inject(ThirdActivity activity);
    }
    
    @Component(modules = XiaoChiModule.class)
    public interface ParentComponent {
        SubComponent provideSubComponent();
    }
    
    
    DaggerParentComponent.builder().build()
                    .provideSubComponent().inject(this);
    

    使用 Subcomponent 时,还是要先构造 ParentComponent 对象,然后通过它提供的 SubComponent 再去进行依赖注入。

    大家可以细细观察下它与 depedency 方法的不同之处。

    但是,SubComponent 同时具备了 ParentComponent 和自身的 @Scope 作用域。所以,这经常会造成混乱的地方。大家需要注意。

    如果你要我比较,SubComponent 和 dependency 形式哪种更好时,我承认各有优点,但我自己倾向于 dependency,因为它更灵活。

    不是说 组合优于继承嘛

    到这里的时候,Dagger2 的基础知识都介绍的差不多了,它还有一些知识点,但是应用的场景太复杂,所以没有必要细究。有兴趣的同学可以到官网上自行研究。

    Dagger2 在什么地方进入依赖注入?如何注入?

    也许会有一部分同学,执着于细节。因为 Dagger2 帮我们进行了依赖注入,但这一切过程是透明的,我们并不知晓。有探索精神的同学总想去获取更多的细节,这种精神值得称赞。

    我简单说一下,Dagger2 运用了 APT 插件,这种插件会在编译时根据 @Provide、@Inject、@Moudle、@Component 这些注解生成许多中间代码,但是不管它多么复杂它的目的也只是为了依赖注入。所以,肯定有一个地方进行了。

    
    a.setB(b);
    

    这样的操作。

    在依赖注入概念中,我分了需求者、注射者、依赖三个角色。
    这里写图片描述
    总是注射者给需求者注入了依赖。

    前面说过 Component 是需求与依赖的联系,因此可以在 Component 的实现类代码中找出分别代表需求、注射者、依赖 3 个角色,然后找出依赖注入发生时的代码,这个问题就算解答完成了。

    @Component(modules = SecondActivityModule.class)
    public interface ActivityComponent {
        void inject(SecondActivity activity);
    }
    

    我们以 ActivityComponent 为例解释说明,在这里显然 SecondActivity 的实例是需求者。

    @Inject
    TestSingleton testSingleton1;
    @Inject
    TestSingleton testSingleton2;
    
    @Inject
    @Phone
    String phone;
    
    @Inject
    @Computer
    String computer;
    

    SecondActivity 它需要 2 种依赖,TestSingleton、String。我们去查看最关键的类,也就是 Dagger2 帮助我们生成的 DaggerActivityComponent。

    public final class DaggerActivityComponent implements ActivityComponent {
      private Provider<TestSingleton> provideTestSingletonProvider;
    
      private Provider<String> providePhoneProvider;
    
      private Provider<String> provideComputerProvider;
    
      private MembersInjector<SecondActivity> secondActivityMembersInjector;
    
      private DaggerActivityComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
      }
    
    }

    我在前面讲解过 Provider 是什么用处,大家一看就懂。而 MembersInjector 以 Injector 为后缀,所以它肯定是一个注射器。我根本不需要去查看它的定义和相关源码。

    好了,现在注射者(MembersInjector)找到了,依赖(Provider provideTestSingletonProvider 等等)找到了,需求者我们知道是 SecondActivity,所以我们只要把依赖注入发生的代码找出来,问题就解答完成。

    @Override
    public void inject(SecondActivity activity) {
        secondActivityMembersInjector.injectMembers(activity);
    }
    

    inject() 方法显然是依赖注入发生的地方,但它内部调用了 secondActivityMembersInjector.injectMembers() 方法,我们跟踪进去。

    public void injectMembers(SecondActivity instance) {
        if (instance == null) {
          throw new NullPointerException("Cannot inject members into a null reference");
        }
        instance.testSingleton1 = testSingleton1AndTestSingleton2Provider.get();
        instance.testSingleton2 = testSingleton1AndTestSingleton2Provider.get();
        instance.phone = phoneProvider.get();
        instance.computer = computerProvider.get();
    }
    

    代码交待的一清二楚,SecondActivity 实例 instance 的 testSingleton1、testSingleton2、phone、computer 4 个依赖,全部在这里进行赋值,也就是在这里进行依赖注入。

    不过,我们都知道依赖都是由 Module 提供的,回到 DaggerActivityComponent 的源码当中关注它的 initialize 方法。

    private void initialize(final Builder builder) {
    
        this.provideTestSingletonProvider =
            DoubleCheck.provider(
                SecondActivityModule_ProvideTestSingletonFactory.create(builder.secondActivityModule));
    
        this.providePhoneProvider =
            SecondActivityModule_ProvidePhoneFactory.create(builder.secondActivityModule);
    
        this.provideComputerProvider =
            SecondActivityModule_ProvideComputerFactory.create(builder.secondActivityModule);
    
        this.secondActivityMembersInjector =
            SecondActivity_MembersInjector.create(
                provideTestSingletonProvider, providePhoneProvider, provideComputerProvider);
      }
    

    它们都是通过工厂方法创建的。大家应该都懂工厂方法都是用来创建对象的。我不嫌麻烦,挑出 SecondActivityModule_ProvidePhoneFactory 这个个例来进行讲解。它代表 Phone 对象的工厂。

    public final class SecondActivityModule_ProvidePhoneFactory implements Factory<String> {
      private final SecondActivityModule module;
    
      public SecondActivityModule_ProvidePhoneFactory(SecondActivityModule module) {
        assert module != null;
        this.module = module;
      }
    
      @Override
      public String get() {
        return Preconditions.checkNotNull(
            module.providePhone(), "Cannot return null from a non-@Nullable @Provides method");
      }
    
      public static Factory<String> create(SecondActivityModule module) {
        return new SecondActivityModule_ProvidePhoneFactory(module);
      }
    }
    
    
    public interface Factory<T> extends Provider<T> {
    }
    

    调用它的 create 方法会返回一个 Factory 对象,但是 Fractory 是 Provider 的子类。
    前面讲过调用 Provider 方法的 get() 方法能够得到实例,所以最终会调用

    public String get() {
        return Preconditions.checkNotNull(
            module.providePhone(), "Cannot return null from a non-@Nullable @Provides method");
    }
    

    所以最终会调用

    @Module
    public class SecondActivityModule {
    
        @Provides
        @Phone
        public String providePhone() {
            return "手机";
        }
    
    }
    

    其它几个流程类似,那就不一一分析了。有兴趣的同学可以去查看源码,我只提示一个信息,你去观察 MembersInjector 怎么创建 @Singleton 注解的依赖时会发现它的实现步骤,跟我前面文章手动生成 DBManager 单例用的手段是很相似的。

    对于 Dagger2 的用途与意义心生迷惑

    我想,还是会有一大部分的同学看到这里的时候仍然不明白,Dagger2 的妙处。
    其实在文章开始的地方我就讲了 Dagger2 的本质,它本质就是一款依赖注入框架,用来解耦的。

    掌握上面的 Dagger2 基础知识已经足够让你进行此类代码编写了,另外也足够让你去看懂一些运用了 Dagger2 的优秀开源项目,比如 Google 提供的示例
    todo-mvp-dagger

    这个项目示例就是为了演示 Dagger2 与 MVP 架构的配合使用。由于文章篇幅所限,我不作过多的讲解,大家自行研究。有机会,我会专门写一篇文章来讲述 Dagger2 在一个完整项目工程中如何进行解耦的。

    如果你对 Dagger2 兴趣更浓烈了

    Dagger2 的知识内容稍多,所以如果你耐着性子学习完后,兴致依赖不减,我可以给你一些建议。

    1. 自己去阅读官网文档。因为那才是第一手资料,虽然它写的不是很好,但毕竟权威。
    2. 多去观察不同的博文,因为每个人思考方式不一样,所以观察问题的角度可能不一样。
    3. 自己多练,任何没有经过自己实践的行为在软件编程中都不可取。
    4. 阅读优秀的开源代码,并思考。
    5. 在思考的同时,纠正自己的理解,然后再实践,再思考,再总结。我总说主动学习要好过被动学习,任何没有经过自己思考和求索的学习都是被动学习,看文档、看博文、看代码那都是别人的知识,你需要的就是用自己把这些纳入自己的知识体系中,当你也能讲述给其他人听的时候,那时你就可以确定你掌握它了。

    如果你仍然意识不到 Dagger2 的美好

    这个其实也没有多大关系。不要迷恋武器。

    也许你写的代码中类文件不是很多,模块之间的耦合并不是很强,或者是整个系统并不复杂,强行引进 Dagger2 只会让你感受复杂,多了很多类,多了很多编译的步骤,还增加了学习成本,你会觉得不划算。

    我们总说优化代码,设计架构,其实对于很多开发人员而言,大量的产品需求就能让自己加好几个晚上的班,并且需求还经常变动,如果你这个时候跟他讲代码规范什么的,肯定不现实。现实的事情是,完成需求永远第一要务。

    饱暖才能思淫欲。

    我与其劝你去感受 Dagger2 的美好,还不如劝你细细去体会一下依赖注入的美好。

    如果,你仍然觉得 Dagger2 麻烦,中肯地讲一句:那么索性放弃它算了,Dagger2 不重要,依赖注入才重要

    等到哪一天,你真的有强烈的对于代码解耦需求,也许你会想起 Dagger2 这么一款框架,那时候回过来学习,我保证你的效果会非常明显,你的理解力也会较现在更加的深刻。

    所以,我最终的目的仍然是希望你能够好好学习 Dagger2,希望大家不要误解我这一节的真实用意。

    demo

    这里写图片描述

    展开全文
  • 需要全部数据样本而不是抽样,不知道的事情比知道的事情更重要,但如果现在数据足够多,它会让人能够看得见、摸得着规律。数据这么大、这么多,所以人们觉得有足够能力把握未来,对不确定状态的一种判断,从而...

    大数据大数据思维原理是什么?笔者概括为10项原理。

    一、数据核心原理

    从“流程”核心转变为“数据”核心

    大数据时代,计算模式也发生了转变,从“流程”核心转变为“数据”核心。Hadoop体系的分布式计算框架已经是“数据”为核心的范式。非结构化数据及分析需求,将改变IT系统的升级方式:从简单增量到架构变化。大数据下的新思维——计算模式的转变。

    例如:IBM将使用以数据为中心的设计,目的是降低在超级计算机之间进行大量数据交换的必要性。大数据下,云计算找到了破茧重生的机会,在存储和计算上都体现了数据为核心的理念。大数据和云计算的关系:云计算为大数据提供了有力的工具和途径,大数据为云计算提供了很有价值的用武之地。而大数据比云计算更为落地,可有效利用已大量建设的云计算资源,最后加以利用。

    科学进步越来越多地由数据来推动,海量数据给数据分析既带来了机遇,也构成了新的挑战。大数据往往是利用众多技术和方法,综合源自多个渠道、不同时间的信息而获得的。为了应对大数据带来的挑战,我们需要新的统计思路和计算方法。

    说明:用数据核心思维方式思考问题,解决问题。以数据为核心,反映了当下IT产业的变革,数据成为人工智能的基础,也成为智能化的基础,数据比流程更重要,数据库、记录数据库,都可开发出深层次信息。云计算机可以从数据库、记录数据库中搜索出你是谁,你需要什么,从而推荐给你需要的信息。

    二、数据价值原理

    由功能是价值转变为数据是价值

    大数据真正有意思的是数据变得在线了,这个恰恰是互联网的特点。非互联网时期的产品,功能一定是它的价值,今天互联网的产品,数据一定是它的价值。

    例如:大数据的真正价值在于创造,在于填补无数个还未实现过的空白。有人把数据比喻为蕴藏能量的煤矿,煤炭按照性质有焦煤、无烟煤、肥煤、贫煤等分类,而露天煤矿、深山煤矿的挖掘成本又不一样。与此类似,大数据并不在“大”,而在于“有用”,价值含量、挖掘成本比数量更为重要。不管大数据的核心价值是不是预测,但是基于大数据形成决策的模式已经为不少的企业带来了盈利和声誉。

    数据能告诉我们,每一个客户的消费倾向,他们想要什么,喜欢什么,每个人的需求有哪些区别,哪些又可以被集合到一起来进行分类。大数据是数据数量上的增加,以至于我们能够实现从量变到质变的过程。举例来说,这里有一张照片,照片里的人在骑马,这张照片每一分钟,每一秒都要拍一张,但随着处理速度越来越快,从1分钟一张到1秒钟1张,突然到1秒钟10张后,就产生了电影。当数量的增长实现质变时,就从照片变成了一部电影。

    美国有一家创新企业Decide.com

    它可以帮助人们做购买决策,告诉消费者什么时候买什么产品,什么时候买最便宜,预测产品的价格趋势,这家公司背后的驱动力就是大数据。他们在全球各大网站上搜集数以十亿计的数据,然后帮助数以十万计的用户省钱,为他们的采购找到最好的时间,降低交易成本,为终端的消费者带去更多价值。

    在这类模式下,尽管一些零售商的利润会进一步受挤压,但从商业本质上来讲,可以把钱更多地放回到消费者的口袋里,让购物变得更理性,这是依靠大数据催生出的一项全新产业。这家为数以十万计的客户省钱的公司,在几个星期前,被eBay以高价收购。

    再举一个例子,SWIFT是全球最大的支付平台,在该平台上的每一笔交易都可以进行大数据的分析,他们可以预测一个经济体的健康性和增长性。比如,该公司现在为全球性客户提供经济指数,这又是一个大数据服务。,定制化服务的关键是数据。《大数据时代》的作者维克托·迈尔·舍恩伯格认为,大量的数据能够让传统行业更好地了解客户需求,提供个性化的服务。

    说明:用数据价值思维方式思考问题,解决问题。信息总量的变化导致了信息形态的变化,量变引发了质变,最先经历信息爆炸的学科,如天文学和基因学,创造出了“大数据”这个概念。如今,这个概念几乎应用到了所有人类致力于发展的领域中。从功能为价值转变为数据为价值,说明数据和大数据的价值在扩大,数据为“王”的时代出现了。数据被解释是信息,信息常识化是知识,所以说数据解释、数据分析能产生价值。

    三、全样本原理

    从抽样转变为需要全部数据样本

    需要全部数据样本而不是抽样,你不知道的事情比你知道的事情更重要,但如果现在数据足够多,它会让人能够看得见、摸得着规律。数据这么大、这么多,所以人们觉得有足够的能力把握未来,对不确定状态的一种判断,从而做出自己的决定。这些东西我们听起来都是非常原始的,但是实际上背后的思维方式,和我们今天所讲的大数据是非常像的。

    举例:在大数据时代,无论是商家还是信息的搜集者,会比我们自己更知道你可能会想干什么。现在的数据还没有被真正挖掘,如果真正挖掘的话,通过信用卡消费的记录,可以成功预测未来5年内的情况。统计学里头最基本的一个概念就是,全部样本才能找出规律。为什么能够找出行为规律?一个更深层的概念是人和人是一样的,如果是一个人特例出来,可能很有个性,但当人口样本数量足够大时,就会发现其实每个人都是一模一样的。

    说明:用全数据样本思维方式思考问题,解决问题。从抽样中得到的结论总是有水分的,而全部样本中得到的结论水分就很少,大数据越大,真实性也就越大,因为大数据包含了全部的信息。

    四、关注效率原理

    由关注精确度转变为关注效率

    关注效率而不是精确度,大数据标志着人类在寻求量化和认识世界的道路上前进了一大步,过去不可计量、存储、分析和共享的很多东西都被数据化了,拥有大量的数据和更多不那么精确的数据为我们理解世界打开了一扇新的大门。大数据能提高生产效率和销售效率,原因是大数据能够让我们知道市场的需要,人的消费需要。大数据让企业的决策更科学,由关注精确度转变为关注效率的提高,大数据分析能提高企业的效率。

    例如:在互联网大数据时代,企业产品迭代的速度在加快。三星、小米手机制造商半年就推出一代新智能手机。利用互联网、大数据提高企业效率的趋势下,快速就是效率、预测就是效率、预见就是效率、变革就是效率、创新就是效率、应用就是效率。

    竞争是企业的动力,而效率是企业的生命,效率低与效率高是衡量企来成败的关键。一般来讲,投入与产出比是效率,追求高效率也就是追求高价值。手工、机器、自动机器、智能机器之间效率是不同的,智能机器效率更高,已能代替人的思维劳动。智能机器核心是大数据制动,而大数据制动的速度更快。在快速变化的市场,快速预测、快速决策、快速创新、快速定制、快速生产、快速上市成为企业行动的准则,也就是说,速度就是价值,效率就是价值,而这一切离不开大数据思维。

    说明:用关注效率思维方式思考问题,解决问题。大数据思维有点像混沌思维,确定与不确定交织在一起,过去那种一元思维结果,已被二元思维结果取代。过去寻求精确度,现在寻求高效率;过去寻求因果性,现在寻求相关性;过去寻找确定性,现在寻找概率性,对不精确的数据结果已能容忍。只要大数据分析指出可能性,就会有相应的结果,从而为企业快速决策、快速动作、创占先机提高了效率。

    五、关注相关性原理

    由因果关系转变为关注相关性

    关注相关性而不是因果关系,社会需要放弃它对因果关系的渴求,而仅需关注相关关系,也就是说只需要知道是什么,而不需要知道为什么。这就推翻了自古以来的惯例,而我们做决定和理解现实的最基本方式也将受到挑战。

    例如:大数据思维一个最突出的特点,就是从传统的因果思维转向相关思维,传统的因果思维是说我一定要找到一个原因,推出一个结果来。而大数据没有必要找到原因,不需要科学的手段来证明这个事件和那个事件之间有一个必然,先后关联发生的一个因果规律。它只需要知道,出现这种迹象的时候,我就按照一般的情况,这个数据统计的高概率显示它会有相应的结果,那么我只要发现这种迹象的时候,我就可以去做一个决策,我该怎么做。这是和以前的思维方式很不一样,老实说,它是一种有点反科学的思维,科学要求实证,要求找到准确的因果关系。

    在这个不确定的时代里面,等我们去找到准确的因果关系,再去办事的时候,这个事情早已经不值得办了。所以“大数据”时代的思维有点像回归了工业社会的这种机械思维——机械思维就是说我按那个按钮,一定会出现相应的结果,是这样状态。而农业社会往前推,不需要找到中间非常紧密的、明确的因果关系,而只需要找到相关关系,只需要找到迹象就可以了。社会因此放弃了寻找因果关系的传统偏好,开始挖掘相关关系的好处。

    例如:美国人开发一款“个性化分析报告自动可视化程序”软件从网上挖掘数据信息,这款数据挖掘软件将自动从各种数据中提取重要信息,然后进行分析,并把此信息与以前的数据关联起来,分析出有用的信息。

    非法在屋内打隔断的建筑物着火的可能性比其他建筑物高很多。纽约市每年接到2.5万宗有关房屋住得过于拥挤的投诉,但市里只有200名处理投诉的巡视员,市长办公室一个分析专家小组觉得大数据可以帮助解决这一需求与资源的落差。该小组建立了一个市内全部90万座建筑物的数据库,并在其中加入市里19个部门所收集到的数据:欠税扣押记录、水电使用异常、缴费拖欠、服务切断、救护车使用、当地犯罪率、鼠患投诉,诸如此类。

    接下来,他们将这一数据库与过去5年中按严重程度排列的建筑物着火记录进行比较,希望找出相关性。果然,建筑物类型和建造年份是与火灾相关的因素。不过,一个没怎么预料到的结果是,获得外砖墙施工许可的建筑物与较低的严重火灾发生率之间存在相关性。利用所有这些数据,该小组建立了一个可以帮助他们确定哪些住房拥挤投诉需要紧急处理的系统。他们所记录的建筑物的各种特征数据都不是导致火灾的原因,但这些数据与火灾隐患的增加或降低存在相关性。这种知识被证明是极具价值的:过去房屋巡视员出现场时签发房屋腾空令的比例只有13%,在采用新办法之后,这个比例上升到了70%——效率大大提高了。

    全世界的商界人士都在高呼大数据时代来临的优势:一家超市如何从一个17岁女孩的购物清单中,发现了她已怀孕的事实;或者将啤酒与尿不湿放在一起销售,神奇地提高了双方的销售额。大数据透露出来的信息有时确实会起颠覆。比如,腾讯一项针对社交网络的统计显示,爱看家庭剧的男人是女性的两倍还多;最关心金价的是中国大妈,但紧随其后的却是90后。而在过去一年,支付宝中无线支付比例排名前十的竟然全部在青海、西藏和内蒙古地区。

    说明:用关注相关性思维方式来思考问题,解决问题。寻找原因是一种现代社会的一神论,大数据推翻了这个论断。过去寻找原因的信念正在被“更好”的相关性所取代。当世界由探求因果关系变成挖掘相关关系,我们怎样才能既不损坏建立在因果推理基础之上的社会繁荣和人类进步的基石,又取得实际的进步呢?这是值得思考的问题。

    解释:转向相关性,不是不要因果关系,因果关系还是基础,科学的基石还是要的。只是在高速信息化的时代,为了得到即时信息,实时预测,在快速的大数据分析技术下,寻找到相关性信息,就可预测用户的行为,为企业快速决策提供提前量。

    比如预警技术,只有提前几十秒察觉,防御系统才能起作用。比如,雷达显示有个提前量,如果没有这个预知的提前量,雷达的作用也就没有了,相关性也是这个原理。比如,相对论与量子论的争论也能说明问题,一个说上帝不掷骰子,一个说上帝掷骰子,争论几十年,最后承认两个都存在,而且量子论取得更大的发展——一个适用于宇宙尺度,一个适用于原子尺度。

    六、预测原理

    从不能预测转变为可以预测

    大数据的核心就是预测,大数据能够预测体现在很多方面。大数据不是要教机器像人一样思考,相反,它是把数学算法运用到海量的数据上来预测事情发生的可能性。正因为在大数据规律面前,每个人的行为都跟别人一样,没有本质变化,所以商家会比消费者更了消费者的行为。

    例如:大数据助微软准确预测世界怀。微软大数据团队在2014年巴西世界足球赛前设计了世界怀模型,该预测模型正确预测了赛事最后几轮每场比赛的结果,包括预测德国队将最终获胜。预测成功归功于微软在世界怀进行过程中获取的大量数据,到淘汰赛阶段,数据如滚雪球般增多,常握了有关球员和球队的足够信息,以适当校准模型并调整对接下来比赛的预测。

    世界杯预测模型的方法与设计其它事件的模型相同,诀窍就是在预测中去除主观性,让数据说话。预测性数学模型几乎不算新事物,但它们正变得越来越准确。在这个时代,数据分析能力终于开始赶上数据收集能力,分析师不仅有比以往更多的信息可用于构建模型,也拥有在很短时间内通过计算机将信息转化为相关数据的技术。

    几年前,得等每场比赛结束以后才能获取所有数据,现在,数据是自动实时发送的,这让预测模型能获得更好的调整且更准确。微软世界怀模型的成绩说明了其模型的实力,它的成功为大数据的力量提供了强有力的证明,利用同样的方法还可预测选举或关注股票。类似的大数据分析正用于商业、政府、经济学和社会科学,它们都关于原始数据进行分析。

    我们进入了一个用数据进行预测的时代,虽然我们可能无法解释其背后的原因。如果一个医生只要求病人遵从医嘱,却没法说明医学干预的合理性的话,情况会怎么样呢?实际上,这是依靠大数据取得病理分析的医生们一定会做的事情。

    从一个人乱穿马路时行进的轨迹和速度来看他能及时穿过马路的可能性,都是大数据可以预测的范围。当然,如果一个人能及时穿过马路,那么他乱穿马路时,车子就只需要稍稍减速就好。但是这些预测系统之所以能够成功,关键在于它们是建立在海量数据的基础之上的。

    此外,随着系统接收到的数据越来越多,通过记录找到的最好的预测与模式,可以对系统进行改进。它通常被视为人工智能的一部分,或者更确切地说,被视为一种机器学习。真正的革命并不在于分析数据的机器,而在于数据本身和我们如何运用数据。一旦把统计学和现在大规模的数据融合在一起,将会颠覆很多我们原来的思维。所以现在能够变成数据的东西越来越多,计算和处理数据的能力越来越强,所以大家突然发现这个东西很有意思。所以,大数据能干啥?能干很多很有意思的事情。

    例如,预测当年葡萄酒的品质

    很多品酒师品的不是葡萄酒,那时候葡萄酒还没有真正的做成,他们品的是发烂的葡萄。因此在那个时间点就预测当年葡萄酒的品质是比较冒险的。而且人的心理的因素是会影响他做的这个预测,比如说地位越高的品酒师,在做预测时会越保守,因为他一旦预测错了,要损失的名誉代价是很大的。所以的品酒大师一般都不敢贸然说今年的酒特别好,或者是特别差;而刚出道的品酒师往往会“语不惊人死不休的”。

    普林斯顿大学有一个英语学教授,他也很喜欢喝酒,喜欢储藏葡萄酒,所以他就想是否可以分析到底哪年酒的品质好。然后他就找了很多数据,比如说降雨量、平均气温、土壤成分等等,然后他做回归,最后他说把参数都找出来,做了个网站,告诉大家今年葡萄酒的品质好坏以及秘诀是什么。

    当他的研究公布的时候,引起了业界的轩然大波,因为他做预测做的很提前,因为今年的葡萄收获后要经过一段的时间发酵,酒的味道才会好,但这个教授突然预测说今年的酒是世纪最好的酒。大家说怎么敢这么说,太疯狂了。更疯狂的是到了第二年,他预测今年的酒比去年的酒更好,连续两次预测说是百年最好的酒,但他真的预测对了。现在品酒师在做评判之前,要先到他的网站上看看他的预测,然后再做出自己的判断。有很多的规律我们不知道,但是它潜伏在这些大数据里头。

    例如,大数据描绘“伤害图谱”

    广州市伤害监测信息系统通过广州市红十字会医院、番禺区中心医院、越秀区儿童医院3个伤害监测哨点医院,持续收集市内发生的伤害信息,分析伤害发生的原因及危险因素,系统共收集伤害患者14681例,接近九成半都是意外事故。整体上,伤害多发生于男性,占61.76%,5岁以下儿童伤害比例高达14.36%,家长和社会应高度重视,45.19%的伤害都是发生在家中,其次才是公路和街道。

    收集到监测数据后,关键是通过分析处理,把数据“深加工”以利用。比如,监测数据显示,老人跌倒多数不是发生在雨天屋外,而是发生在家里,尤其是旱上刚起床时和浴室里,这就提示,防控老人跌倒的对策应该着重在家居,起床要注意不要动作过猛,浴室要防滑,加扶手等等。

    说明:用大数据预测思维方式来思考问题,解决问题。数据预测、数据记录预测、数据统计预测、数据模型预测,数据分析预测、数据模式预测、数据深层次信息预测等等,已转变为大数据预测、大数据记录预测、大数据统计预测、大数据模型预测,大数据分析预测、大数据模式预测、大数据深层次信息预测。

    互联网、移动互联网和云计算机保证了大数据实时预测的可能性,也为企业和用户提供了实时预测的信息,相关性预测的信息,让企业和用户抢占先机。由于大数据的全样本性,人和人都是一样的,所以云计算机软件预测的效率和准确性大大提高,有这种迹象,就有这种结果。

    七、信息找人原理

    从人找信息,转变为信息找人

    互联网和大数据的发展,是一个从人找信息,到信息找人的过程。先是人找信息,人找人,信息找信息,现在是信息找人的这样一个时代。信息找人的时代,就是说一方面我们回到了一种最初的,广播模式是信息找人,我们听收音机,我们看电视,它是信息推给我们的,但是有一个缺陷,不知道我们是谁,后来互联网反其道而行,提供搜索引擎技术,让我知道如何找到我所需要的信息,所以搜索引擎是一个很关键的技术。

    例如:从搜索引擎——向推荐引擎转变。今天,后搜索引擎时代已经正式来到,什么叫做后搜索引擎时代呢?使用搜索引擎的频率会大大降低,使用的时长也会大大的缩短,为什么使用搜索引擎的频率在下降?时长在下降?原因是推荐引擎的诞生。就是说从人找信息到信息找人越来越成为了一个趋势,推荐引擎就是说它很懂我,知道我要知道,所以是最好的技术。乔布斯说,让人感受不到技术的技术是最好的技术。

    大数据还改变了信息优势。按照循证医学,现在治病的第一件事情不是去研究病理学,而是拿过去的数据去研究,相同情况下是如何治疗的。这导致专家和普通人之间的信息优势没有了。原来我相信医生,因为医生知道的多,但现在我可以到谷歌上查一下,知道自己得了什么病。

    谷歌有一个机器翻译的团队,最开始的时候翻译之后的文字根本看不懂,但是现在60%的内容都能读得懂。谷歌机器翻译团队里头有一个笑话,说从团队每离开一个语言学家,翻译质量就会提高。越是专家越搞不明白,但打破常规让数据说话,得到真理的速度反而更快。

    说明:用信息找人的思维方式思考问题,解决问题。从人找信息到信息找人,是交互时代一个转变,也是智能时代的要求。智能机器已不是冷冰冰的机器,而是具有一定智能的机器。信息找人这四个字,预示着大数据时代可以让信息找人,原因是企业懂用户,机器懂用户,你需要什么信息,企业和机器提前知道,而且主动提供你需要的信息。

    八、机器懂人原理

    由人懂机器转变为机器更懂人

    不是让人更懂机器,而是让机器更懂人,或者说是能够在使用者很笨的情况下,仍然可以使用机器。甚至不是让人懂环境,而是让我们的环境来懂我们,环境来适应人,某种程度上自然环境不能这样讲,但是在数字化环境中已经是这样的一个趋势,就是我们所在的生活世界,越来越趋向于它更适应于我们,更懂我们。哪个企业能够真正做到让机器更懂人,让环境更懂人,让我们随身携带的整个的生活世界更懂得我们的话,那他一定是具有竞争力的了,而“大数据”技术能够助我们一臂之力。

    例如:亚马逊网站,只要买书,就会提供一个今天司空见惯的推荐,买了这本书的人还买了什么书,后来发现相关推荐的书比我想买的书还要好,时间久之后就会对它产生一种信任。这种信任就像在北京的那么多书店里面,以前买书的时候就在几家,原因在于我买书比较多,他都已经认识我了,都是我一去之后,我不说我要买什么书,他会推荐最近上来的几本书,可能是我感兴趣的。这样我就不会到别的很近的书店,因为这家书店更懂我。

    例如,解题机器人挑战大型预科学校高考模拟试题的结果,解题机器人的学历水平应该比肩普通高三学生。计算机不擅长对语言和知识进行综合解析,但通过借助大规模数据库对普通文章做出判断的方法,在对话填空和语句重排等题型上成绩有所提高。

    让机器懂人,是让机器具有学习的功能。人工智能已转变为研究机器学习。大数据分析要求机器更智能,具有分析能力,机器即时学习变得更重要。机器学习是指:计算机利用经验改善自身性能的行为。机器学习主要研究如何使用计算机模拟和实现人类获取知识(学习)过程、创新、重构已有的知识,从而提升自身处理问题的能力,机器学习的最终目的是从数据中获取知识。

    大数据技术的其中一个核心目标是要从体量巨大、结构繁多的数据中挖掘出隐蔽在背后的规律,从而使数据发挥最大化的价值。由计算机代替人去挖掘信息,获取知识。从各种各样的数据(包括结构化、半结构化和非结构化数据)中快速获取有价值信息的能力,就是大数据技术。大数据机器分析中,半监督学习、集成学习、 概率模型等技术尤为重要。

    说明:用机器更懂人的思维方式思考问题,解决问题。机器从没有常识到逐步有点常识,这是很大的变化。去年,美国人把一台云计算机送到大学里去进修,增加知识和常识。最近俄罗斯人开发一台计算机软件通过图林测试,表明计算机已初步具有智能。

    让机器懂人,这是人工智能的成功,同时,也是人的大数据思维转变。你的机器、你的软件、你的服务是否更懂人?将是衡量一个机器、一件软件、一项服务好坏的标准。人机关系已发生很大变化,由人机分离,转化为人机沟通,人机互补,机器懂人,现在年青人已离不开智能手机是一个很好的例证。在互联网大数据时代,有问题—问机器—问百度,成为生活的一部分。机器什么都知道,原因是有大数据库,机器可搜索到相关数据,从而使机器懂人。是人让机器更懂人,如果机器更懂人,那么机器的价值更高。

    九、电子商务智能原理

    大数据改变了电子商务模式,让电子商务更智能

    商务智能,在今天大数据时代它获得的重新的定义。

    例如:传统企业进入互联网,在掌握了“大数据”技术应用途径之后,会发现有一种豁然开朗的感觉,我整天就像在黑屋子里面找东西,找不着,突然碰到了一个开关,发现那么费力的找东西,原来很容易找得到。大数据思维,事实上它不是一个全称的判断,只是对我们所处的时代某一个纬度的描述。

    大数据时代不是说我们这个时代除了大数据什么都没有,哪怕是在互联网和IT领域,它也不是一切,只是说在我们的时代特征里面加上这么一道很明显的光,从而导致我们对以前的生存状态,以及我们个人的生活状态的一个差异化的一种表达。

    例如:大数据让软件更智能。尽管我们仍处于大数据时代来临的前夕,但我们的日常生活已经离不开它了。交友网站根据个人的性格与之前成功配对的情侣之间的关联来进行新的配对。例如,具有“自动改正”功能的智能手机通过分析我们以前的输入,将个性化的新单词添加到手机词典里。在不久的将来,世界许多现在单纯依靠人类判断力的领域都会被计算机系统所改变甚至取代。计算机系统可以发挥作用的领域远远不止驾驶和交友,还有更多更复杂的任务。别忘了,亚马逊可以帮我们推荐想要的书,谷歌可以为关联网站排序,Facebook知道我们的喜好,而linkedIn可以猜出我们认识谁。

    当然,同样的技术也可以运用到疾病诊断、推荐治疗措施,甚至是识别潜在犯罪分子上。或者说,在你还不知道的情况下,体检公司、医院提醒你赶紧去做检查,可能会得某些病,商家比你更了解你自己,以及你这样的人在某种情况下会出现的可能变化。就像互联网通过给计算机添加通信功能而改变了世界,大数据也将改变我们生活中最重要的方面,因为它为我们的生活创造了前所未有的可量化的维度。

    说明:用电子商务更智能的思维方式思考问题,解决问题。人脑思维与机器思维有很大差别,但机器思维在速度上是取胜的,而且智能软件在很多领域已能代替人脑思维的操作工作。例如美国一家媒体公司已用电脑智能软件写稿,可用率已达70%。云计算机已能处理超字节的大数据量,人们需要的所有信息都可得到显现,而且每个人互联网行为都可记录,这些记录的大数据经过云计算处理能产生深层次信息,经过大数据软件挖掘,企业需要的商务信息都能实时提供,为企业决策和营销、定制产品等提供了大数据支持。

    十、定制产品原理

    由企业生产产品转变为由客户定制产品

    下一波的改革是大规模定制,为大量客户定制产品和服务,成本低、又兼具个性化。比如消费者希望他买的车有红色、绿色,厂商有能力满足要求,但价格又不至于像手工制作那般让人无法承担。因此,在厂家可以负担得起大规模定制带去的高成本的前提下,要真正做到个性化产品和服务,就必须对客户需求有很好的了解,这背后就需要依靠大数据技术。

    例如:大数据改变了企业的竞争力。定制产品这是一个很好的技术,但是能不能够形成企业的竞争力呢?在产业经济学里面有一个很重要的区别,就是生产力和竞争力的区别,就是说一个东西是具有生产力的,那这种生产力变成一种通用生产力的时候,就不能形成竞争力,因为每一个人,每一个企业都有这个生产力的时候,只能提高自己的生产力,过去没有车的时候和有车的时候,你的活动半径、运行速度大大提高了,但是当每一个人都没有车的时候,你有车,就会形成竞争力。大数据也一样,你有大数据定制产品,别人没有,就会形成竞争力。

    在互联网大数据的时代,商家最后很可能可以针对每一个顾客进行精准的价格歧视。我们现在很多的行为都是比较粗放的,航空公司会给我们里程卡,根据飞行公里数来累计里程,但其实不同顾客所飞行的不同里程对航空公司的利润贡献是不一样的。所以有一天某位顾客可能会收到一封信,“恭喜先生,您已经被我们选为幸运顾客,我们提前把您升级到白金卡。”这说明这个顾客对航空公司的贡献已经够多了。有一天银行说“恭喜您,您的额度又被提高了,”就说明钱花得已经太多了。

    正因为在大数据规律面前,每个人的行为都跟别人一样,没有本质变化。所以商家会比消费者更了消费者的行为。也许你正在想,工作了一年很辛苦,要不要去哪里度假?打开e-Mail,就有航空公司、旅行社的邮件。

    说明:用定制产品思维方式思考问题,解决问题。大数据时代让企业找到了定制产品、订单生产、用户销售的新路子。用户在家购买商品已成为趋势,快递的快速,让用户体验到实时购物的快感,进而成为网购迷,个人消费不是减少了,反而是增加了。为什么企业要互联网化大数据化,也许有这个原因。2000万家互联网网店的出现,说明数据广告、数据传媒的重要性。

    企业产品直接销售给用户,省去了中间商流通环节,使产品的价格可以以出厂价销售,让销费者获得了好处,网上产品便宜成为用户的信念,网购市场形成了。要让用户成为你的产品粉丝,就必须了解用户需要,定制产品成为用户的心愿,也就成为企业发展的新方向。

    大数据思维是客观存在,大数据思维是新的思维观。用大数据思维方式思考问题,解决问题是当下企业潮流。大数据思维开启了一次重大的时代转型。

    摘自:虎嗅网 作者:蒋卫东

    展开全文
  • solaris下的tomcat遇到“java.io.IOException: 没有足够的空间”的问题[案例]:java.io.IOException: 没有足够的空间73017017 [发送央视命令线程] DEBUG cdm.GlobalCAHelper - -----------------生成摘要:925c10...

    solaris下的tomcat遇到“java.io.IOException: 没有足够的空间”的问题

    [案例]:
    java.io.IOException: 没有足够的空间
    73017017 [发送央视命令线程] DEBUG cdm.GlobalCAHelper  - -----------------生成摘要:925c10d20d5f0419f339aafd2a6c20a0  32
            at java.lang.UNIXProcess.forkAndExec(Native Method)
            at java.lang.UNIXProcess.<init>(UNIXProcess.java:52)
            at java.lang.Runtime.execInternal(Native Method)
            at java.lang.Runtime.exec(Runtime.java:566)
            at java.lang.Runtime.exec(Runtime.java:428)
            at java.lang.Runtime.exec(Runtime.java:364)
            at java.lang.Runtime.exec(Runtime.java:326)
            at com.onewaveinc.cetus.business.util.DVBUtil.runcmd(DVBUtil.java:1179)
            at com.onewaveinc.cetus.business.external.PreBillingTask.preBillingByAccountId(PreBillingTask.java:63)
            at com.onewaveinc.cetus.business.external.AppExternalBridge.preBillingByAccountId(AppExternalBridge.java:117)
            at com.onewaveinc.cetus.business.external.AppExternalBridge.preBillingByApp(AppExternalBridge.java:103)
            at com.onewaveinc.cetus.business.external.AppExternalBridge.sendMessage(AppExternalBridge.java:45)
            at com.onewaveinc.cetus.util.ldapaccess.send.LdapSendThread.preBilling(LdapSendThread.java:143)
            at com.onewaveinc.cetus.util.ldapaccess.send.LdapSendThread.process(LdapSendThread.java:92)
            at com.onewaveinc.cetus.util.ldapaccess.send.LdapSendThread.run(LdapSendThread.java:67)


    问题分析(此处是引用网络上的信息)
    在solaris环境上运行JAVA程序时,JVM的堆内存设置应小于总虚拟内存的50%,实际上最好小于30%,如果超过50%则系统中所有使用
    到Runtime.exec这种创建子进程java.lang.Process的操作都将失败。创建子进程会复制一份父进程的内存,如果交换内存不够则会出现
    IO异常:java.io.IOException: 没有足够的空间
    2007-11-12 15:58:03,126 ERROR [STDERR]  at java.lang.UNIXProcess.forkAndExec(Native Method)
    2007-11-12 15:58:03,127 ERROR [STDERR]  at java.lang.UNIXProcess.<init>(UNIXProcess.java:52)
    2007-11-12 15:58:03,128 ERROR [STDERR]  at java.lang.Runtime.execInternal(Native Method)
    2007-11-12 15:58:03,129 ERROR [STDERR]  at java.lang.Runtime.exec(Runtime.java:573)

    JDK相关故障为:5049299,4227230。
       http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5049299

    内容(此处是官方文档解释):
    Bug ID:  5049299 
    Votes  20 
    Synopsis  (process) Use posix_spawn, not fork, on S10 to avoid swap exhaustion 
    Category  java:classes_lang 
    Reported Against  b06 , 1.4.2_04 
    Release Fixed  
    State  In progress, request for enhancement 
    Related Bugs  6381152 , 4343908 , 4391042 
    Submit Date  18-MAY-2004 
    Description  If you run a "small" program (e.g., a Perl script) from a "big" Java process on
    a machine with "moderate" free swap space (but not as much as the big Java
    process), then Runtime.exec() fails.
     
    Work Around  1) mkfile followed by swap -a to add more swap space
    2) do Runtime.exec "early" in the application execution before the process has
       grown so large (i.e. so the transient swap requirement between Runtime.exec's    fork and exec calls is big), cache resulting Process object, then replace
       the "later" Runtime.exec calls that kicked off perl with println or the like     to direct the aforementioned process exec perl with the same command line
       and relay back the perl command's standard output and error traffic.
    3) Like (2) but spawn the "exec daemon" separate from Java to avoid any use of
       Runtime.exec and instead communicate with Java via a pipe or socket to
       initiate running the perl scripts.
       exit status.

      xxxxx@xxxxx   2004-05-19
     
    Evaluation  Solaris reserves swap space conservatively, so when an X-megabyte process
    forks the kernel attempts to reserve an additional X MB of swap space just in
    case the child actually does touch all those pages, thereby making private
    copies, and then later needs to swap them out.  (Linux doesn't do this, so
    this bug will not be reproducible on a Linux system.)

    Within the constraints of the existing semantics of Runtime.exec there does
    not appear to be any way to avoid this in current Solaris releases.  vfork(2)
    is not thread-safe and popen(3C) only provides access to one of the child's
    standard streams rather than all three of them.  S10 does support the new
    posix_spawn call; we should look into using that when running on S10.

    See the comments section for additional information.

    --   xxxxx@xxxxx   2004/5/19
    I agree that the use of posix_spawn on S10 should be investigated.
    Historically, changes to this kind of code has been extraordinarily risky
    due to unforseen race conditions, so this sort of change should be introduced
    near the beginning of a release.  Therefore I am targeting this at dolphin.
    Hopefully, it will get addressed early in that release.
    Posted Date : 2006-02-06 18:03:11.0

     

    我的分析:
    根据以上的描述,可知是由于交换分区不够导致的;
    由于solaris沿用了老的分区方式,对swap默认分区还是以旧的分区规则进行分配大小。
    linux已经改正了这个bug

    增加交换分区命令:
    mkfile 500m swapfile
    swap -a swapfile


    其他相关命令:
    swap -l: 列出当前交换分区情况
    swap -a /data/swapfile: 添加交换分区文件,注意:后面跟的文件名需要以绝对路径来执行。

     

     

     

     

     


    相关参考文档: http://developer.e800.com.cn/articles/2007/11/1167665124017589497_1.html

    Solaris性能监控的Swap空间管理

     随着电子商务如火如荼的开展,网站服务器的性能变得尤其重要。一旦服务器的能力不能满足用户的需要,就会对用户的服务大打折扣,那么就需要对服务器进行升级扩容。但是,有些时候只需对服务器进行一些适当的性能调整,便可以越过性能的瓶颈,大大提高服务器的吞吐能力,从而减少服务器升级的费用。

      本文介绍了在Solaris平台上Swap(交换)空间的基本概念、实现的原理以及对Swap(交换)空间进行监控的方法和调整的策略。

      什么是SWAP(交换)空间

      对于一般的Solaris系统管理员来说,很少会接触Swap(交换)空间,在他们看来Swap区只不过是磁盘上的一两个分区或是几个Swap(交换)文件,当系统没有足够的物理内存来处理当前进程的时候,就利用Swap(交换)空间作为虚拟内存的临时存储空间,这种说法从技术角度来说是没有错的,但Solaris在实现Swap时有其非常独特的地方。

      SWAP空间作用

      众所周知,现代操作系统都实现了“虚拟内存”这一技术,不但在功能上突破了物理内存的限制,使程序可以操纵大于实际物理内存的空间,更重要的是“虚拟内存”是隔离每个进程的安全保护网,使每个进程不受其他程序的干扰。

      Swap空间的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中。这样,系统总是在物理内存不够时,才进行Swap交换。这种现象对于计算机使用者是经常遇到的。

      有一点要声明的是,并不是所有从物理内存中交换出来的数据都会被放到Swap中(如果这样的话,Swap会不堪重负),有相当一部分的数据直接交换到文件系统。例如,有的程序会打开一些文件,对文件进行读写(其实每个程序都至少打开一个文件,那就是运行程序本身),当这些程序的内存空间需要交换出去时,文件部分的数据就没有必要放到Swap空间中了,如果是读文件操作,那么内存数据直接就释放了,不需要交换出来,因为下次需要时,直接从文件系统就能恢复;如果是写文件,只需要将变化的数据保存到文件中,以便恢复。但是那些用malloc(3C)和new函数生成的对象的数据则不同,需要Swap空间,因为它们在文件系统中没有相应的“储备”文件,因此被称为“匿名”(Anonymous)的内存数据,这类数据还包括堆栈中的一些状态和变量数据等,所以说,Swap空间是“匿名”数据的交换空间。

      Swap的配置对性能的影响

      太多的Swap空间会浪费磁盘的空间,而太少的Swap空间,系统则会发生错误。

      如果系统的物理内存用光了,你的系统就会跑得慢,但仍能运行;如果Swap空间用光了,那么系统就会发生错误。例如,Web服务器能根据不同的请求数量衍生出多个服务进程(或线程),如果Swap空间用完,则服务进程无法进动,通常会出现"application is out of memory"的错误,严重时会造成服务进程的死锁。因此Swap空间的分配是很重要的。

      通常情况下,Swap空间应大于或等于物理内存的大小,最小不应小于64M,通常Swap空间的大小应是物理内存的2-2.5倍(Solaris 2以上的版本有所变化,见下文)。但根据不同的应用,应有不同的配置:如果是小的桌面系统,只需要较小的Swap空间,而大的服务器系统则视情况不同需要不同大小的Swap空间。特别是数据库服务器和Web服务器会随着访问量的增加,对Swap 空间的要求也会增加,具体配置参见各自服务器产品的说明。

      另外,Swap分区的数量对性能也有很大的影响。因为Swap交换的操作是磁盘I/O的操作,如果有多个Swap交换区,Swap空间的分配会以轮流的方式操作于所有的Swap,这样会大大均衡I/O的负载,加快Swap交换的速度。如果只有一个交换区,所有的交换操作会使交换区变得很忙,使系统大多数时间位于等待状态,效率很低,用性能监视工具就会发现,此时的CPU并不很忙,而系统却慢,这说明,瓶颈在I/O上,依靠提高CPU的速度是解决不了问题的。


      性能监视

      Swap空间的分配固然很重要,而系统在运行时的性能监控却更加有价值,通过性能监视工具可以检查系统的各项性能指标,找到系统性能的瓶颈。本文只介绍一下在Solaris下和Swap相关的一些命令和用途。

      最常用的是Vmstat命令,在大多数Unix平台下都有此命令,此命令可以查看大多数性能的指标。

      另外使用swap -s 也能简单的查看当前swap资源的使用情况。例如:
      # swap -s
      total: 65896k bytes allocated + 56840k reserved = 122736k used,   1069456k available

      能够方便的看出swap空间的已用和未用资源的大小。应该使Swap保持30%的负载以下,才能保证系统的良好性能。

      Solaris中Swap的特点

      虚拟Swap空间

      本来Swap空间就是为虚拟内存服务的,现在Solaris的Swap空间也成为虚拟,这到底是怎么回事呢?

      让我们看一个例子就明白了,当在Solaris 2以前版本的Solaris(或其它Unix, 如Linux)上编程时经常会出现一个问题:

      假设系统当前还有可用的内存空间为30M,而只剩下10M的Swap空间了,这时,如果有一个进程开始运行并企图执行Malloc(15*1024*1024)的命令(分配15M空间),这个进程会因为这个命令而失败。

      为什么呢?系统不是有30M可用的内存空间吗?原因在于:你的Swap空间不足,系统认为你在分配空间以后,没有能力(空间)在发生页面交换时,将这部分数据保存起来,因此认为你没有资格分配这块空间。这不是太不公平了吧!也许这15M空间根本不用交换,当前系统可是还有30M内存空间的富余啊!

      还有更不公平的呢?有些大型系统配备了海量的内存,1G或4G,配了这么多内存就是为了避免交换,提高运行速度,可是系统还要为这个系统分配并不需要的Swap空间,占用了大量磁盘资源。

      为了弥补这个缺陷,Sun为Solaris 2 以后的版本设计了虚拟Swap空间。所谓虚拟的Swap空间,概念其实很简单,swap空间再也不是单指硬盘的分区或文件。虚拟Swap空间包含两个部分:部分物理内存和传统上的Swap分区。经过适当的配置,可以使系统需要Swap空间时,先使用内存部分的swap空间,如果内存部分的swap空间不够,再使用磁盘部分的Swap空间。这样,也许你硬盘上的Swap空间很少得到使用了,甚至根本不需要Swap分区。

      Swap空间与TMPFS文件系统的关系

      你知道吗?虚拟Swap空间与 /tmp目录有相当大的关系。Sun在实现/tmp目录时,充分考虑了应用程序运行的效率。许多应用程序,特别是数据库服务都会频繁使用/tmp目录作为临时数据保存区,而Solaris将/tmp目录下的文件都放在内存中而不是硬盘里,这样会大大提高应用程序的效率。

      但是/tmp目录的空间是从系统虚拟空间里挤出来的,是虚拟Swap空间的一部分。如果说,你用完了/tmp空间,也就是用完了Swap空间,所以要小心监视系统的/tmp目录的使用情况,千万别用光了,否则系统会瘫痪!下面两点建议作为参考:

      1.在Mount /tmp目录时,使用(-o Size)选项来控制/tmp目录的大小。

      2.当使用编译器编译文件时,如果不想占用Swap空间,则用TMPDIR环境变量指向另外一个临时目录,而不是/tmp目录。

      有关Swap空间操作的系统命令

      增加Swap空间

      1.成为超级用户 $su - root

      2.创建Swap文件 #mkfile nnn[klblm] filename
      如:#mkfile 100m swapfile1

      3.激活Swap文件
      Swap文件必须以绝对路径来指定,filename指的是上一步创建的文件。

      4.现在新加的Swap文件已经起作用了,但系统重新启动以后,并不会记住前几步的操作。因此要在/etc/vfstab文件中记录文件的名字,和Swap类型,如:
    /path/filename - - Swap - no -

      5.效验Swap文件是否加上 /usr/sbin/swap -l

      删除多余的Swap空间

      1.成为超级用户

      2.使用swap -d 命令收回swap空间。
      #/usr/sbin/swap -d /path/filename

      3.编辑/etc/ufstab文件,去掉此Swap(交换)文件的实体。

      4.从文件系统中回收此文件。
      #rm swap-filename

      5.当然,如果此Swap(交换)空间不是一个文件,而是一个分区,则需创建一个新的文件系统,再挂接到原来的文件系统上。

     

     

    展开全文
  • IDEA入门级使用教程----怎么在用eclipse?

    万次阅读 多人点赞 2016-10-12 18:48:58
    上个月,idea的使用量...智能提示重构代码如果写的代码过于复杂,或者有更好的方式来替代写的代码,那么IDEA会给一个提示,告诉你还可以有更好的方式。如下图:这个是说使用了没有必要的装箱操作(什么是装箱
  • 能力陷阱》学习总结

    千次阅读 2019-05-28 22:20:10
    能力陷阱2.领导者的特征1).像桥梁一样连接不同的人或组织2).做一些“有远见”的事情3).提升影响力4).提升领袖气质3.改变的工作日程安排二、建立良好的人际关系网络1.人际交往陷阱2.评估的人际关系3.打造良好...
  • HR压价,说只值7K,该怎么回答?

    万次阅读 多人点赞 2020-03-24 16:35:23
    HR压价,说只值7K时,可以流畅地回答,记住,是流畅,不能犹豫。 礼貌地说:“7K是吗?了解了。嗯~其实我对贵司的面试官印象很好。只不过,现在我的手头上已经有一份11K的offer。来面试,主要也是自己对贵司...
  • 程序员调试能力提升

    千次阅读 2016-07-14 22:22:20
    在软件行业中,个人觉得每个Coder、Leader(那些了Leader以后就不需要Code的除外)都应该除了具有良好的编码能力以外,最为主要的就是Debug的能力要坚实。千万不要告诉我Debug工作是Tester和QA的事情,首先要...
  • 谈谈程序员解决问题的能力

    万次阅读 多人点赞 2017-03-25 12:36:47
    本想推些技术文章的,但写技术文章又很耗时,写得太浅显又没有技术含量,写多了恐怕大家也没耐心去看(不就是懒么,给自己找这么多借口)。公众号这么多,又能看的了多少呢?小巫这个公众号不会像某些网红那样每天...
  • 如何提升能力?给年轻程序员的几条建议

    万次阅读 多人点赞 2019-04-08 16:21:42
    当你报怨求职面试时又被问到各种无厘头的程序题时,有没有想过面试官也很无奈,因为他没有任何其他方法得知的Coding能力究竟如何。如果每一个程序员都有自己的作品,我想程序员的面试会简单许多。 重视沟通能力...
  • 该篇文章为转载,是对原作者系列文章的总汇加上标注。...或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为做了这个工作,但我觉得要作一个好的和professional的程序员,makefil...
  • Makefile经典教程(掌握这些足够)

    万次阅读 多人点赞 2012-07-31 15:28:42
    或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果想成为一个...
  • 的深度思考能力,是如何一步步被毁掉的?

    万次阅读 多人点赞 2018-01-12 16:00:09
    前阵子,喜茶风靡全国的时候; 有一次,经过一家商场,看到一家新开的喜茶,门口毫不意外地排着几十米的长队。 排队的人中,男女老少均有,居然还有不少穿着正装、提着公文包的白领。 有些明显赶时间,频频...
  • 快速提升编程能力的关键

    万次阅读 多人点赞 2020-02-27 19:50:22
    当你开始学习编程的时候身边的一切都开始发生变化。在Firehose,我们称其为编程的拐点时刻。在这个阶段之后,作为开发人员这一身份的行为将发生巨大的改变。达到这一拐点的过程就是...
  • C++ Primer太厚了。如果以前有基础,看Accelerated C++,没基础的话,看Essential ...C++ Primer当你拿到书的时候,就不想看了。女孩子的话,拿都拿不动。 Essential C++Lippman的小作,试图让快速进入C++世界,
  • 被空降到一个团队领导,会如何烧自己的三把火?   前言   领导当然是好事情,升官发财嘛! 然而许多人了领导没多久,要么自己逃了,要么被大领导赶走了。 业绩不好,被大领导赶走,这可以想象,...
  • 软件能力成熟度模型(CMMI)

    万次阅读 2019-04-30 16:27:39
    转载自:... 本章内容提要CMMI概述CMMI的成熟度等级及其过程域CMMI的应用PSP,TSP与CMMI第一节 CMMI概述CMMI( Capability Maturity Model Integration)即能力成熟度模型集成,由CMM (Ca...
  • 是怎么变自律的?

    万次阅读 多人点赞 2019-11-29 07:30:59
    很多小伙伴咨询我, “看别人坚持健身拥有好身材,我坚持不了咋办?...先写结论吧:自律是假相,要能找到自己的驱动器,而不是自律本身,有些自律,其实是没有任何意义的。记得点赞呀。 目录...
  • 程序员调试能力和相关书籍

    千次阅读 2007-11-22 21:52:00
    楼主vcleaner(我没大哥很久了.......)2005-05-23 09:09:42 在 VC/MFC / 基础类 提问在软件行业中,个人觉得每个Coder、Leader(那些了Leader以后就不需要Code的除外)都应该除了具有良好的编码能力以外,最为...
  • 面对纷繁复杂的路由器市场,是选择老牌路由厂商的路由,还是选择一些互联网出品的新品牌路由,对于消费者来说 真是一个头疼的问题!在这里就给大家普及一些路由器的常识,本文不设计到刷路由固件,手动改造路由...
  • 时候程序员面试得不好,可能是因为面试官不好(或者程序员太烂,或者面试官太烂,或者气场不合). 由于大多数企业都是粗糙管理,基本上处于蛮荒时代,管理混乱和低效. 但蛮荒创业时代有其特有的属性,必须先活下来再考虑...
  • 说出我的故事,献给正在迷茫的

    万次阅读 多人点赞 2019-06-19 19:18:24
    今天在翻看 Blink 的时候,可能正值毕业季,发现好多人陷入迷茫。于是放下本来亟待今日开发的项目任务,牺牲了每日常规做晚饭的时间,花了整整一天,思如泉涌,行云流水般一气呵成,写下了我的故事,希望能略尽绵薄...
  • 曾经在知乎关注了一个帖子,国外博士的能力真的比国内博士强吗? 因为有段日子没刷知乎了,今天打开一看,多了很多回答。 有一些回答肯定了本土博士的科研水平。比如: 反对@小马过河回答:用上课来区分国内外...
  • 大家好,我是高胜寒,本文是过关斩将的第1篇文章,过关斩将系列主要讨论面试中那些常被问到的高频问题...是否是被上家开除的,或者公司不曾挽留是否足够优秀) 的离职是否给上家公司带来了一些不好的影响 (.
  • 优秀互联网高级测试工程师应该具备的能力 概述 在之前写的互联网高级测试工程师...在实际的工作中,可能会遇到很多测试人员在测试功能模块的时候,一遇到问题,马上就来找开发,由开发来定位问题。测试人员发...
  • IT 行业加班到底有没有价值?

    万次阅读 多人点赞 2017-07-18 14:02:39
    之前网上流行这么一个段子:说一小伙去面试,面试官问到:才毕业一年,为什么简历上写着两年工作经验?小伙答:那多的一年经验是加班来的。段子归段子,不过也确实反映了 IT 行业加班严重的一种现象,很多人对...
  • 360技术能力测评

    千次阅读 2017-08-27 16:31:05
    前天晚上看到自己的状态可以进入下一轮进行技术能力测评,第一眼看这个标题的时候还以为这个笔试考察的内容会是和我应聘的岗位相关的技术笔试呢,不过当时看了一下考试时间是30分钟,还是有一点疑惑笔试时间30分钟太...
  • 手把手教写专利申请书/如何申请专利

    万次阅读 多人点赞 2008-12-10 10:46:00
    手把手教写专利申请书·如何申请专利   摘要 小前言 (一)申请前的准备工作  1、申请前查询  2、其他方面的考虑  3、申请文件准备 (二)填写专利申请系列文档  1、实际操作步骤  2、具体操作  3、经验...
  • 当你成为大龄码农时,会怎么找的出路?

    万次阅读 多人点赞 2018-04-27 00:00:00
    我们很多时候常常会带着幸存者偏差,就像那些不读书的坏同学最后都去老板去了,认为读书没有用,也总有大龄码农说,我就出去找工作随便找哇。这类就是幸存者偏差,人们总是喜欢把那些留下来的人,当成最终整体的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 273,056
精华内容 109,222
关键字:

当你还没有足够能力的时候