NO END FOR LEARNING

Writing blog if you feel tired | 学海无涯 苦写博客

基于Gradle和Intellij的Spring Boot热交换

| Comments

在去年的一篇《Gradle Jetty和Gradle Watch插件实现热部署》中,谈到Java热部署带来的好处,文章地址在: http://benweizhu.github.io/blog/2014/07/27/gradle-jetty-plugin-hot-deploy/

但是现在越来越多的Spring应用直接使用Spring Boot作为框架,那么这个Jetty插件的配置就不起作用了,好在Spring官方针对热部署问题,提供了解决方案:Spring Reloaded。

项目地址在: https://github.com/spring-projects/spring-loaded

这里,我就不多说废话了,直接告诉大家怎么用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
buildscript {
    repositories { jcenter() }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE"
        classpath 'org.springframework:springloaded:1.2.4.RELEASE'
    }
}

apply plugin: 'idea'

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

如果想将Spring Loaded和Gradle,IntelliJ结合起来,那你需要付出代价。默认情况下,IntelliJ将类编译到一个跟Gradle不同的位置,这会导致Spring Loaded监控失败,所以使用idea模块修改编译输出位置和Gradle一样。

注:IntelliJ必须配置跟命令行Gradle任务相同的Java版本,并且springloaded必须作为一个buildscript依赖被包含进去。

官方文档的springloaded版本是1.2.0.RELEASE,这个版本有问题,会出现:

org.springsource.loaded.jvm.JVM : Problems copying method. Incompatible JVM? 报错

依赖下载完成之后,正常启动Spring Boot Run。

1
./gradlew bRun

如果你自己有仔细阅读官方文档的代码,你会发现官方少了一行testOutputDir的配置。官方文档上没有设置testOutputDir,这就会导致,intellij编译代码时,输出test下面的class到了out目录的main中。从而改变了Gradle默认的输出测试路径(main和test分开的),所以最好还是手动配置一下。

此时,如果你在应用启动的时候修改了Java代码,只需要点击Intellij的编译按钮,重新编译代码即可。

导致集成测试的问题

由于这样配置之后,导致Intellij上进行make project,输出到build/classes下。

这样做会导致集成测试有一个问题,运行集成测试的时候,我们常常需要使用properties文件和xml文件。

如果之前运行过gradle build,而build目录下没有被clean,则gradle默认会将properties文件和xml文件放在build/resources下,这与Intellij的行为不同。

而运行测试的时候,本来Intellij会默认先跑make project,但是由于build/classes已经有文件了,所以就skip了,于是导致Intellij找不到resource文件,因为Intellij要求的resource路径和gradle构建时输出的路径不同。

手动点击Project Rebuild

可以手动点击Project Rebuild,它会清理Intellij的输出目录(也就是当前Gradle的classes目录),然后在make。

配置Intellij的Junit

又或者改变Intellij中Junit的配置,Junit会在运行测试之前,先跑make,可以然它在make之前先跑Gradle的clean任务。

通过Watch来解决

还可以将我在上篇文章中介绍的Watch引入,监测文件变化,自动运行compileJava和processResources等Gradle命令,而不要改变Intellij的输出目录,让Gradle和Intellij采用各自的输出目录,就可以的。

参考文献:
1.http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html

学好JavaScript - 初学者忽视和困惑的东西

| Comments

JavaScript普通对象

JavaScript中除了数字、字符串、布尔值、null和undefined之外的就是对象了。对象是属性的集合,每个属性都由“名/值对”构成,值可以是原始值,比如数字、字符串,也可以是对象。普通的JavaScript对象就是“命名值”的无序集合。(JavaScript同样定义了一种特殊对象-数组,表示带编号的值的有序集合)

可选的分号

JavaScript使用分号将语句分开。但如果语句各自独占一行,通常可以省略语句之间的分号。是否省略分号是一种风格,对于不知道什么时候可以省略分号的开发,在任何时候都采用分号分割是一种常见风格。

强制类型转换和弱类型

如果使用的数据类型和JavaScript所需的数据类型不同,那么它会尽量使这个操作变得有意义,而不是直接报错。

为完成操作,JavaScript会在背后进行数据类型转换,这被称作强制类型转换。例如,表达式(‘1’ > 0)中,字符串‘1’会被转换成数值1,从而导致该表达式的结果为true。

JavaScript被称作弱类型语言,因为值的数据类型是可以变的。其他一些语言则要求指定每个变量的数据类型,这样的语言被称为强类型,比如说Java,C++。

强制类型转换可能会导致代码出现意外情况。因此,当检测两个值是否相等时,推荐使用更加严格的===和!==来取代==和!=,因为严格相等和严格不等会同时检测值及其类型。

布尔值

因为存在强制类型转换,JavaScript中的每个值都可以被当做true或者false处理,这就导致了有趣的现象。

任意JavaScript的值都可以转换为布尔值,下面这些值会被转换为false,

1
2
3
4
5
6
undefined
null
0
-0
NaN
""

所有其他值,包括所有对象(数组)都会转换成true,false和上面6个可以转换成false的值有时称作“假值”,其他值称作“真值”。JavaScript期望使用一个布尔值的时候,假值会被当做false,真值会被当做true。

null和undefined

null是JavaScript语言的关键字,它表示一个特殊值,常用来描述“空值”。对null执行typeof运算,结果返回字符串“object”,也就是说,可以将null认为是一个特殊的对象值,含义是“非对象”。但实际上,通常认为null是它自由类型的唯一一个成员,它可以表示数字,字符串和对象是“无值”的。

JavaScript还有第二值来表示值的空缺。用未定义的值表示更深层次的“空值”。它是变量的一种取值,表示变量没有初始化,如果要查询对象属性或者数组元素的值时,返回undefined则说明这个属性或者元素不存在。如果函数没有任何返回值,则返回undefined。

undefined是预定义的全局变量,它和null不一样,它不是关键字,它的值就是“未定义”。

尽管null和undefined是不同的,但它们都表示“值的空缺”,两者往往可以互换。判断相等运算符“==”认为两者是相等的,所以需要使用严格相等运算符“===”来区分它们。

如果你想将它们赋值给变量或者属性,或将它们作为参数传入函数,最佳的选择是使用null。

检测相等和存在

因为对象或者数组被视为真值,所以这种方法经常被用来判断页面中的元素是否存在。

1
2
3
4
5
if (document.querySelector('#main')) {
  
} else {
  
}

由于强制类型转换,严格等于操作符===和严格不等于操作符!==所得结果比==和!=更容易预料。

如果使用==操作,那么下列值被视为相等的:false、0以及’‘(空字符串),但在严格的===下,它们是不相等的。

虽然null和undefined都是假值,但它们除了自身之外不等于任何值。同样在使用严格相等时,也是不相等的。

虽然NaN被视为假值,但是它不等于任何值,它升值不等于它自己(因为NaN表示不可定义的数字,两个不可定义的数字是不相同的)。

短路值

逻辑操作符是从左向右计算的,当它们获得确定结果时,立刻会发生“短路”(停止运算),但是它们会返回停止运算时的值(不一定是true或者false),这里很有意思。

1
2
3
4
5
6
7
8
9
10
11
var a = 'a'
var b = (a||'c')
// a

var a = ''
var b = (a||'c')
// c

var a = ''
var b = (a||{})
// Object{}

逻辑操作符并非总返回true或者false,原因在于:

它们会返回停止运算时的值,那个值可能是真值或者假值,但不一定是布尔类型。这种技巧常常被使用于变量赋值和创建对象。

因为这种短路机制,在“或”运算符常常把最可能返回true的放在第一位,在“与”操作符把最可能返回false的放在第一位,或者把最耗时的操作放在最后一位。

全局对象

全局对象的属性是全局定义的符号,JavaScript程序可以直接使用。当JavaScript解释器启动时(或者任何Web浏览器加载新页面的时候),它将创建一个新的全局对象,并给它一组定义的初始属性:全局属性,undefined,全局函数,isNaN(),构造函数,Date(),全局对象,Math。

全局对象的初始属性并不是保留字,但它们应该当做保留字来对待。

在代码的最顶级——不在任何函数内的JavaScript代码——可以使用JavaScript关键字this来引用全局对象:

1
var global = this; // 定义一个引用全局对象的全局变量

在客户端JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,Window对象充当了全局对象。这个全局对象Window对象有一个属性window引用其自身,它可以代替this来引用全局对象。

当初次创建的时候,全局对象定义了JavaScript中所有的预定义全局值。这个特殊对象同样包含了为程序定义的全局值。如果代码声明了一个全局变量,这个全局变量就是对象的一个属性。

1
2
var a = 'a';
window.a; //输出"a"

包装对象

我们看到字符串也同样具有属性和方法,字符串既然不是对象,为什么它会有属性呢?
只要引用了字符串的属性,JavaScript就会将字符串通过调用new String(“some string”)的方法转换成对象。

同字符串一样,数字和布尔值也具有各自的方法:通过Number()和Boolean()构造函数创建一个临时对象。

JavaScript会在必要时将包装对象转换成原始值。“==”等于运算符将原始值和其他包装对象视为相等,但“===”全等运算符将它们视为不等。

in运算符

in运算符希望它的左操作数是一个字符串或可以转换为字符串,希望它的右操作数是一个对象。如果右侧的对象拥有一个名为做操作数值的属性名,那么表达式返回true。

1
2
a = {text:"hi"}
"text" in a

for/in语句也使用for关键字,但它是和常规的for循环完全不同的一类循环。for/in循环语句的语法如下:

1
2
for (variable in object)
  statement

variable通常是一个变量名,也可以是一个可以产生左值的表达式或者一个通过var语句声明的变量,总之必须是一个适用于赋值表达式左侧的值。object是一个表达式,这表达式的计算结果是一个对象。

for/in循环则是用来更方便地遍历对象属性成员:

1
2
for (var p in o)
  console.log(o[p]);

在执行for/in语句的过程中,JavaScript解释器首先计算object表达式。如果表达式为null或者undefined,JavaScript解释器将会跳出循环并执行后续的代码。如果表达式等于一个原始值,这个原始值将会转换为与之对应的包装对象。否则,expression本身已经是对象了。JavaScript会依次枚举对象的属性来执行循环。

“use strict”

“use strict”是ECMAScript5引入的一条指令。指令不是语句。

它不含任何语言的关键,指令仅仅是一个包含一个特殊字符串直接量的表达式,对于那些没有实现ECMAScript5的JavaScript解释器来说,它只是一条没有副作用的表达式语句,它什么也没做。

它只能出现在脚本代码的开始或者函数体的开始,任何实体语句之前。但他不必一定要出现在脚本的首行或者函数体首行,因为“use strict”指令之后或者之前都可能有其他字符串直接量表达式语句,并且JavaScript的具体实现可能将它们解析为解释器自有的指令。在脚本或者函数体第一条常规语句之后字符串直接量表达式语句只当做普通表达式语句对待;它们不会当做指令解析,它们也没有任何副作用。

使用“use strict”指令的目的是说明(脚本或函数中)后续的代码将会解析为严格代码。

严格代码以严格模式执行。ECMAScript5中的严格模式是该语言的一个受限制的子集,它修正了语言的重要缺陷,并提供健壮的查错功能和增强的安全机制。

原型

每个JavaScript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。

所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{}创建对象一样,通过new Object()创建的对象也继承自Object.prototype。同样,通过new Array()创建的对象的原型就是Array.prototype,通过new Date()创建的对象的原型就是Date.prototype。

没有原型的对象为数不多,Object.prototype就是其中之一。他不继承任何属性。其他原型对象都是普通对象,普通对象也有原型。所有的内置的构造函数都具有一个继承自Object.prototype的原型。例如,Date.prototype的属性继承自Object.prototype,因此由new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype。这就是所谓的“原型链”。

关联数组

object.property object[“property”]

第二种语法使用方括号和一个字符串,看起来更像数组,只是这个数组是通过字符串索引而不是数字索引,这种数组就是所说的关联数组。

在C++、Java等强类型语言中,对象只能拥有固定数目的属性,并且这些属性名称必须提前定义好。由于JavaScript是弱类型语言,因此不必遵循这个条规定,在任何对象中程序都可以创建任意数量的属性。但当通过点运算符访问对象的属性时,属性名用一个标识符来表示。标示符必须直接出现在JavaScript程序中,它们不是数据类型,因此程序无法修改它们。

反过来讲,当通过[]来访问对象的属性时,属性名通过字符串来表示。字符串是JavaScript的数据类型,在程序运行时可以修改和创建它们。

for(i = 0; i < 4; i++) addr += customer[“address” + i];