博客

  • Gulp使用ng-annotate

    Angular中声明注入依赖的方法有三种

    1. 数组注解
    2. $inject属性
    3. 隐式注释

    隐式注释虽然简单,但是在压缩代码的时候会出错,所以一般还是需要选用方法一或者方法二。

    样例如下:

    module.controller('controller', ['$scope', 'greeter', function($scope, greeter) {
      // ...
    }]);
    var controller = function($scope, greeter) {
      // ...
    }
    controller .$inject = ['$scope', 'greeter'];
    module.controller('controller ', controller );

    ng-annotate是专门处理这种情况的插件,因为对于注入的处理毕竟是为了程序运行,而不是开发逻辑需要,这种自动化交给插件比较快。

    安装插件以后只需要加一个@ngInject就行了。

    /* @ngInject */
    function Avengers(storageService, avengerService) {
        var vm = this;
        vm.heroSearch = '';
        vm.storeHero = storeHero;
    
    
        function storeHero() {
            var hero = avengerService.find(vm.heroSearch);
            storageService.save(hero.name, hero);
        }
    }
    

    ng-annotate会采用方案二生成需要的东西。

    Gulp配置样例如下:

    gulp.task('js', ['jshint'], function() {
        var source = pkg.paths.js;
        return gulp.src(source)
            .pipe(sourcemaps.init())
            .pipe(concat('all.min.js', {newLine: ';'}))
            .pipe(ngAnnotate({
                add: true
            }))
            .pipe(bytediff.start())
            .pipe(uglify({mangle: true}))
            .pipe(bytediff.stop())
            .pipe(sourcemaps.write('./'))
            .pipe(gulp.dest(pkg.paths.dev));
    });

    参考文档:

    https://docs.angularjs.org/guide/di

    http://bguiz.github.io/js-standards/angularjs/minification-and-annotation/

  • wiredep自动注入的条件

    Bower解决了前端依赖的获取问题,而Wiredep的初衷是为了解决前端依赖的自动引用问题。

    比如来个简单的例子,以Bootstrap为例,在html中添加

    <html>
    <head>
      <!-- bower:css -->
      <!-- endbower -->
    </head>
    <body>
      <!-- bower:js -->
      <!-- endbower -->
    </body>
    </html>

    那么在安装bootstrap以后我们会发现只有bootstrap.js被正常引用了,但是对应的css并没有。

    这是因为wiredep的工作原理是检查依赖的bower.json文件,并根据其中的main和dependencies来判断需要注入的文件和对应的依赖。而Bootstrap的bower.json文件如下:

    {
      "name": "bootstrap",
      "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
      "keywords": [
        "css",
        "js",
        "less",
        "mobile-first",
        "responsive",
        "front-end",
        "framework",
        "web"
      ],
      "homepage": "http://getbootstrap.com",
      "license": "MIT",
      "moduleType": "globals",
      "main": [
        "less/bootstrap.less",
        "dist/js/bootstrap.js"
      ],
      "ignore": [
        "/.*",
        "_config.yml",
        "CNAME",
        "composer.json",
        "CONTRIBUTING.md",
        "docs",
        "js/tests",
        "test-infra"
      ],
      "dependencies": {
        "jquery": "1.9.1 - 2"
      }
    }
    

    在main中有两个文件,一个js文件,一个less文件,这就是为什么js被注入了,而css没有被注入的原因,当然作为依赖的jquery在bower安装时已经被安装了,这里也会被注入。

    那么如果需要注入bootstrap.css怎么办?

    1. 手动引用dist目录下的css
    2. 利用自动注入将boostrap.less注入应用自己的less中一起编译

    再来看一下jquery-ui库,在安装之后也是只有js文件,没有对应的css。

    {
      "name": "jquery-ui",
      "version": "1.11.4",
      "main": [
        "jquery-ui.js"
      ],
      "ignore": [
      ],
      "dependencies": {
        "jquery": ">=1.6"
      }
    }
    

    可以看到在main中根本没有css,对应的css文件在theme目录中有很多可选主题样式,估计这也是为什么css没有被包含在main中的原因,这种情况手动引用比较简单。

    当然在依赖库的bower.json文件有问题或者不符合你项目要求的时候还可以自定义bower.json。因为wiredep库是可以自定义bower.json文件的,将自定义的bower.json作为项目代码控制的一部分提交,这样既可以保证bower依赖的独立性,也避免了手动引用。

    附上wiredep中自定义bower.json的对应代码

     config.set
        ('bower.json', opts.bowerJson || JSON.parse($.fs.readFileSync($.path.join(cwd, './bower.json'))))

     

  • Gulp inject插件的startTag和endTag

    最近接触了一些前端的东西,在Gulp之下有若干插件,配合起来对于新手而言简直神乎其神,难以理解。

    以gulp-inject为例,只需要在页面中添加

    <!DOCTYPE html>
    <html>
    <head>
      <title>My index</title>
      <!-- inject:css -->
      <!-- endinject -->
    </head>
    <body>
    
      <!-- inject:js -->
      <!-- endinject -->
    </body>
    </html>

    然后配合配置

    var gulp = require('gulp');
    var inject = require('gulp-inject');
    
    gulp.task('index', function () {
      var target = gulp.src('./src/index.html');
      // It's not necessary to read the files (will speed up things), we're only after their paths:
      var sources = gulp.src(['./src/**/*.js', './src/**/*.css'], {read: false});
    
      return target.pipe(inject(sources))
        .pipe(gulp.dest('./src'));
    });

    就可以自动注入js和css文件。

    对于写在html中的那些tag是如何解析的,如果需要自定义怎么办,我很好奇,所以稍微看了一下源代码,gulp-inject真的很小巧(不知道是不是node项目都是这样的)

    gulp-inject

    当传入文件列表之后,会对所有的文件进行分析(主要是文件类型),然后获得startAndEndTags,其中会根据文件后缀获取对应的类型tag

    var DEFAULTS = {
      STARTS: {
        'html': '<!-- {{name}}:{{ext}} -->',
        'jsx': '{/* {{name}}:{{ext}} */}',
        'jade': '//- {{name}}:{{ext}}',
        'slm': '/ {{name}}:{{ext}}',
        'slim': '/ {{name}}:{{ext}}',
        'haml': '-# {{name}}:{{ext}}',
        'less': '/* {{name}}:{{ext}} */',
        'sass': '/* {{name}}:{{ext}} */',
        'scss': '/* {{name}}:{{ext}} */'
      },
      ENDS: {
        'html': '<!-- endinject -->',
        'jsx': '{/* endinject */}',
        'jade': '//- endinject',
        'slm': '/ endinject',
        'slim': '/ endinject',
        'haml': '-# endinject',
        'less': '/* endinject */',
        'sass': '/* endinject */',
        'scss': '/* endinject */'
      }
    };

    这里是默认的定义,比如html文件中的js和css就是

    <!-- inject:js -->
    <!-- inject:css -->

    而结尾是不区分类型的,都是<!– endinject –>

    这也是为什么例子中的tag能够工作的原因。

    如果需要自定义tag,只需要在运行的opt中传入即可,具体的在这里

    var startTag = opt.tags.start(targetExt, ext, opt.starttag);
    var endTag = opt.tags.end(targetExt, ext, opt.endtag);
  • Gulp获取Jenkins Build Number

    Spring Boot配合nebula插件可以很轻易获取Jenkins的相关信息并暴露出来。

    如果一个纯前端的项目,那么相关信息就需要在自己收集以后在build的时候写入一个地方,比如将buildNumber写入info.json中。

    var file = require('gulp-file');
    var path = require('path');
    gulp.task('generateInfo', function() {
      var buildNumber = process.env.BUILD_NUMBER || 'local';
      var info = {
        build: buildNumber
      };
      return file('info.json', JSON.stringify(info), [src: true])
        .pipe(gulp.dest(path.join(conf.path.dist,'/')));
    });

    这样项目在jenkin上构建的时候就会生成info.json文件。

  • Spring Boot指定active profile的几种方法

    Spring Boot提供了分割配置的一些方法,不再需要把常常的配置写在一个文件中,而是拆分成诸如data,mvc,backend等对应的配置,这个特性也可以用在区分环境上,比如data-dev,data-dryrun,data-pro。

    Spring Boot提供了灵活的指定Profile的方法,比如注解

    @Configuration
    @Profile("production")
    public class ProductionConfiguration {
    
        // ...
    
    }

    直接写在需要读取配置的对应类上。

    当然我觉得更多的人可能倾向于在主配置文件中引用其他配置文件

    spring.profiles.active=dev,mysql

    当然,也可以用程序去解析,比如一些CI的情况下,你可能会使用特定的环境和配置,这样就需要在代码中判断

    SpringApplication.setAdditionalProfiles(...)

     

  • Java项目中混合Scala

    虽然我并不怎么用Scala,但是经常接触到一些Scala的开源库。由于Scala本身的特性,所以对于使用者而言,懂不懂Scala并不重要。

    Spark是由Scala编写,可以只用Java调用,但是有时候需要自定义其中的一些组件的时候可能Java并不能做到,这个时候就需要写一些Scala的代码。

    原有的项目是Gradle管理的,而Gradle本身提供了对于Scala的支持,简单来看看Gradle Scala插件。在build.gradle中添加两行

    apply plugin: 'scala'
    ...
    compile 'com.databricks:spark-csv_2.11:1.4.0'

    然后在目录结构中添加一个scala,位置在这里scala-project

    然后直接开始写就行了,不得不说IDEA和Gradle工作的都很好,运行还是直接运行,其他什么都不用改,直接运行就行了。

    scala-spark

    参考资料

    https://docs.gradle.org/current/userguide/scala_plugin.html

  • 领教了百度全家桶的牛逼之处了

    之前一直听说百度全家桶多么牛逼,一直没有放在心上。今天算是领教了,真是厉害。

    下午的时候正在打战争雷霆,之前一直是直连的,但是不知道为什么,速度很慢,ping很大,就寻思着找个游戏加速器。然后看看百度有加速器,就下来试试,结果它包含在一个叫百度桌面的东西里面,想想也没多大事情,安装来试试。

    结果试了一下加速器完全没用,加速以后连登录都不行,准备卸载,卸载也很顺利,没有什么问题,把所有有关百度的都卸载了。

    然后,然后

    然后问题就来了,上不起网了。。。

    重启再试,开机以后进入桌面,起码20个程序弹出报错框,用360修复了LSP,可以上网了。但是输入法不见了!没有切换输入法,也看不见图标,有一个占位符在那里,但是点不动。

    好吧,再重启,依然无效。

    最后在网上研究了很久,居然都不行,输入法就不见了。。。

    只能说百度的工程师真厉害,专心做黑产多好。

  • Spring Boot在Docker中集成oneapm

    Oneapm是一个不错的监控平台,提供很多监控,从系统层面到具体的应用层面。

    Oneapm有Spring Boot application的支持,今天试着集成了一下。

    因为应用是在Docker中的,所以具体的集成需要修改Dockerfile。首先是配置自己的oneapm.properties文件。然后将文件放在项目中。

    修改Dockerfile,先下载oneapm.zip(下载地址登录oneapm后台可以看到)。然后将自己项目中准备的配置文件覆盖掉默认的,然后在启动参数中添加,具体配置如下

    RUN wget https://user.oneapm.com/account/{你自己的地址}/agent/java/OneAPM_java_Agent_latest.zip
    RUN apk add --no-cache unzip
    RUN unzip OneAPM_java_Agent_latest.zip -d /
    ADD ./.build/oneapm.properties /OneAPM/
    
    ENTRYPOINT ["java,"-javaagent:/OneAPM/oneapm.jar","-Djava.security.egd=file:/dev/./urandom","-Dserver.port=8080","-jar","/app.jar"]

    重新部署以后更新应用,很快就可以看到数据

    oneapm

  • Spring Boot和Alpine Linux

    今天突然收到阿里云的短信,提示硬盘使用率超标,感觉特别奇怪。

    因为机器上只有两个数据库和一点应用,所有的资源啥的都放在七牛上的,不应该硬盘不够用才对。

    仔细看了一下发现是docker把空间吃了,速度把一些没用的image等等清理一番,硬盘使用率瞬间降到53%。

    机器上所有应用都是spring boot的,也就是java8环境的,之前使用的Docker Image基于ubuntu构建,大小500M+以上,突然想起之前听闻过Alpine Linux,小巧方便,说不定可以减少镜像大小。

    在网上找了一个找了一个基础镜像

    frolvlad/alpine-oraclejdk8:slim

    Dockerfile改造为

    FROM frolvlad/alpine-oraclejdk8:slim
    VOLUME /tmp
    ADD spring-boot-application.jar app.jar
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

    如果需要在镜像中构建,编译的话可能还需要bash支持

    FROM frolvlad/alpine-oraclejdk8:slim
    VOLUME /tmp
    RUN apk add bash
    RUN ./gradlew build -x test
    ADD spring-boot-application.jar app.jar
    RUN apk del bash
    RUN rm -rf /root/.gradle/
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

    镜像大小可以节约200M+。

  • Travis CI更细致的配置

    Travis CI是一个很好的免费CI,和Github搭配也很合适,但是对于多Env的项目在具体的配置上有很多可以更细致处理的地方。

    第一个是coverage的检测,默认情况下一句简单的命令可以让它工作

    after_success:
      - ./gradlew cobertura coveralls

    但是这种情况,所有的Env都会上传测试覆盖率数据,虽然coveralls等平台会自己处理,但是还是希望能够节约时间,只上传一份。

    很多情况下我们都会配置每次代码更新就发布一个snapshot版本,对于多Env的情况,如果没有特殊处理,就会出现一次commit,发布多个版本的情况。

    Travis CI中有很多环境变量,有默认的,有和当前构建有关的,也有用户自己定义的,可以依靠这些变量做出判断,决定是否执行相关操作。

    比如在.travis.yml中添加

    after_success:
     - .travis/coverage.sh
     - .travis/deploy.sh

    然后对应的bash脚本中做判断

    #!/bin/bash
    
    if [[ $TRAVIS_PULL_REQUEST == 'false' && $TRAVIS_REPO_SLUG == 'varFamily/cocos-ui-libgdx' && $GDX_VERSION == '1.9.2' && $TRAVIS_BRANCH == 'master' ]];
    then
        echo 'Upload snapshot to maven central'
        ./gradlew uploadArchives
    fi

    条件是可以自己定义,我这里写的是针对发布snapshot的。pull request不发布,只有在主项目的主分支的对应Env才发布。