log4j2的使用

Log4j2 是一个常用的日志工具。本文把试用结果和遇到的问题记录一下。

Java文件

Java代码如下:

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class TestLog {

private static final Logger logger = LogManager.getLogger();

public static void main(String[] args) {
logger.info("Hello, world!");
logger.error("What are you doing?");
}

}

Log4j2配置

log4j2默认在console上输出ERROR级别的信息。为了显示INFO信息,需要通过配置更改默认设置。试用中采用XML文件来进行设置。log4j2.xml的具体设置内容如下(Root level设置为INFO):

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

代码的组织按照gradle/maven的默认结构。

Q1: 程序找不到log4j2的配置文件?

A1: 配置文件必须放在src\main\resources下面,否则运行时需要指定配置文件。

Gradle脚本

build.gradle文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apply plugin: 'java'

repositories {
mavenCentral()
}

dependencies {
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.5'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.5'
}

jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }

manifest {
attributes 'Main-Class': 'com.shizhihua.example.TestLog'
}
}

Q2: 运行jar文件,找不到依赖包?

A2: Gradle能自动解决编译过程中的第三方依赖,但是我们直接从命令后直接运行程序时,还是要指定第三方依赖才行。为了能直接运行jar文件,上述脚本中在jar文件生成配置中增加了一行代码,这样就把第三方依赖包也打包起来了,形成了“胖”jar。

1
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }

运行结果

直接执行下面命令:

1
2
gradle build
java -jar build\libs\testLog.jar

得倒下面的输出(与时间有关)

1
2
07:33:56.484 [main] INFO  com.shizhihua.example.TestLog - Hello, world!
07:33:56.485 [main] ERROR com.shizhihua.example.TestLog - What are you doing?

《Spring In Action》学习笔记(三):Spring 4.0 模块

Spring 4.0含有20个独立的模块(modules),每个模块含有三个文件:

  • binary class file
  • source JAR file
  • JavaDoc JAR file

这20个独立模块可以分为6大类。

Data Access & Integration

  1. JDBC
  2. Transaction
  3. ORM
  4. OXM
  5. Messaging
  6. JMS

Web and Remoting

  1. Web
  2. Web servlet
  3. Web portlet
  4. WebSocket

Aspect-Oriented Programing

  1. AOP
  2. Aspects

Instrumentation

  1. Instrument
  2. Instrument Tomcat

Core Spring Container

  1. Beans
  2. Core
  3. Context
  4. Expression
  5. Context support

Testing

  1. test

附录:缩写

  • DAO:Data-access object
  • ORM:Object-relational mapping
  • MVC:Model-View-Controller

《Spring In Action》学习笔记(二):Spring Container

在一个Spring-based application中,程序中的对象都生存在Spring Container中。由这一Container来负责这些对象的生成、关联、配置和管理等等。

两类容器

在Spring中,存在多个容器的实现,大体可以分为两大类:

  • Bean factories
    • 由接口org.springframework.beans.factory.BeanFactory定义
    • 最简单的容器,为DI提供基本的支持
  • Application contexts
    • 由接口org.springframework.context.ApplicationContext定义
      - 建立在bean factory基础上,提供application-framework services

在实际应用中,由于bean factories过于底层,不便于应用。所以,在大部分程序中,一般更倾向于采用application contexts

Application Context 容器

常见的一些application contexts

  • AnnotationConfigApplicationContext:Loads a Spring application context from one or more Java-based configuration classes
  • AnnotationConfigWebApplicationContext:Loads a Spring web application context from one or more Java-based configuration classes
  • ClassPathXmlApplicationContext:Loads a context definition from one ormore XML files located in the classpath, treating context-definition files as classpath
    resources
  • FileSystemXmlApplicationContext:Loads a context definition from one or more XML files in the filesystem
  • XmlWebApplicationContext:Loads context definitions from one or more XML files contained in a web application

两个例子:

1
2
ApplicationContext context = new FileSystemXmlApplicationContext("c:/knight.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");

《Spring In Action》学习笔记(一):基本概念

基于《Spring In Action》第四版。

概述

为了替代笨重的Enterprise JavaBeans (EJB),人们设计了轻量级的框架Spring。 因此Spring的设计目的就是:简化java开发。 Spring simplifies Java development。

为了降低Java开发复杂度,Spring采用了四方面的策略

  • Lightweight and minimally invasive development with POJOs
  • Loose coupling through DI and interface orientation
  • Declarative programming through aspects and common conventions
  • Eliminating boilerplate code with aspects and templates

后续四部分会分别针对这四个方面,以实际例子来说明其含义。

先记录一些常用的名词:

  • POJO: plain old Java object (简单Java类)
  • Bean/JavaBean:Spring中使用宽松的定义,就是指一个spring component,当作POJO的同义词
  • DI:Dependency Injection(依赖注入)
  • AOP:Aspect-Oriented Programming

Non-invasive Programming Model

有一些Java框架在使用时,开发人员需要在自己的程序中扩展或者实现框架的的一些interface或者class。

Spring框架不会让程序代码和他的API混在一起。Spring almost never forces you to implement a Sprint-specific interface or extend a Spring-specific class。

通常,使用Spring时,往往感受不到在使用Spring。 最差情况是会使用一些Spring’s annotation。下面是一个例子:

1
2
3
4
5
6
7
8
// Spring doesn’t make any unreasonable demands on HelloWorldBean

package com.habuma.spring;
public class HelloWorldBean {
public String sayHello() {
return "Hello World";
}
}

从上面例子可以看到,这就是一个简单Java类(POJO),没有Spring的组件直接体现出来。这样的好处是:

同样的代码可以在其他非Spring程序中使用(in a non-Spring application)。

Dependency Injection

Dependency Injection (DI) 的用处主要是解耦不同的class/object。

一个典型应用场景是:

  • Class B1来自通用的Inferface B
  • Class A里面需要使用Class B1
    • 若直接在Class A中直接生产Class B1的对象,则两个类型之间产生了紧耦合。不利于后续程序的扩展
    • Class A可以把Interface B的对象作为输入参数,这样就从外界获取Class B1的对象,而不是内部直接产生,从而实现解耦;同时也利于扩展,例如获取Interface B的其他实现B2、B3等。

下面分三部分通过例子来

传统紧耦合方式

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.springinaction.knights;

public class DamselRescuingKnight implements Knight {
private RescueDamselQuest quest;

public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
}

public void embarkOnQuest() {
quest.embark();
}
}

上述代码,存在两方面的问题:

  1. 紧耦合、难以扩展:两个类DamselRescuingKnightRescueDamselQuest紧密联系在一起。如果要一个屠龙的knight,则上述代码都得重新写,很难直接利用。
  2. 测试测试困难:单元测试需要知道是否正确调用了quest.embark(),目前没有简洁的方法可以进行验证。

依赖注入(DI)

使用DI,则有一个第三方来负责协同两个类。看下面例子(constructor injection):

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

public class BraveKnight implements Knight {

private Quest quest;

public BraveKnight(Quest quest) {
this.quest = quest;
}

public void embarkOnQuest() {
quest.embark();
}
}

上面的BraveKnight就比较通用,可以接收各种QuestRescueDamselQuest, SlayDragonQuest, MakeRoundTableRounderQuest

因此BraveKnight已经与Quest的各种具体实现完全解耦了,不再依赖于具体实现。 (设计模式原则之一:基于接口编程。策略模式)

采用constructor injection后,很容易使用Mock来进行单元测试:

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

import static org.mockito.Mockito.*;
import org.junit.Test;

public class BraveKnightTest {

@Test
public void knightShouldEmbarkOnQuest() {
Quest mockQuest = mock(Quest.class);
BraveKnight knight = new BraveKnight(mockQuest);
knight.embarkOnQuest();
verify(mockQuest, times(1)).embark();
}
}

通过采用调用embarkOnQuest(),可以利用Mockito来验证Questembark()是被调用了1次。

注入Quest

首先实现被注入的类SlayDragonQuest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.springinaction.knights;
import java.io.PrintStream;

public class SlayDragonQuest implements Quest {
private PrintStream stream;

public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}

public void embark() {
stream.println("Embarking on quest to slay the dragon!");
}
}

可以看到,SlayDragonQuest也使用了构造注入(constructor injection)。

在Spring中,可以使用XML来把SlayDragonQuest传递给BraveKnight

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">


<bean id="knight" class="com.springinaction.knights.BraveKnight">
<constructor-arg ref="quest" />
</bean>

<bean id="quest" class="com.springinaction.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}" />
</bean>
</beans>

在上述XML文件中,两个类都被声明为Bean了。每个Bean都配置了构造函数输入参数,从而关联的对应的类(wire beans)。

在Spring中,可以用Java代码来等价替换上述XML文件:

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

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.springinaction.knights.BraveKnight;
import com.springinaction.knights.Knight;
import com.springinaction.knights.Quest;
import com.springinaction.knights.SlayDragonQuest;

@Configuration
public class KnightConfig {

@Bean
public Knight knight() {
return new BraveKnight(quest());
}

@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}
}

工作起来

在Spring application中,一个application context来读取bean定义,并把他们wire在一起。

Spring提供了application context的多种实现,其差别仅仅在于如何load configuration。 本处以XML文件为例,需要使用ClassPathXmlApplicationContext(Java配置文件时,需要使用AnnotationConfigApplicationContext):

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

import org.springframework.context.support.
ClassPathXmlApplicationContext;

public class KnightMain {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml");
Knight knight = context.getBean(Knight.class); // Get knight
bean
knight.embarkOnQuest();
context.close();
}
}

AOP

DI实现了模块间的解耦(松耦合),AOP可以帮组我们提高功能模块的可重用性。

系统的不同功能分为不同的模块。但在实际过程中,一些模块除了自己的核心功能以外,还有一些其他的辅助功能,而这些辅助的功能可能会涉及多个模块。这就造成,修改一些代码,可能会涉及到系统很多部分。

定义一个类Minstrel,其有两个函数,分别在knight做一个quest(embark on a quest)之前和之后执行:

1
2
3
4
package com.springinaction.knights;
import java.io.PrintStream;
public class Minstrel { private PrintStream stream; public Minstrel(PrintStream stream) { this.stream = stream; } public void singBeforeQuest() { // Called before quest stream.println("Fa la la, the knight is so brave!"); }
public void singAfterQuest() { // Called after quest stream.println("Tee hee hee, the brave knight " + "did embark on a quest!"); } }

传统处理方式

直接在某个具体骑士的类中调用minstrel的两个函数:

1
2
package com.springinaction.knights;

public class BraveKnight implements Knight {
	private Quest quest;
	private Minstrel minstrel;
	public BraveKnight(Quest quest, Minstrel minstrel) {
this.quest = quest; this.minstrel = minstrel; } public void embarkOnQuest() throws QuestException { minstrel.singBeforeQuest(); quest.embark(); minstrel.singAfterQuest(); } }

在这个例子中,两个类紧紧耦合在一起了。

AOP处理方式

在Spring中,AOP处理方式对应的XML配置如下:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="knight" class="com.springinaction.knights.BraveKnight"> <constructor-arg ref="quest" /> </bean>
<bean id="quest" class="com.springinaction.knights.SlayDragonQuest"> <constructor-arg value="#{T(System).out}" /> </bean>
<bean id="minstrel" class="com.springinaction.knights.Minstrel"> <constructor-arg value="#{T(System).out}" /> </bean>
<aop:config> <aop:aspect ref="minstrel"> <aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/> <aop:before pointcut-ref="embark" method="singBeforeQuest"/> <aop:after pointcut-ref="embark" method="singAfterQuest"/> </aop:aspect> </aop:config> </beans>

在上述配置中,首先把Minstrel声明为bean,然后通过<aop:aspect>来配置AOP。在这儿,Minstrel仍然是一个POJO。

利用模板消除冗余代码

Eliminating boilerplate code with templates

传统处理方式

JDBC相关程序,会产生很多例行(冗余)代码,很多代码不是程序的核心功能,而是辅助性的代码。

1
public Employee getEmployeeById(long id) {
	Connection conn = null;
	PreparedStatement stmt = null;
	ResultSet rs = null;
	try {
		conn = dataSource.getConnection();
		stmt = conn.prepareStatement(
"select id, firstname, lastname, salary from " +
"employee where id=?");
	stmt.setLong(1, id);
	rs = stmt.executeQuery();
	Employee employee = null;
	if (rs.next()) {
		employee = new Employee();
		employee.setId(rs.getLong("id"));
		employee.setFirstName(rs.getString("firstname"));
		employee.setLastName(rs.getString("lastname"));
		employee.setSalary(rs.getBigDecimal("salary"));
	}
	return employee;
	} catch (SQLException e) {
	} finally {                  // Clean up mess
		if(rs != null) {
			try {
				rs.close();
			} catch(SQLException e) {}
		}
		if(stmt != null) {
			try {
				stmt.close();
			} catch(SQLException e) {}
		}

		if(conn != null) {
			try {
				conn.close();
			} catch(SQLException e) {}
		}
	}
	return null;
}

模板处理方式

使用Spring中的模板SimpleJdbcTemplate,只要写核心功能相关的代码,和JDBC API相关的一些辅助性代码都交由模板处理,不需要额外写代码。

1
public Employee getEmployeeById(long id) {
	return jdbcTemplate.queryForObject(
		"select id, firstname, lastname, salary " +
		"from employee where id=?",
		new RowMapper<Employee>() {
			public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
				Employee employee = new Employee();
				employee.setId(rs.getLong("id"));
				employee.setFirstName(rs.getString("firstname"));
				employee.setLastName(rs.getString("lastname"));
				employee.setSalary(rs.getBigDecimal("salary"));
				return employee;
				}
		},
		id);
}

Gradle构建Java程序:Task

下列内容来自《Building and Testing with Gradle》。

Gradle运行生命周期如下:

  • Configuration phase: 运行configuration blocks
  • Execution phase:运行task actions

Task action定义

在Gradle中,task是build activity的基本单元,由一系列的构建指令组合而成。Task是first-class object。

Task Action使用task<<来定义:

1
2
3
task hello << {
println 'hello, world'
}

其中<<在此处表示为hello任务添加一系列的操作(append a code block to the list of actions a task performs)。

上述表达式可以用下面的命令来等价:

1
2
3
4
5
6
7
8
9
task hello

hello << {
print 'hello, '
}

hello << {
println 'world'
}

Task配置

Task的定义与配置很像,差别在于task配置中不使用<<

1
2
3
task initializeDatabase
initializeDatabase << { println 'task' }
initializeDatabase { println 'configuration}

上述第1条声明一个task,第2条添加operation到task,第3条是task的配置。

运行上述build文件,可以得到下列结果:

1
2
3
4
$ gradle -b scratch.gradle initializeDatabase
configuration
:initializeDatabase
task

Task是对象

Tasks are Objects。和不同的对象一样,a task object可以含有方法(methods)和属性(properties)。

前述定义的Task,属于DefaultTask,有以下一些方法。

dependsOn

被依赖的task会先执行。可以有不同的表示方法,结果是等效的。下面示例中定义的task依赖于两个其它task(只显示了4种方法):

1
task world(dependsOn: [compileTestClasses, createSchema])
1
2
task world
world.dependsOn compileTestClasses, createSchema
1
2
3
task world {
dependsOn compileTestClasses, createSchema
}
1
2
3
4
task world {
dependsOn << compileTestClasses
dependson << createSchema
}

doFirst

doFirst中的代码会在对应的task action之前执行;多个doFirst出现时,后出现的先运行,即遵照FILO规则。

1
2
3
4
5
6
7
task setupDatabaseTest << {
println 'load test data'
}

setupDatabaseTest.doFirst {
println 'create schea'
}
1
2
3
4
5
6
7
8
9
task setupDatabaseTest << {
println 'load test data'
}

setupDatabaseTest{
doFirst {
println 'create schea'
}
}

doLast

doLast中的代码会在对应的task action之后执行。多个doLast出现时,先出现的先运行,即遵照FIFO规则。doLast用法与doFirst类似。

1
2
3
4
5
6
7
task setupDatabaseTest << {
println 'create database schema'
}

setupDatabaseTest.doLast {
println 'load test data'
}

Task type

除了DefaultTask,task还有其它一些type。在task名字后面添加(type: typeName)即可指定task type。

Copy

Copy task从一个目录拷贝文件到另一个目录。

1
task copyFiles(type: Copy) {
  from 'resources'
  into 'target'
  include '**/*.xml', '**/*.txt', '**/*.properties'
}

Jar

从源文件生成jar文件

1
2
apply plugin: 'java'
task customJar(type: Jar) { manifest { attributes firstKey: 'firstValue', secondKey: 'secondValue' } archiveName = 'hello.jar' destinationDir = file("${buildDir}/jars") from sourceSets.main.classes }

Gradle构建Java程序:Gradle常用命令

Gradle通用命令格式:

1
gradle [option ...] [task ...]

运行gradle相关命令后,可以看到依次执行的tasks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

帮助命令:

1
2
gradle help
gradle --help

执行完整的项目构建(生成class文件、jar文件、执行单元测试等):

1
gradle build

编译源代码,并生成jar文件(不执行单元测试):

1
gradle assemble

删除构建目录:

1
gradle clean

编译源代码:

1
gradle classes

编译测试类的代码:

1
gradle testClasses

生成jar文件:

1
gradle jar

生成Javadoc API文档:

1
gradle javadoc

显示项目的properties

1
gradle properties

显示当前项目的可运行task的完整列表:

1
gradle tasks

通过这种方式,不需要阅读构建脚本,就能对项目进行大致的浏览。由于Java插件在构建中自动加入了很多任务,因此上述命令可以看到build文件中之外的其它一些task。

在某个项目下,上述命令运行后,terminal显示如下信息:

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
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'testJunit'.
components - Displays the components produced by root project 'testJunit'. [incubating]
dependencies - Displays all dependencies declared in root project 'testJunit'.
dependencyInsight - Displays the insight into a specific dependency in root project 'testJunit'.
help - Displays a help message.
model - Displays the configuration model of root project 'testJunit'. [incubating]
projects - Displays the sub-projects of root project 'testJunit'.
properties - Displays the properties of root project 'testJunit'.
tasks - Displays the tasks runnable from root project 'testJunit'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 3.786 secs

Gradle构建Java程序:Junit集成

Build文件

build.gradle如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apply plugin: 'java'

repositories {
mavenCentral()
}

dependencies {
testCompile 'junit:junit:4.12'
}

jar {
manifest {
attributes 'Main-Class': 'com.shizhihua.example.Calculator'
}
}

上述配置指定仓库后,不需要额外再安装Junit4了。

Java文件

Calculator.java

1
2
3
4
5
6
7
8
9
10
package com.shizhihua.example;

public class Calculator {
public int evaluate(String expression) {
int sum = 0;
for (String summand: expression.split("\\+"))
sum += Integer.valueOf(summand);
return sum;
}
}

测试文件

CalculatorTest.java

  • 测试文件和类是在被测类名后添加Test
  • @Test表示需要运行的测试函数
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.shizhihua.example;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class CalculatorTest {
@Test
public void evaluatesExpression() {
Calculator calculator = new Calculator();
int sum = calculator.evaluate("1+2+3");
assertEquals(6, sum);
}
}

项目结构

为了Gradle,项目结构需要按照下面约定。注意被测的源java文件和测试例的java文件目录相对应:

  • src/main/java目录包含项目源代码
  • src/main/resources目录包含项目的资源
  • src/test/java目录包含项目测试类
  • src/test/resource目录包含项目测试资源

执行测试:

1
gradle test

运行上述命令后,整个项目的目录结构如下:

打开上述index.html可以看到测试结果的统计报告。

设置多进程

当项目中的测试例过多,需要通过并行的方法来加快运行速度。但是,如果每个unit test都需要自己独立的JVM,则系统overhead消耗大。Gradle采用了一种这种方法:设定maxParallelForks,限定最大的并发JVMs。

如果一个JVM不断运行unit test,可能会引起性能问题(e.g.,leak)。Gradle设定forkEvery,使得一个test-running JVM运行完设定的test数目后会结束,并启动一个新的test-running JVM来替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apply plugin: 'java'

repositories {
mavenCentral()
}

dependencies {
testCompile 'junit:junit:4.12'
}

jar {
manifest {
attributes 'Main-Class': 'com.shizhihua.example.Calculator'
}
}

test {
maxParallelForks = 5
forkEvery = 50
}

Gradle构建Java程序:入门

安装Gradle

1
brew install gradle

查看版本

1
gradle -v

本机安装版本为Gradle 2.12

Java程序构建

Gradle文件

在当前目录下生成文件build.gradle:

1
apply plugin: 'java'

项目结构

项目的目录结构需要按照下列固定约束(最里面几层对应package):

  • src/main/java目录包含项目源代码
  • src/main/resources目录包含项目的资源
  • src/test/java目录包含项目测试类
  • src/test/resource目录包含项目测试资源

本例中,没有测试相关内容,因此后两个目录没有。

Java文件:

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
package com.shizhihua.example;

import java.net.*;

public class OReillyByName {
public static void main(String[] args) {
String host = "www.baidu.com";
try {
InetAddress add = InetAddress.getLocalHost();
System.out.println(add);
} catch (UnknownHostException ex) {
System.out.println("Couldn't not find the localhost");
}

try {
InetAddress adds[] = InetAddress.getAllByName(host);
for (InetAddress add:adds) {
System.out.println(add);
System.out.println(add.getHostName());
System.out.println(add.getHostAddress());
}
} catch (UnknownHostException ex) {
System.out.println("Couldn't not find " + host);
}
}
}

执行构建

1
gradle build

生成后的项目目录

执行构建后,Gradle会生出一个目录build

  • classes目录包含编译生成的.class文件
  • libs目录包含生成的jarwar文件

运行java程序

运行程序时,必须通过-cp指定CLASSPATH:

1
java -cp build/classes/main/ com/shizhihua/example/OReillyByName

jar配置

上面得到的jar文件运行时需要指定入口,即
libs中的jar文件,需要设置Main-Class属性的值,指定程序的入口点。为了运行简单,可以在gradle文件中直接配置好入口,即

1
2
3
4
5
6
7
apply plugin: 'java'

jar {
manifest {
attributes 'Main-Class': 'com.shizhihua.example.OReillyByName'
}
}

通过运行gradle build生产的jar文件,即可直接运行:

1
java -jar build/libs/testGradle.jar

Homebrew使用简介

Homebrew,简称为brew,是Mac上的软件包管理工具,类似于Debain中的apt-get

Homebrew的官网地址为: Homebrew

brew的安装与卸载

安装时不需要使用sudo。Homebrew不需要sudo可以正常安装软件,因为其认为sudo不完全。

安装命令如下:

1
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

卸载命令如下:

1
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)"

brew常用命令

通用命令

1
brew command [--verbose|-v] [options] [formula] ...

formula 就是对应要安装的软件。formularuby脚本,定义了安装对应软件的具体步骤和操作命令。

安装软件

1
brew install formula

卸载软件可以用下列两条命令之一

1
2
brew remove formula
brew uninstall formula

重装软件

1
brew reinstall formula

更新软件

1
brew upgrade [--cleanup] [formulae]

cleanup软件旧版本

1
brew cleanup

查看版本

1
brew --version

显示formula脚本代码

1
brew cat formula

更新Homebrew

1
brew update

罗列已安装的formula

1
brew list

查找软件

1
brew search text

其中text可以是正则表达式。

安装目录

通过brew安装的文件,可以在目录/usr/local/Cellar/下面找到。