Bower提供了一个静态资源的共享平台。安装之前你必须要有node环境。
https://nodejs.org/en/ ,执行命令:

1
2
$ node -v
v6.4.0

Bower是命令行工具,通过npm安装,如果你觉得官网的下载比较慢,可以考虑使用淘宝提供的镜像进行下载:

1
$ npm install -g bower

检查bower是否安装成功:

1
2
$ bower -v
1.7.9

接下来对项目进行初始化操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
E:\bower\cjoop-ad-angular>bower init
? name cjoop-ad-angular
? description AngularJS module for ad
? main file
? keywords
? authors cjjava <85309651@qq.com>
? license MIT
? homepage https://github.com/cjjava/cjoop-ad-angular
? set currently installed components as dependencies? Yes
? add commonly ignored files to ignore list? Yes
? would you like to mark this package as private which prevents it from being ac
cidentally published to the registry? No

{
name: 'cjoop-ad-angular',
authors: [
'cjjava <85309651@qq.com>'
],
description: 'AngularJS module for ad',
main: '',
license: 'MIT',
homepage: 'https://github.com/cjjava/cjoop-ad-angular',
ignore: [
'**/.*',
'node_modules',
'bower_components',
'test',
'tests'
]
}

? Looks good? Yes

安装我们项目需要使用的js组件:

1
bower install angular --save

如果我们的项目是作为一个组件使用的话,可以发布到bower仓库中,首先进行注册操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ bower register cjoop-ad-angular git://github.com/cjjava/cjoop-ad-angular.git
bower resolve git://github.com/cjjava/cjoop-ad-angular.git#*
bower checkout cjoop-ad-angular#master
bower resolved git://github.com/cjjava/cjoop-ad-angular.git#0909515b4f
? Registering a package will make it installable via the registry (https://bower
.herokuapp.com), continue? Yes
bower register git://github.com/cjjava/cjoop-ad-angular.git

Package cjoop-ad-angular registered successfully!
All valid semver tags on git://github.com/cjjava/cjoop-ad-angular.git will be av
ailable as versions.
To publish a new version, just release a valid semver tag.

Run bower info cjoop-ad-angular to list the available versions.

完成注册后,我们就可以在其他项目中使用bower命令下载组件了:

1
bower install cjoop-ad-angular

再也不用担心你的组件掉了吧。

Fork me on GitHub

这篇文章描述SpringSecurity的跨域认证,通过前后端分离进行登陆认证,跨域调用资源。适合开发阶段使用的方式。
环境准备

  • jdk1.7+
  • Gradle2.8
  • IntelliJ 15 or eclipse
  • Redis

创建项目

通过idea创建spring-security-cors顶层模块,cors-resource服务端模块,cors-ui前端模块.目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
│  build.gradle
│ settings.gradle
├─cors-resource
│ │ build.gradle
│ └─src
│ ├─main
│ │ ├─java
│ │ └─resources
│ └─test
│ ├─java
│ └─resources
└─cors-ui
│ build.gradle
└─src
├─main
│ ├─java
│ └─resources
└─test
├─java
└─resources

顶层模块settings.gradle配置子模块信息:

1
include "cors-resource", "cors-ui"

build.gradle配置通用信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
buildscript {
ext {
springBootVersion = '1.4.0.RELEASE'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

subprojects{
group 'com.cjoop'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'

sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
}

跨域认证配置

通过Spring Security4.1.x提供的跨域支持进行配置,这里的认证结果进行了简单处理,在认证成功后返回sessionId给客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.cjoop.cors.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import com.cjoop.cors.handler.AuthenticationFailureHandler;
import com.cjoop.cors.handler.AuthenticationSuccessHandler;
import com.cjoop.cors.handler.LogoutSuccessHandler;

@Configuration
@EnableWebSecurity
public class SpringSecurityAutoConfiguration extends WebSecurityConfigurerAdapter{
@Autowired
@Qualifier("authenticationSuccessHandler")
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
@Qualifier("authenticationFailureHandler")
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
@Qualifier("logoutSuccessHandler")
private LogoutSuccessHandler logoutSuccessHandler;

/**
* 设置表单登陆信息
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
String loginProcessingUrl = "/j_spring_security_check";
//判断当前运行模式(开发阶段)添加跨域认证允许
http.cors();//主要是这行代码完成了跨域认证的支持
http.csrf().disable()
.authorizeRequests()
.antMatchers("/index.html", "/index.js","/","/favicon.ico").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl(loginProcessingUrl)
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.logout().logoutSuccessHandler(logoutSuccessHandler)
.permitAll();
}
}

通过Spring MVC提供的跨域支持进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.cjoop.cors.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebMvcAutoConfiguration extends WebMvcConfigurerAdapter{
@Override
public void addCorsMappings(CorsRegistry registry) {
//判断当前运行模式(开发阶段)添加跨域允许
registry.addMapping("/**")
.allowedHeaders("x-requested-with","x-auth-token","content-type")
.maxAge(3600)
.allowedOrigins("*")
.allowCredentials(true);
}
}

认证完成后我们的sessionId是被持久化到了redis中,每一次浏览器调用资源请求都会携带X-Auth-Token和X-Requested-With两个头信息。这里我们实现一个跨域的会话策略CorsHttpSessionStrategy,这个策略合并了CookieHttpSessionStrategy和HeaderHttpSessionStrategy,以便提供本地策略和跨域策略的支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.cjoop.cors.session;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.session.Session;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.HeaderHttpSessionStrategy;
import org.springframework.session.web.http.MultiHttpSessionStrategy;

/**
* 合并HeaderHttpSessionStrategy和CookieHttpSessionStrategy2种策略,该策略仅作开发时候使用
* @author 陈均
*
*/
public class CorsHttpSessionStrategy implements MultiHttpSessionStrategy{
CookieHttpSessionStrategy cookieHttpSessionStrategy = new CookieHttpSessionStrategy();
HeaderHttpSessionStrategy headerHttpSessionStrategy = new HeaderHttpSessionStrategy();

@Override
public String getRequestedSessionId(HttpServletRequest request) {
String sessionId = headerHttpSessionStrategy.getRequestedSessionId(request);
if(null==sessionId){
sessionId = cookieHttpSessionStrategy.getRequestedSessionId(request);
}
return sessionId;
}

@Override
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
headerHttpSessionStrategy.onNewSession(session, request, response);
cookieHttpSessionStrategy.onNewSession(session, request, response);
}

@Override
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
headerHttpSessionStrategy.onInvalidateSession(request, response);
cookieHttpSessionStrategy.onInvalidateSession(request, response);
}

@Override
public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
return cookieHttpSessionStrategy.wrapRequest(request, response);
}

@Override
public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
return cookieHttpSessionStrategy.wrapResponse(request, response);
}
}

项目cors-resource(application.properties)提供一个账户信息user来进行登陆,服务端口设置为9000:

1
2
server.port=9000
security.user.password=password

前端代码的准备

cors-ui项目中创建首页内容index.html,用来展示后端请求的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html ng-app="spring-security">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style type="text/css">
.container{
padding-top: 50px;
}
</style>
</head>
<body ng-controller="IndexController">
<div class="container">
<div class="row">
<h3>Spring Security 跨域调用</h3>
</div>
<div class="row">
<div class="col-md-12">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="username" class="col-md-2 control-label">Username:</label>
<div class="col-md-6">
<input type="text" class="form-control" id="username" name="username" ng-model="credentials.username"/>
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-2 control-label">Password:</label>
<div class="col-md-6">
<input type="password" class="form-control" id="password" name="password" ng-model="credentials.password"/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<a class="btn btn-primary" ng-click="resource()">resource</a>
<a class="btn btn-primary" ng-click="login()">login</a>
<a class="btn btn-primary" ng-click="logout()">logout</a>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<span ng-bind="message" style="color:red;"></span>
</div>
<div class="row">
<h1>Greeting</h1>
<div>
<p>The ID is {{greeting.id}}</p>
<p>The content is {{greeting.content}}</p>
<p>The sessionid is {{greeting.sessionId}}</p>
<p>The username is {{greeting.user.username}}</p>
</div>
</div>
</div>

<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.5.8/angular.min.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.5.8/angular-cookies.min.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="index.js"></script>
</body>
</html>

index.js通过ajax认证获取sessionId保存到cookies中,这样在刷新页面以后也能够获取到sessionId,每一个资源访问的头信息中都会携带X-Auth-Token和X-Requested-With。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(function(angular,$){
'use strict';
window.contextPath = "http://localhost:9000";
angular.module('spring-security', ['ngCookies'])
.run(['$http', '$cookies', function($http, $cookies) {
$http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$http.defaults.headers.common['X-Auth-Token'] = $cookies.get('X-Auth-Token');
}])
.controller('IndexController', ['$scope','$http','$cookies',function($scope,$http,$cookies) {
$scope.credentials = {};
$scope.login = function(){
return $http.post(contextPath+"/j_spring_security_check",null,{
params: $scope.credentials
}).then(function (resp) {
var result = resp.data;
$scope.message = result.text;
if(result.code == 0){//save cookie
$cookies.put('X-Auth-Token',result.data);
$http.defaults.headers.common['X-Auth-Token'] = result.data;
}
});
}
$scope.logout = function() {
$http.post(contextPath+'/logout', {}).then(function(resp){
$scope.message = resp.data.text;
$scope.greeting = {};
});
}
$scope.resource = function(){
$http.get(contextPath+'/resource').then(function(resp) {
if(resp.data.code==-500){
$scope.message = resp.data.text;
}
$scope.greeting = resp.data;
});
}
}]);
})(angular,jQuery);

测试程序

启动redis服务,执行命令 gradle bootRun 运行cors-resource和cors-ui两个项目,访问地址http://localhost:9000 体验本地调用效果,访问地址http://localhost:8080/ 体验跨域调用效果。 ,输入用户信息进行登陆,然后调用resource服务,结果如图:

以上就是结合Spring Security进行前后端分离开发时候的调用思路。
Fork me on GitHub

创建项目

通过idea创建一个gradle项目spring-security-basic。
build.gradle配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
group 'com.cjoop'
version '1.0-SNAPSHOT'

buildscript {
ext {
springBootVersion = '1.3.5.RELEASE'
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath 'org.springframework:springloaded:1.2.6.RELEASE'
}
}

apply plugin: 'maven'
apply plugin: 'idea'
apply plugin: 'spring-boot'

sourceCompatibility = 1.7
targetCompatibility = 1.7

idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
testOutputDir = file("$buildDir/classes/test/")
}
}

repositories {
mavenLocal()
jcenter()
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-security")
testCompile("org.springframework.boot:spring-boot-starter-test")
}

application.properties配置中添加认证用户的密码信息:

1
security.user.password=password

创建启动类Application:

1
2
3
4
5
6
7
8
9
10
11
package com.cjoop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}

创建控制类IndexController:

这里我们提供一个简单的API进行访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.cjoop.com.cjoop.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
* Created by chenjun on 2016-08-19.
*/
@RestController
public class IndexController {
@RequestMapping("/resource")
public Map<String,Object> home(){
Map<String,Object> model = new HashMap<String,Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
}

后端的程序已经准备完成。

前端代码的准备

创建首页内容index.html,用来展示后端的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello AngularJS</title>
<link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style type="text/css">
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
</style>
</head>
<body ng-app="hello">
<div class="container">
<h1>Greeting</h1>
<div ng-controller="home as home" ng-cloak class="ng-cloak">
<p>The ID is {{home.greeting.id}}</p>
<p>The content is {{home.greeting.content}}</p>
</div>
</div>

<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.5.8/angular.min.js" type="text/javascript"></script>
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="js/hello.js"></script>
</body>
</html>

hello.js负责请求后端的数据。

1
2
3
4
5
6
angular.module('hello', []).controller('home', function($http) {
var self = this;
$http.get('resource/').then(function(response) {
self.greeting = response.data;
})
});

测试程序

执行命令 gradle bootRun,访问地址http://localhost:8080/ ,要求你输入身份验证信息,用户名默认为user,密码为配置文件里设置的password,打开浏览器的调试面板,看到Rquest Headers包含有认证信息:

1
Authorization:Basic dXNlcjpwYXNzd29yZA==

使用curl命令去访问/resource请求,得到以下结果:

1
2
3
4
$ curl http://localhost:8080/resource
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 150 0 150 0 0 1376 0 --:--:-- --:--:-- --:--:-- 1612{" timestamp":1471596710281,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/resource"}

没有权限不能访问/resource,我们带上认证信息重试,访问成功:

1
2
3
4
$ curl -u user:password http://localhost:8080/resource
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 69 0 69 0 0 2225 0 --:--:-- --:--:-- --:--:-- 4600{"content":"Hello World","id":"5a1f806f-b2d3-4545-9bde-b1596f69bb96"}

还可以把浏览器产生的头信息带上进行访问,也成功。

1
2
3
4
$ curl -H "Authorization:Basic dXNlcjpwYXNzd29yZA==" http://localhost:8080/resource
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 69 0 69 0 0 2225 0 --:--:-- --:--:-- --:--:-- 4600{"content":"Hello World","id":"61114583-6c61-491b-ae42-1400133b8aa5"}

以上就是最简答的请求认证方式。

现代IDEs(Eclipse, IDEA等)都支持字节码的热交换,所以如果你做了一个没有影响类或方
法签名的改变,它会利索地重新加载并没有任何影响。
Spring Loaded在这方面走的更远,它能够重新加载方法签名改变的类定义。如果对它进行一
些自定义配置可以强制ApplicationContext刷新自己(但没有通用的机制来确保这对一个运行
中的应用总是安全的,所以它可能只是一个开发时间的技巧)。
1.假设我有一个控制类SimpleController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.cjoop.com.cjoop.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* Created by chenjun on 2016-08-15.
*/
@Controller
public class SimpleController {

@RequestMapping("/")
@ResponseBody
public String home(){
return "Hello World!";
}
}

2.当我们启动了这个web项目后(一直保持启动状态),我想把SimpleController类的home方法改成这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.cjoop.com.cjoop.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* Created by chenjun on 2016-08-15.
*/
@Controller
public class SimpleController {

@RequestMapping("/")
@ResponseBody
public String home(){
return say("cjoop");
}

public String say(String name){
return "Hello World!" + name;
}
}

如果你使用的是IDEA,执行Ctrl+Shift+F9,重新编译该文件,然后访问地址http://localhost:8080/ ,你会发现输出的结果还是Hello World!,没有重新加载,那我们就得重新启动服务器。
Spring给我们提供了一个热交换的功能,就是springloaded,maven工程添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
</plugin>

gradle工程添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
group 'com.cjoop'
version '1.0-SNAPSHOT'

buildscript {
ext {
springBootVersion = '1.3.5.RELEASE'
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath 'org.springframework:springloaded:1.2.6.RELEASE'
}
}

apply plugin: 'maven'
apply plugin: 'idea'
apply plugin: 'spring-boot'

sourceCompatibility = 1.7
targetCompatibility = 1.7

idea {
module {
inheritOutputDirs = false
outputDir = file("$buildDir/classes/main/")
testOutputDir = file("$buildDir/classes/test/")
}
}

repositories {
mavenLocal()
jcenter()
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}

再次运行项目,重复之前步骤1和2,结果变成了Hello World!cjoop,这就是springloaded不一样的体验。动手自己体验吧(^-^)

1.Spring Boot介绍

Spring Boot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只需”just run”。
我们为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数
Spring Boot应用需要很少的Spring配置。
你可以使用Spring Boot创建Java应用,并使用 java -jar 启动它或采用传统的war部署方式。
我们也提供了一个运行”spring脚本”的命令行工具。
我们主要的目标是:

  • 为所有的Spring开发提供一个从根本上更快的和广泛使用的入门经验。
  • 开箱即用,但你可以通过不采用默认设置来摆脱这种方式。
  • 提供一系列大型项目常用的非功能性特征(比如,内嵌服务器,安全,指标,健康检
    测,外部化配置)。
  • 绝对不需要代码生成及XML配置。

2.环境准备

  • jdk1.7+
  • Gradle2.8
  • IntelliJ 15

3.第一个项目

打开IntelliJ,New Project,选择Gradle项目(Java)。填写项目信息:
GroupId com.cjoop
ArtifactId spring-boot-simple
version 1.0-SNAPSHOT
选择项目保存的路径,Finish。项目下面产生2个文件settings.gradle和build.gradle。src目录是一个标准的maven工程目录。
修改build.gradle文件内容,这里我使用的是1.3.5.RELEASE版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
group 'com.cjoop'
version '1.0-SNAPSHOT'

buildscript {
ext {
springBootVersion = '1.3.5.RELEASE'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'spring-boot'

sourceCompatibility = 1.6
targetCompatibility = 1.6

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}

创建控制类SimpleController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.cjoop.com.cjoop.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* Created by chenjun on 2016-08-15.
*/
@Controller
public class SimpleController {

@RequestMapping("/")
@ResponseBody
public String home(){
return "Hello World!";
}
}

创建启动类Application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.cjoop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Created by chenjun on 2016-08-15.
*/
@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}

运行Application,控制台输入以下信息(最后几行):

1
2
3
4
5
INFO 9140 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
INFO 9140 --- [ main] com.cjoop.Application : Started Application in 8.073 seconds (JVM running for 9.472)
INFO 9140 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
INFO 9140 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
INFO 9140 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 36 ms

访问http://localhost:8080 可以看到Hello World!内容。从开始到最后我们都没有进行配置文件的编写,可见SpringBoot给我们简化了很多工作。
配置好了spring-boot-gradle-plugin插件后,执行gradle build命令,你会发现build/libs下面生成了一个可执行的spring-boot-simple-1.0-SNAPSHOT.jar文件,通过命令就可以在运行该文件:

1
java -jar build/libs/spring-boot-simple-1.0-SNAPSHOT.jar

你还可以通过gradle bootRun运行项目。

Gradle User Guide

Version 2.14.1

I. 关于 Gradle

1.介绍

很高兴能向大家介绍 Gradle, 这是一个构建系统, 我们认为它是 java ( JVM ) 世界中构建技术的一个飞跃.
Gradle 提供了:

  • 一个像 Ant 一样的非常灵活的通用构建工具
  • 一种可切换的, 像 maven 一样的基于合约构建的框架
  • 支持强大的多工程构建
  • 支持强大的依赖管理(基于 ApacheIvy )
  • 支持已有的 maven 和 ivy 仓库
  • 支持传递性依赖管理, 而不需要远程仓库或者 pom.xml 或者 ivy 配置文件
  • 优先支持 Ant 式的任务和构建
  • 基于 groovy 的构建脚本
  • 有丰富的领域模型来描述你的构建

1.1关于这本指南

这本用户指南还并不完善,
就像 Gradle 一样还在开发当中.

在这本指南中,
Gradle 的一些功能并没有被完整的展示出来.
一些内容的解释也并不是十分的清楚,
或者假设关于 Gradle 你知道得更多.
我们需要你的帮助来完善这本指南.
在 Gradle 网站上你可以找到更多关于完善这本指南的信息.

通过这本指南,
你将会看到一些代表 Gradle 任务之间依赖关系的图表.
类似于 UML 依赖关系的表示方法,
从一个任务 A 指向另一个任务 B 的箭头代表A依赖于B.

2.概述

2.1特点

这里简述下 Gradle 的特点.

1. 声明式构建和合约构建
Gradle 的核心是基于 Groovy 的
领域特定语言 (DSL), 具有十分优秀的扩展性. Gradle 通过提供可以随意集成的声明式语言元素将声明性构建推到了一个新的高度. 这些元素也为 Java, Groovy, OSGi, Web 和Scala 等项目提供基于合约构建的支持. 而且, 这种声明式语言是可扩展的. 你可以添加自己的语言元素或加强现有的语言元素, 从而提供简洁, 易于维护和易于理解的构建.

2. 基于依赖的编程语言
声明式语言位于通用任务图 ( general purpose task graph ) 的顶端,它可以被充分利用在你的构建中. 它具有强大的灵活性, 可以满足使用者对 Gradle 的一些特别的需求.

3. 让构建结构化
Gradle 的易适应性和丰富性可让你在构建里直接套用通用的设计原则. 例如, 你可以非常容易地使用一些可重用的组件来构成你的构建. 但是不必要的间接内联内容是不合适的. 不要强行拆分已经结合在一起的部分 (例如, 在你的项目层次结构中). 避免使构建难以维护. 总之, 你可以创建一个结构良好,易于维护和易于理解的构建.

4. API深化
你会非常乐意在整个构建执行的生命周期中使用 Gradle, 因为Gradle 允许你管理和定制它的配置和执行行为.

5. Gradle 扩展
Gradle 扩展得非常好. 不管是简单的独立项目还是大型的多项目构建, 它都能显著的提高效率.
这是真正的结构构建. 顶尖水平的构建功能,还可以解决许多大公司碰到的构建 性能低下的问题.

6. 多项目构建
Gradle 对多项目的支持是非常出色的. 项目依赖是很重要的部分. 它允许你模拟在多项目构建中项目的关系,这正是你所要关注的地方. Gradle 可以适应你的项目的结构, 而不是反过来.

Gradle 提供了局部构建的功能. 如果你构建一个单独的子项目, Gradle 会构建这个子项目依赖的所有子项目. 你也可以选择依赖于另一个特别的子项目重新构建这些子项目. 这样在一些大型项目里就可以节省非常多的时间.

7. 多种方式来管理你的依赖
不同的团队有不同的管理外部依赖的方法. Gradle 对于任何管理策略都提供了合适的支持. 从远程 Maven 和 Ivy 库的依赖管理到本地文件系统的 jars 或者 dirs.

8. Gradle 是第一个构建整合工具
Ant 的 tasks是 Gradle 中很重要的部分, 更有趣是 Ant 的 projects 也是十分重要的部分. Gradle 可以直接引入Ant 项目, 并在运行时直接将 Ant targets 转换成 Gradle tasks.
你可以从 Gradle 中依赖它们, 并增强它们的功能, 甚至可以在 build.xml 文件中声明 Gradle tasks 的依赖. 并且properties, paths 等也可以通过同样的方法集成进来.

Gradle 完全支持你已有的 Maven 或者 lvy 仓库来构造发布或者提取依赖. Gradle 也提供了一个转化器, 用来将 maven 的 pom.xml 文件转换成 Gradle 脚本. 在运行时引入 Maven 项目也会在稍后推出.

9. 易于迁移
Gradle 可以兼容任何结构. 因此你可以直接在你的产品构建的分支上开发你的 Gradle 构建, 并且二者可以并行. 我们通常建议编写一些测试代码来确保它们的功能是相同的.
通过这种方式,
在迁移的时候就不会显得那么混乱和不可靠,
这是通过婴儿学步的方式来获得最佳的实践.

10. Groovy
Gradle 的构建脚本是通过 Groovy 编写的而不是 XML.
但是并不像其他方式,
这并不是为了简单的展示用动态语言编写的原始脚本有多么强大.
不然的话,
只会导致维护构建变得非常困难.
Gradle 的整个设计是朝着一种语言的方向开发的,
并不是一种死板的框架.
Groovy 就像胶水一样, 把你想实现的构想和抽象的 Gradle 粘在一起. Gradle提供了一些标准的构想, 但是他们并不享有任何形式的特权. 相比于其他声明式构建系统,对我们来说这是一个比较突出的特点.

10. Gradle 包装器
Gradle 包装器允许你在没有安装 Gradle 的机器上运行 Gradle 构建.
在一些持续集成的服务器上,
这个功能将非常有用.
它同样也能降低使用一个开源项目的门槛,
也就是说构建它将会非常简单.
这个包装器对于公司来说也是很有吸引力的.
它并不需要为客户机提供相应的管理防范.
这种方式同样也能强制某一个版本 Gradle 的使用从而最小化某些支持问题.

11. 免费和开源
Gradle 是一个开源项目, 遵循 ASL 许可.

2.2为什么用Groovy?

我们认为在脚本构建时,
一个内部的 DSL(基于一个动态语言)相对于 XML 的优势是巨大的. 有这么多的动态语言,
为什么选择 Groovy?
答案在于 Gradle 的运行环境.
虽然 Gradle 以一个通用构建工具为核心, 但是它的重点是Java项目.
在这样的项目中,
显然团队每个成员都对 Java 非常熟悉. 我们认为构建应尽可能对所有团队成员都是透明的, 所以选择了 Groovy.

你可能会说,为什么不直接使用 Java 作为构建脚本的语言. 我们认为这是一个很有用的问题.
对于你的团队,
它要有最高的透明度和最低的学习曲线,
也就是说容易掌握.
但由于 Java 的限制,
这样的构建语言不会那么完美和强大.
而像 Python,Groovy 或 Ruby 语言用来作为构建语言会更好.
我们选择了 Groovy 是因为它给 Java 开发人员提供了迄今为止最大的透明度. 其基本的符号和类型与 Java 是一样的,其封装结构和许多其他的地方也是如此.
Groovy 在这基础上提供了更多的功能,
而且与 java 有共同的基础.

对于那些同时是或者即将是 Python 或 Ruby 开发者的 Java 开发人员来说,
上述的讨论并不适用.
Gradle 的设计非常适合在 JRuby 和 Jython 中创建另一个构建脚本引擎. 它对于我们来说只是目前开发中没有最高优先级. 我们十分支持任何人来做贡献,
创建额外的构建脚本引擎.

APPEND Key value

开始版本 2.0.0.
时间复杂度: O(1).均摊时间复杂度是O(1), 因为redis用的动态字符串的库在每次分配空间的时候会增加一倍的可用空闲空间,所以在添加的value较小而且已经存在的 value是任意大小的情况下,均摊时间复杂度是O(1) 。

如果 key 已经存在,并且值为字符串,那么这个命令会把 value 追加到原来值(value)的结尾。 如果 key 不存在,那么它将首先创建一个空字符串的key,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。

返回值

Integer reply: 返回追加到字符串后的长度。

例子

1
2
3
4
5
6
7
8
9
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
redis>

模式:Time series

APPEND命令可以用来创建一个比列表更紧凑的固定长度的样例,通常称为time series。任何时间里有新的样例送来我们都可以使用下面的命令存储:

1
APPEND timeseries "fixed-size sample"

在time series里面访问单个元素并不难:

  • STRLEN 能够获取这个样例的长度。
  • GETRANGE 允许随机访问某些元素。如果我们的time series有相关的信息,我们就可以在Redis2.6版本里面通过GETRANGE结合Lua脚本来实现一个二分查找。
  • SETRANGE 能够覆盖一个已存在的time series。

该模式的局限在于只能做追加操作,目前无法裁剪这个time series到一个给定的大小是因为Redis目前缺少这样的命令去修剪字符串对象。但是time series以这种方式存储的空间效率是非常好的。

小贴士: 在键值中组合Unix时间戳, 可以在构建一系列相关键值时缩短键值长度,更优雅地分配Redis实例.

使用定长字符串进行温度采样的例子(在实际使用时,采用二进制格式会更好):

1
2
3
4
5
6
7
8
9
redis> APPEND ts "0043"
(integer) 4
redis> APPEND ts "0035"
(integer) 8
redis> GETRANGE ts 0 3
"0043"
redis> GETRANGE ts 4 7
"0035"
redis>

BITCOUNT key [start end]

开始版本 2.6.0.
时间复杂度: O(N)

计算给定字符串中被设置为1的bit数。

一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。
start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。

不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0

返回值

Integer reply
被设置为1的bit数量。

例子

1
2
3
4
5
6
7
8
9
redis> SET mykey "foobar"
"OK"
redis> BITCOUNT mykey
(integer) 26
redis> BITCOUNT mykey 0 0
(integer) 4
redis> BITCOUNT mykey 1 1
(integer) 6
redis>

模式: 使用bitmaps方式进行实时的量化

Bitmaps在某种信息的表示中是非常有用的。比如一个web程序需要通过用户的访问历史来确定哪些用户是beta进行测试活动的重点目标对象。
使用SETBIT命令就可以很容易的解决,采用一个很小的渐增的整数来对应每一天,程序上线的第一天用0表示,第二天用1表示,以此类推。
每次用户访问了一个页面,这个程序就可以记录下用户访问站点时候的当天值,使用SETBIT命令来设置bit对应的当天值是多少。
接下来就可以使用BITCOUNT命令来统计用户访问的总天数.

参考文章 “Fast easy realtime metrics using Redis bitmaps“.

性能考虑

前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, BITCOUNT 的处理速度就像 GETINCR 这种 O(1) 复杂度的操作一样快。
如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

  • 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
  • 使用 BITCOUNT 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。

SET key value

1
SET key value [EX seconds] [PX milliseconds] [NX|XX]

开始版本 1.0.0.
时间复杂度: O(1)

设置一个key包含一个值。如果这个key已经拥有了值,不会管它是什么类型,它都将被覆盖。当SET操作成功后之前任何时候和这个key有关的东西都会被丢弃掉。

选项

从Redis 2.6.12版本开始 SET支持以下选项来修改它的行为:

  • EX seconds – 设置指定的过期时间,单位是秒。
  • PX milliseconds – 设置指定的过期时间,单位是毫秒。
  • NX – 只有这个key不存在的情况下才执行set操作。
  • XX – 只有这个key存在的情况下才执行set操作。

注意: 由于SET命令加上选项已经可以完全取代SETNX, SETEX, PSETEX的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。

返回值

如果SET正确执行了,会返回一个简单的应答:OK。如果SET操作没有执行就会返回一个Null的应答,因为用户指定了NX或者XX选项,但是条件不满足的。

例子

1
2
3
4
5
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis>

模式

注意:下面的模式不赞成使用,参考Redlock algorithm,这是一个更复杂的实现,但是提供了更好的保证和容错。
这个命令 SET resource-name anystring NX EX max-lock-time 是一种用 Redis 来实现锁机制的简单方法。

如果上述命令返回OK,那么客户端就可以获得锁(如果命令返回Nil可以在一段时间后重新尝试),并且可以通过DEL命令来释放锁。这个锁会在到达过期时间后自动释放。
采用下面的解锁模式可以让系统更健壮:

  • 不要设置固定的字符串,而是设置为随机的大字符串,称为令牌。
  • 不要通过DEL命令去直接释放锁,而是发送一个脚本:仅仅在这个key匹配的情况下才执行del。

这样就避免了锁到期后在尝试释放锁的时候删除了另一个客户端创建的锁。解锁脚本的例子如下:

1
2
3
4
5
6
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end

这个脚本可以这样调用:

1
EVAL ...script... 1 resource-name token-value

GET key

开始版本 1.0.0.
时间复杂度: O(1)

获取指定key的值。如果这个key不存在就会返回特定的值nil。如果这个key存储的值不是字符串类型就会返回一个错误,因为GET命令只处理字符串类型值。

返回值

返回一个字符串:如果key不存在就返回nil。

例子

1
2
3
4
5
6
7
redis> GET nonexisting
(nil)
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis>

STRLEN key

开始版本 2.2.0.
时间复杂度: O(1)

返回指定key存储的字符串值的长度。当key没有对应的字符串类型的值时返回一个错误。

返回值

Integer reply:key对应的字符串长度,如果key不存在返回0.

例子

1
2
3
4
5
6
7
redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
redis>

Overview

JSCS 已经合并到了ESLint!
JSCS 是一个能够指导你对代码风格进行检查和格式化的工具。

Presets

注意:最简单的方式就是使用一个预先设置好的选项,如下:

你可以在创建的配置文件.jscsrc中将任意一个规则禁用,只需要赋值null或者false,像这样:

1
2
3
4
{
"preset": "jquery",
"requireCurlyBraces": null // or false
}

Friendly packages

Extensions

Installation

jscs可以使用npm来进行安装:

1
npm install jscs -g

你可以使用以下命令从项目的根路径运行:

1
jscs path[ path[...]]

你还可以通过管道输出到jscs:

1
cat myfile.js | jscs

Programmatic Usage

jscs 可以在你的程序代码里面直接使用:

1
2
3
var Checker = require("jscs");
var checker = new Checker();
checker.registerDefaultRules();

你可以配置检查器实例去使用指定的选项,或者预设的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Configure the checker with an options object
checker.configure({
"requireCurlyBraces": [
"if",
"else",
"for"
]
});

// Use the jQuery preset
checker.configure({
preset: "jquery"
});

// Use the Google preset, but override or remove some options
checker.configure({
preset: "google",
disallowMultipleLineBreaks: null, // or false
validateIndentation: "\t"
});

要检查一个字符串类型的代码,通过使用checkString方法:

1
2
var results = checker.checkString(stringOfCode);
var errors = results.getErrorList();

这个results对象可以获取每个错误的描述信息:

1
2
3
4
results.getErrorList().forEach(function(error) {
var colorizeOutput = true;
console.log(results.explainError(error, colorizeOutput) + "\n");
});

CLI

某些CLI选项也可以放到你的.jscsrc文件中(比如preset)。
下面的这些选项已经从3.0中移除掉了(esnext默认就是激活的).

  • –esnext (-e)
  • –esprima (-s)

这个verbose标记也在3.0被移除了,因为它默认就被开启的(所以你知道哪些规则是错误的)。

–fix (-x)

可以修复所有支持的风格。

1
jscs path[ path[...]] --fix

–auto-configure

自动生成一个你选择的预置规则文件:

1
jscs --auto-configure path

path 可以是文件或者目录。

–config (-c)

允许指定一个具体路径的配置文件:

1
jscs path[ path[...]] --config=./.config.json

若这个–config没有指定,jscs会去package.json文件中通过jscsConfig选项来找.jscsrc和.jscs.json文件。

–preset (-p)

指定预定义的代码风格规则:

1
jscs path[ path[...]] --preset=jquery

你需要穿点一个.jscsrc配置文件来添加/删除这些规则。

–extract

使用这个选项可以检查嵌入式的js代码:

1
jscs path[ path[...]] --extract *.html

如果需要的话可以设置多个:

1
jscs path[ path[...]] --extract *.html --extract *.htm

目前,只支持html格式的,但是不能够自动修复。

–reporter (-r)

jscs 提供了8种方式的报告: checkstyle, console, inline, inlinesingle, junit, text, unix and json.

1
jscs path[ path[...]] --reporter=console

还可以指定自己的路径文件:

1
jscs path[ path[...]] --reporter=./some-dir/my-reporter.js

–error-filter (-f)

错误过滤器:

1
jscs path[ path[...]] --error-filter=path/to/my/module.js

–no-colors (-n)

无颜色输出。

–max-errors (-m)

设置报告的最大错误数量。(-1表示报告所有的错误)

–help (-h)

输出使用信息。

–version (-V)

输出jscs版本信息。

Options

以下选项已经在3.0版本中被移除(esnext默认是激活的)。

  • esnext: Attempts to parse your code as ES6+, JSX, and Flow using babylon as the underlying parser.

  • esprima: Attempts to parse your code with a custom Esprima version.

  • esprimaOptions: Custom options to be passed to esprima.parse(code, options)

verbose: Prepends the name of the offending rule to all error messages.

plugins

从指定的多个路径加载插件。详细信息查看Plugin API
Values: Array of NPM package names or paths

1
"plugins": ["jscs-plugin", "./lib/project-jscs-plugin"]

additionalRules

额外规则的路径。
Type: Array
Values: Array of file matching patterns
Example

1
"additionalRules": ["project-rules/*.js"]

preset

集成预定义的规则。
Type: String

Values: 你可以选择一个默认的规则:”airbnb”, “crockford”, “google”, “jquery”, “mdcs”, “node-style-guide”, “wikimedia”, “wordpress”, “idiomatic”.
或者从你的本地加载。
Example

1
2
3
4
5
6
"preset": "jquery"
"preset": "./path-to-your-preset"

// If your preset called "jscs-your-preset-node_modules-path"
// You can either define full name or omit "jscs-" prefix -
"preset": "your-preset-node_modules-path"

你也可以通过指定null或者false来禁用指定的预设规则。

excludeFiles

排除不需要检查的文件。
Type: Array

Values: Array of file matching patterns

Example

1
2
// Use `"!foo"` to specifically include a file/folder
"excludeFiles": ["folder_to_exclude/**", "src/!(bar|foo)"]

Default
这个 .git 和 node_modules 文件夹默认被排除。

fileExtensions

改变文件的扩展类型。
type: Array or String or “*”

Values: 单个文件后缀或者数组,以.开头,匹配不区分大小写,如果是”*”表示匹配所有的文件。
Example

1
"fileExtensions": [".js", ".jsx"]

Default
.js 后缀的文件是默认被处理的格式。

extract

设置嵌入式代码被提取的文件格式。

Type: Array or Boolean

Values: Array of file matching patterns

js代码提取不会匹配fileExtensions 或者 excludeFiles设置过的。目前只支持html格式的。

Example

“extract”: [“.htm”, “.html”]
Value true

如果设置的值为true,将从.htm,.html或者.xhtml后缀的文件提取代码。

maxErrors

设置报告的最大错误数量(通过设置-1或者null输出所有的错误)。如果fix属性被激活则忽略。

Type: Number|null

Default: 50

Example

1
2
3
4
5
6
// Report only the first 10 errors
"maxErrors": 10

// Report all errors
"maxErrors": -1
"maxErrors": null

fix

将修复所有支持的代码规则。

Type: Boolean|null

Default: false

es3

Use ES3 reserved words.

Type: Boolean

Value: true

Example

“es3”: true

errorFilter

错误过滤器,根据返回的值来决定是否报告这个错误。

Type: String

Example

1
"errorFilter": "path/to/my/filter.js"

See how to define an error filter.

Error Suppression

Disabling a Rule

通过在你的.jscsrc配置里面指定null可以禁用规则。

1
2
3
4
{
"preset": "jquery",
"requireCurlyBraces": null
}

Inline Comments

你也可以通过注释的写法来禁用或者激活:

1
2
/* jscs: enable */
// jscs: enable

你可以同时使用以上几种方式来禁用规则。

Disabling All Rules

The placement of the special comments will disable or enable the checking of all rules against the code that appears after the comments。

1
2
3
4
5
var a = b;
// jscs:disable
var c = d; // all errors on this line will be ignored
// jscs:enable
var e = f; // all errors on this line will be reported

Disabling Specific Rules

通过行注释jscs:disable来禁用指定的规则或者jscs:enable来激活规则。

1
2
3
4
// jscs:disable requireCurlyBraces
if (x) y(); // all errors from requireCurlyBraces on this line will be ignored
// jscs:enable requireCurlyBraces
if (z) a(); // all errors, including from requireCurlyBraces, on this line will be reported

Disabling Specific Rules For a Single Line

通过单行注释也可以进行规则:

1
2
if (x) y(); // jscs:ignore requireCurlyBraces
if (z) a();

你可以在禁用规则之后在激活所有的规则。

1
2
3
4
// jscs:disable requireCurlyBraces
if (x) y(); // all errors from requireCurlyBraces on this line will be ignored
// jscs:enable
if (z) a(); // all errors, even from requireCurlyBraces, will be reported

你也可以禁用多个规则后在逐步重新激活规则。

1
2
3
4
5
6
// jscs:disable requireCurlyBraces, requireDotNotation
if (x['a']) y(); // all errors from requireCurlyBraces OR requireDotNotation on this line will be ignored
// jscs:enable requireCurlyBraces
if (z['a']) a(); // all errors from requireDotNotation, but not requireCurlyBraces, will be ignored
// jscs:enable requireDotNotation
if (z['a']) a(); // all errors will be reported

Disabling rule checks on the entire file

通过在文件顶部添加注释来禁用所有的规则。

1
// jscs:disable

As the comments are applicable only to the file they are placed in there is no requirement to put the special comment // jscs:enable at the end of the file.

The same concept is applicable to disable only specific rules in the file. So instead of // jscs:disable, you can put // jscs:disable requireCurlyBraces to disable a single rule or // jscs:disable requireCurlyBraces, requireDotNotation to disable multiple rules

Versioning & Semver

We recommend installing JSCS via NPM using ^, or ~ if you want more stable releases.

Semver (http://semver.org/) dictates that breaking changes be major version bumps. In the context of a linting tool, a bug fix that causes more errors to be reported can be interpreted as a breaking change. However, that would require major version bumps to occur more often than can be desirable. Therefore, as a compromise, we will only release bug fixes that cause more errors to be reported in minor versions.

Below you fill find our versioning strategy, and what you can expect to come out of a new JSCS release.

  • Patch release:
  1. A bug fix in a rule that causes JSCS to report less errors.
  2. Docs, refactoring and other “invisible” changes for user;
  • Minor release:
  1. Any preset changes.
  2. A bug fix in a rule that causes JSCS to report more errors.
  3. New rules or new options for existing rules that don’t change existing behavior.
  4. Modifying rules so they report less errors, and don’t cause build failures.
  • Major release:
  1. Purposefully modifying existing rules so that they report more errors or change the meaning of a rule.
  2. Any architectural changes that could cause builds to fail.
0%