java enum的使用以及字符串其字符串之间的转换

via:http://blog.csdn.net/xieyuooo/

文章简单,相信在很多网站都能搜索到Java enum枚举的使用方式;可能有些东西我当时在刚开始用的时候没找到,所以我写了这篇文章,例如:

大多数地方写的枚举都是给一个枚举然后例子就开始switch,可是我想说,我代码里头来源的数据不太可能就是枚举,通常是字符串或数字,比如一个SQL我解析后首先判定SQL类型,通过截取SQL的token,截取出来可能是SELECT、DELETE、UPDATE、INSERT、ALTER等等,但是都是字符串,此时我想用枚举就不行了,我要将字符串转换成枚举怎么转呢,类似的情况还有从数据库取出数据根据一些类型做判定,从页面传入数据,根据不同的类型做不同的操作,但是都是字符串,不是枚举,悲剧的是我很少看到有人写到这个东西;所以我把它写下来,希望有人能用到。

首先为什么要用枚举?我们在什么时候用枚举比较好,用枚举有啥优势?

我认为哈,当你在一些一个范畴类,并可列举,不变化的类型,用以指导程序向不同的地方路由,用枚举是较好的选择;

听起来有点绕,不过有个例子也许可以明白,例如:

我们可以列举下日常工作日所做的事情:

上班、开会、吃饭、睡觉等

我们可以列举医院五官科需要检查人的部位:

眼睛、鼻子、耳朵、嘴巴等

这些都是可以被列举的,且每种事情我们要用不同的方式去做

当然你可以说:

1、可以用动态方法分派,通过配置文件或annotation;

2、可以使用常量来达到类似的效果;

3、直接通过字符串的equals来表达,用if else来表达

如果用配置加方法分派来做,是灵活,便于修改;但是如果在很多不经常修改的参数上,我们用这中方式往往增加配置的负担,并且当你需要看系统逻辑的时候,需要需要一遍看配置一遍看代码;不过,如果参数是可动态变换的信息,用配置是正确的选择;

而常量的使用,通常在switch case的时候都是数字,字符串在java中是不能做switch case的,使用常量的目的比case 1、case 2 …这种增加了可读性;但是字符串数据也麻烦,除非再映射一次,那没那个必要,其实枚举也差不多是帮你映射了一次,只是它将代码封装了而已吧了,既然他弄好了,而且语法上支持,干嘛不用呢!其次,常量虽然增加了可读性,不过他没有范畴和管理类型的概念,即一个枚举的定义会定义个范畴,可以很好的将这个范围所需要的东西列举出来,而常量通常是些自己定义的一些池,放在一些公共类中或随机定义,都是比较零散的,并且枚举在switch的时候就明确定义好了就在锁列举的范围内case,既可以控制好系统,增加可读性,并且可以随时查看这个范畴的枚举信息到底有那些,达到类似看配置文件的作用;不过还是回到那句话,如果参数是可变的,那么就不适合做枚举,枚举是一定是可列举的,或者说当前系统考虑范围是可以被枚举的,例如上面的医院五官科,可能还有很多没有列举到,但是当前医院只处理几个部位,不处理其他的,就是这个道理;什么是可变的呢,例如URL参数来分派到对应方法,不可能大家加一段逻辑就去加一个枚举,加一个case,此时用【配置 动态方法分派】更好,当然配置可以用文件或annotation而已。

还有最土的就是,通过字符串equals,用if else来实现,呵呵,这个并没有什么不好,只是这个写比较零散,其次,字符串匹配的equals每次匹配都需要对比每个字符,如果你的代码中大量循环,性能并不是很好,其余的看看上面的描述就更加清楚了;

其次,枚举提供一种类型管理的组件,让面向对象的体系更加完善,使得一些类型的管理既可配置化,并可以管理,在使用枚举的地方都可以沿着枚举的定义找到那些有处理过,那些没处理过,而上述几种很难做到;例如,数据库的操作类型定义了10种,那么再判定的过程中就可以讲枚举像配置文件一样看待,而又非常简单的来管理。

最后,枚举绝对是单例的,对比的性能和数字性能相当,既可以得到可读性,也可以得到性能。

我们先定义个简单枚举(这里只是个例子,就简单定义3个变量了):

public enum SqlTypeEnum {  
    INSERT ,   
    UPDATE ,  
    DELETE ,  
    SELECT  
}

此时解析SQL后,获取出来一个token,我们要获取这个token的枚举怎么获取呢?

这样获取:

String token = "select";  
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf(token.toUpperCase());

如果没获取到,java会抛出一个异常哦:IllegalArgumentException No enum const class SqlTypeEnum.XXX

我做大写处理的原因是因为枚举也是大写的(当然如果你的枚举是小写的,那你就小写,不过混写比较麻烦哈),其实valueOf就是调用了枚举的底层映射:

java enum的使用以及字符串其字符串之间的转换

调用的时候会调用这个方法:

java enum的使用以及字符串其字符串之间的转换

所以内部也是一个HashMap,呵呵!

拿到这个信息后,就可以做想要的操作了:

switch(sqlTypeEnum) {  
  case INSERT:处理insert逻辑;break;  
  case DELETE:处理delete逻辑;break;  
 ....  
}

OK,有些时候可能我们不想直接用INSERT、UPDATE这样的字符串在交互中使用,因为很多时候命名规范的要求;

例如定义一些用户操作类型:

1、保存用户信息

2、通过ID获取用户基本信息

3、获取用户列表

4、通过ID删除用户信息

等等

我们可能定义枚举会定义为:

public enum UserOptionEnum {  
    SAVE_USER,  
    GET_USER_BY_ID,  
    GET_USER_LIST,  
    DELETE_USER_BY_ID  
}

但是系统的方法和一些关键字的配置,通常会写成:

saveUser、getUserById、getUserById、deleteUserById

当然各自有各自的规则,不过中间这层映射,你不想做,就一方面妥协,要么枚举名称全部换掉,貌似挺奇怪的,要么方法名称全部换掉,更加奇怪,要么自己做映射,可以,稍微麻烦点,其实也不麻烦?

我们首先写个将枚举下划线风格的数据转换为驼峰的方法,放在一个StringUtils里面:

public static String convertDbStyleToJavaStyle(String dbStyleString , boolean firstUpper) {  
        dbStyleString = dbStyleString.toLowerCase();  
        String []tokens = dbStyleString.split("_");  
        StringBuilder stringBuilder = new StringBuilder(128);  
        int length = 0;  
        for(String token : tokens) {  
            if(StringUtils.isNotBlank(token)) {  
                if(length == 0 && !firstUpper) {  
                    stringBuilder.append(token);  
                }else {  
                    char c = token.charAt(0);  
                    if(c >= \'a\' || c <= \'z\') c = (char)(c - 32);  
                    stringBuilder.append(c);  
                    stringBuilder.append(token.substring(1));  
                }  
            }  
              length;  
        }  
        return stringBuilder.toString();  
    }

重载一个方法:

public static String convertDbStyleToJavaLocalStyle(String dbStyleString) {  
        return convertDbStyleToJavaStyle(dbStyleString , false);  
    }

然后定义枚举:

public enum UserOptionEnum {  
    SAVE_USER,  
    GET_USER_BY_ID,  
    GET_USER_LIST,  
    DELETE_USER_BY_ID;  
  
    private final static Map ENUM_MAP = new HashMap<String, UserOptionEnum>(64);  
  
  
    static {  
        for(UserOptionEnum v : values()) {  
            ENUM_MAP.put(v.toString() , v);   
        }  
    }  
  
    public staticUserOptionEnum fromString(String v) {  
        UserOptionEnum userOptionEnum = ENUM_MAP.get(v);  
        return userOptionEnum == null ? DEFAULT :userOptionEnum;  
    }  
  
    public String toString() {  
        String stringValue = super.toString();  
        return StringUtil.convertDbStyleToJavaLocalStyle(stringValue);  
    }  
}

OK,这样传递一个event参数让如果是:saveUser,此时就用:

String event = "saveUser";//假如这里得到参数  
UserOptionEnum enum = UserOptionEnum.fromString(event);

其实就是自己做了一个hashMap,我这加了一个fromString,因为枚举有一些限制,有些方法不让你覆盖,比如valueOf方法就是这样。

其实没啥好讲的了,非要说,再说说枚举加一些自定义变量吧,其实枚举除了是单例的外,其余的和普通类也相似,它也可以有构造方法,只是默认情况下不是而已,也可以提供自定义的变量,然后获取set、get方法,但是如果有set的话,线程不是安全的哦,要注意这点;所以一般是构造方法就写好了:

public enum SqlTypeEnum {  
   INSERT("insert into"),  
   DELETE("delete from")  
   ......省略;  
  
   private String name;//定义自定义的变量  
  
   private SqlTypeEnum(String name) {  
      this.name = name;  
   }  
  
   public String getName() {  
       return name;  
   }  
  
   public String toString() {  
       return name   " 我靠";//重写toString方法  
  }  
  //一般不推荐  
  public void setName(String name) {  
        this.name = name;  
  }  
}

 

调用下:

SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");  
System.out.println(sqlTypeEnum);  
System.out.println(sqlTypeEnum.getName());

不推荐也调用下:

sqlTypeEnum.setName("我靠");  

在另一个线程:

SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");  
System.out.println(sqlTypeEnum);  
System.out.println(sqlTypeEnum.getName());

发现结果被改了,呵呵!

 

fiddler无法抓取chrome解决方法

前端开发中,不可避免的要和服务器端进行联调,少了fiddler这个利器可不行。

由于无线开发需要配置UA,我使用chrome进行访问,但是今儿一早过来,发现fiddler无法抓取chrome的请求了。

想想昨天对chrome进行的操作,我觉得问题应该出在代理的身上。

实际上fiddler是可以抓chrome的请求的。
由于我的chrome安装了代理管理的插件SwitchySharp,无论选择直接连接还是选择使用代理连接,插件都会屏蔽fiddler的设置。
fiddler会自动给浏览器设置一个代理127.0.0.1 端口8888,并且记忆浏览器的代理设置,所有的请求先走fiddler代理,再走浏览器代理。
如果使用插件,可能会直接屏蔽了fiddler的代理,因此无法监听到请求了。

chrome下的解决方法,代理插件选择“使用系统代理设置”选项,fiddler又重新能看到chrome的请求了。
或者不使用插件,不用卸载,chrome很方便禁用一个插件。然后使用浏览器默认的代理设置方式就ok了。

使用代理插件是为了方便切换代理,但是可能会导致fiddler等工具无法使用。真是鱼和熊掌不可兼得。

Orika 教程以及与Spring的集成

via: http://malsolo.com/  又来挖坑了,找了一圈,只有这篇文字比较详细。得空翻译一下

I love good software, and Orika is a really interesting project. In this post I’m going to talk about this Java bean mapping framework that I’ve used recently and I highly recommend.

Orika (formerly hosted at google code) claims to be a simpler, lighter and faster Java bean mapping. And it really is.

It allows you to convert objects by copying its attributes from one to another. It performs this by using Java introspection, instead of XML configuration or something like that. It uses code generation to create the mappers, and it has an interesting strategy for its core classes that allows you to optimize the performance.

It is completely configurable, for instance, the java code generator by default is Javassist, but you can use EclipseJdt or even use another provider by implementing the appropriate interface.

Finally, it’s a very well documented project, with clear explanations and useful code.

I really like it.

If you want to try it, just add the next dependency to your maven project:

Or download the library along with three required libraries:

  • javassist (v 3.12.0+)
  • slf4j (v 1.5.6+)
  • paranamer (v 2.0+)

Main concepts

There are two core classes, the first one is MapperFactory, the class to configure the mappings and to obtain the second core class: MapperFacade, that actually provides the service of a Java bean mapping.

MapperFactory

By using a fluent API, you can construct a MapperFactory via its main implementation,DefaultMapperFactory and its static class helpers intended for building theMapperFactory.

Then you can obtain a ClassMapBuilder from the MapperFactory in order to declare the mapping configuration in a fluent-style, that is: mapping fields (bi-directional by default, or in one direction if you’d rather), excluding fields, specifying constructors and customizing mappings of Lists, Sets, nested fields and so on.

There is a default mapping that maps fields with matching names between two classes, and you can also register the mapping in the MapperFactory (the ClassMapBuilder is built with the very MapperFactory as atribute). Both operations have the appropriate methods to be called.

Let’s see an example:

We have two classes: PersonDTO and Person, and we want to convert one object for the former class to an object of the latter one, so let’s configure the MapperFactory:

The ClassMapBuilder provides interesting mappings for excluding fields, mapping fields in one direction only (as I mentioned above, mapping is by default bi-directional) and so on. See the Declarative Mapping Configuration and Advanced Mapping Configurations for more options.

MapperFacade and BoundMapperFacade

The MapperFacade performs the actual mapping work, for doing so, you have to obtain the MapperFacade from the MapperFactory and then use its map methods. There are two general options, map(objectA, B.class), which will create a new instance of B.class and map the property values of objectA onto it, or map(objectA, objectB), which copies properties from objectA onto objectB, being both of them not null.

Let’s see an example:

The BoundMapperFacade is intended to be used with a pair of types to map and no further customization needed. It provides improved performance over use of the standardMapperFacade and it has the same usage plus a mapReverse method for mapping in the reverse direction.

See below an example:

If you’re primarily concerned with the mapping of a particular type to another, and your object graph has no cycles, use BoundMapperFacade because it has better performance, since you avoid the overhead of looking up an appropriate mapping strategy for the fields of the objects to be mapped, that happens when you call the map method.

Performance

The most expensive operations are instantiation and initialization of the MapperFactory, and the MapperFacade which is obtained from it.

Both of them are thread-safe objects, thus singletons or static access for them are a very recommended approach.

For instance, you can consider an static acces to the MapperFactory:

And then use this class for registering class maps and access to the MapperFacade in a customize way:

In this case I’m using two new classes: Customer and CustomerDTO.

Finally, just use this helper classes:

How it works and Customization

The best way to understand how Orika works is to read the FAQ, but the source code is very clear and easy to follow, just in case you want to take a deeper look to the details.

“Orika uses reflection to investigate the metadata of the objects to be mapped and then generates byte-code at runtime which is used to map those classes […]”

“Orika mapping is configured via Java code at runtime, via a fluent-style ClassMapBuilder API. An instance is obtained from a MapperFactory which is then used to describe the mapping from one type to another. Finally, the specified ClassMap is registered with the MapperFactory for later use in generating a byte-code mapper.”

To configure Orika, the author recommends to extend the ConfigurableMapper class, and I will use this approach for using Orika with Spring (see below)

If you want to configure the code generation, compilation and so on, see how to do this using the MapperFactory in the next topic.

MapperFactory Configuration

The core class MapperFactory is instantiated as you’ve seen above, by building the provided implementation: DefaultMapperFactory. It uses a set of Strategy and factory objects for resolving constructors, for compiling code (actually, they belong to its inner static class MapperFactoryBuilder that is extended by its other inner static class that you use to instantiate the MapperFactory: the Builder), for resolving properties, and specially for building class maps.

So you can customize all of them after calling Builder() and before the build() method:

Custom Mappers

A Mapper<A, B> copies the properties from one object onto another and it’s used internally by Orika (MapperFacade, MapperGenerator and MapperFactory use it, but it isn’t really interesting for this post, actually)

If you want to take control of how properties of one object instance are copied to another object instance (not null), just extends the abstract class CustomMapper<A,B>.

Then, you can register your custom mapper in the MapperFactory via its methodregisterMapper(Mapper<A, B> mapper).

It’s intended to be used when the mapping behavior configured via the ClassMapBuilder API doesn’t suit your purposes, but only to copy properties, that is, the destination object already exists. If you want to control instantiation, use a CustomConverter or ObjectFactory which both return an instance. See below.

If you need to map nested members of the given object, you can doing so by using the current MapperFacade that it’s accessible via a protected mapperFacade variable.

I’ve used converters in a Spring applications as components that can be scanned and used to configure the MapperFactory by extending ConfigurableMapper as I mentioned above and I’ll show below.

Object Factories

An ObjectFactory<D> instantiates objects. You can implement this interface and register your implementation in the MapperFactory via their method registerObjectFactory (there are a couple of them)

I’ve never used this option.

Custom Converters

A Converter<S, D> combines both ObjectFactory and Mapper together, returning a new instance of the destination type with all properties copied from the source instance.

When you want to take complete control over the instantiation and mapping, just extendCustomConverter<S, D> to create the new instance and to provide it the properties from the source object.

Then, you can register your custom converter in the MapperFactory by obtaining itsConverterFactory via the getConverterFactory() method and then using one of itsregisterConverter() methods.

I’ve found this option useful when you want to create some hard conversion between classes that has the same meaning in your code but are not really related in terms of Java. Well, it’s a conversion, let’s say, from a date represented in a String and a date that has to be a Calendar. You have to use some formatter for doing so.

It’s also very easy to configure converters as Spring components if you need so.

Using Orika with Spring

Since the Orika core classes MapperFactory and the MapperFacade obtained from it are very expensive to instantiate and initialize, but at the same time are thread-safe, they should be shared as singletons. That is, they are good candidates for being Spring beans with singleton scope.

Let’s start with using spring-framework in a maven project,just add the next dependency to the pom.xml file:

The Spring application

Let’s create a couple of beans to obtain a Person and a PersonDTO, let’s call them service and repository respectively:

And their implementations, the one for the service will use the repository interface, and the one for the repository will be a simple in-memory implementation.

Note that the service will need to convert from the DTO to the domain object, so let’s inject a mapper.

As you can see, I used setter injection (via autowiring) to provide a MapperFactory in order to obtain from it a MapperFacade. I’m using the suggestion of having the MapperFactory as singleton, that it’s easy to achieve with Spring.

Since MapperFactory has a particular way to be instantiated, the best option is to create a factory for exposing it, a FactoryBean:

This approach has been selected just in case I want to use a BoundMapperFacade, but it’s possible to use a MapperFacade directly by creating a MapperFacadeFactory.

Now, let’s configure the Spring application

This is enough for the project to run:

Person{id=0, name=Name 0, surnames=null, address=null}

But pay attention, the mapper didn’t work, because there are differences between the two classes (tow attributes with different names, and the same concept, address, with different types)

Plugging in Orika Mappers and Converters with Spring

Orika provides custom converters and mappers, so the only thing we need is to configure them as spring beans in the application context. Let’s see how to do this.

First, we need a custom mapper. We annotate it as component to be discovered (via component scanning) by Spring.

Finally, we have to plug in mappers and converters that belongs to the Spring application context, into the MapperFactory. If we extend ConfigurableMapper we will have a MapperFactory created for us, and later on, the chance to access the application context itself to look up for the custom mappers and converters that are Spring components to register them in the MapperFactory.

Let’s show the code:

Some important things to note here:

    • Our class SpringConfigurableMapper is a @Component

When Spring instantiates it, it will call its constructor that is inherited fromConfigurableMapper and it calls an init method that creates a MapperFactory and allows you to configure via the overriden method.

So, the first method called is configure(MapperFactory).

Furthermore, don’t create your own MapperFactory as I did above, because it won’t be the one you’ll use to register the mappers and converters, on the contrary, this class will create a new MapperFactory.

    • Our class SpringConfigurableMapper has the ApplicationContext @Autowired

It’s the same as if you implement ApplicationContextAware, but I’d rather this approach.

Besides, it implies that the method will be called after all the beans have been created. Since you already have a MapperFactory, you can then search for custom components and converters that are spring beans and register them in the MapperFactory.

    • Our class extends ConfigurableMapper that implements MapperFacade

So, you have to slightly change the PersonServiceImpl and it’s better to not have a MapperFacadeFactory or you will have an autowiring exception because there would be two beans of the MapperFacade class:

Now, if you run the Orika with Spring main class, OrikaSpringTest, everything will run as expected:

Person{id=85, name=Name 85, surnames=[S., Surname 85], address=Address{street=My street 85, city=City 85, zipCode=code 85}}

 

Resources

Java 多线程

随着多核芯片逐渐成为主流,大多数软件开发人员不可避免地需要了解并行编程的知识。而同时,主流程序语言正在将越来越多的并行特性合并到标准库或者语言本身之中。我们可以看到,JDK 在这方面同样走在潮流的前方。从JDK 5到JDK 7,越来越多的线程相关的新API加入到了标准库中,为不同场景下对线程实现与调度提供了完善的支持。我们可以充分利用这些类库,来提高开发效率,改善程序性能。

Java中使用线程

Java的多线程基于以下两个重要的机制:

  1. 父线程结束,如果子线程正在运行,子线程不会跟着结束。
  2. 每个线程拥有一个线程栈,会维护引用,用于GC。

Java提供了以下两种常用的方式来使用多线程:

  1. 继承Thread
  2. 实现Runnable接口

继承Thread

class MyThread extends Thread {
    @Override
    public void run() {
    
    } 
}

Thread t = new MyThread();
t.start();

 

实现Runnable接口

class MyThread implements Runnable {
    @Override
    public void run() {
    
    }
}

MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.start();

 

需要注意的是:一个线程只能start()一次,如果多次调用start()方法,会出现Java.lang.IllegalStateException

两种方式的区别在于继承Thread方式是每次都会创建一个MyThread对象,而如果采用实现Runnable接口的方式,可以使用同一个MyThread对象来创建多个线程,便于线程间共享内存。因此,设计线程间的数据共享时,一般应当采用实现Runnable接口的方式。

Callable与Future

java.util.concurrent包中还提供了一个Callable接口来实现线程,使用Callable需要与Future类配合使用。

public class TEMP {
    public static void main(String[] args) throws Exception {
         FutureTask<Integer> task = new FutureTask<>(new FF());
         new Thread(task).start();
         System.out.println("main thread");
         System.out.println(task.get());
    }
}

class FF implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(2000);
        System.out.println("ff thread");
        return new Random().nextInt(100);
    }
}

 

程序运行的输出为:

main thread
ff thread
34

 

Callable接口与Runnable接口类似,不同的是,call方法是可以有返回值的,并且可以抛出异常。如果没有System.out.println(task.get()),那么主线程在运行完System.out.println("main thread")就会结束,但是因为有task.get(),因此,主线程会等待Callable线程执行完毕。

Callable-Future类似的一个方法是回调(callback)。在子线程结束时调用父线程的方法来通知父线程。

一般Callable/Future与ExecutorService配合使用。使用ExecutorService来运行Callable线程:

ExecutorService exec = Executors.newCachedThreadPool();
// exec = Executors.newFixedThreadPool(3 /* nThread */);
exec.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        // ...
        return null;
    }
});
// exec.shutdown();

 

ExecutorService也可以用来执行实现Runnable线程:

exec.submit(new Runnable() {
    @Override
    public void run() throws Exception {
        // ...
    }
});

 

使用ExecutorService时,可以通过FutureTask来获取Callable线程call方法的返回值:

ExecutorService exec = Executors.newCachedThreadPool();
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        // ...
        return null;
    }
});
exec.submit(task);
Integer result = task.get();

 

ExecutorService还可以取消(cancel)一个Task(FutureTask)。

简单的线程同步

Java提供了强制原子性的内置锁机制:synchronized块。一个synchronized块有两部分:所对象的引用以及这个锁保护的代码块。内置锁在Java中扮演了互斥锁(mutex)的角色。内置锁匙可重入的(Reentrant)。通常使用synchronized关键字来进行线程间的同步。synchronized有两种用法:修饰方法和修饰代码块。

修饰方法

可以使用synchronized关键字来修饰类中的方法,从而达到线程间同步的目的。示例:

class A {
    public synchronized void Func() {
        // ...
    }
}

 

synchronized关键字修饰方法表明该方法在运行期间将会对this加锁,也就是说,当方法Func运行时其他方法无法操作this对象。需要注意的是,synchronizedstatic关键字是可以共存的。

修饰代码块

synchronized关键字除了用来修饰方法,还可以用来修饰代码块:

class A {
    Integer i;
    // ...
    public void func() {
        // ...
        synchronized(i) {
            // ...
        }
    }
}

 

这表明在synchronized中的代码块运行期间将会占有锁this.isynchronized修饰方法等价于下列写法:

class A {
    public void Func() {
        synchronized(this) {
            // ...
        }
    }
}

 

可以看到,synchronized修饰代码块与修饰方法类似,但控制粒度更细,同步开销也较小。但是,在尽量减小同步代码块规模时应该注意原子操作中的语句必须在同一个同步代码块中。

静态方法同步

要同步静态方法,需要一个作用于整个类对象的锁。这个锁就是类自身。举例:

class A {
    public static void func() {
        synchronized(B.class) {
            // ...
        }
    }
}

 

可重入锁

另一个需要注意的地方是Java的同步锁匙可重入锁,例如,递归调用run方法或者调用的函数有同样的synchronized锁并不会造成死锁。示例如下:

class KK extends Thread {
    static Integer x = 10;
    @Override
    public void run() {
        synchronized (x) {
            if(x > 5) {
                x -= 1;
                System.out.println(x);
                run();
            }
        }
    }
}

 

wait, notify, notifyAlljoin是常用的线程控制调度方法。

共享对象可见性和volatile

一种常见的错误观念认为只有写入共享变量是才需要同步,其实并非如此。volatile是一个声明,volatile关键字表示内存可见,volatile声明的变量不会被copy到线程的本地内存(缓存、缓冲区、寄存器,Cache等)中,而是直接在主存上进行操作。每次使用前从主存中读取新值,修改后立即写入主存,这用于防止线程间因寄存器和缓存引起的肮脏数据的读取和使用。

进一步的,JVM的规范里并没有要求volatile关键字修饰的变量一定不能被copy到线程的本地内存中,而是要求对其读写需要遵循happens-before语义,类似于互斥锁。同时,volatile还涉及到编译器的指令重排和CPU的重排序等等(影响变量之间的访问顺序)。

有些时候,可能一个线程只需要一个变量的值,这时也有可能需要使用volatile的方式来防止出现同步错误。但需要注意的是,volatile不能和final一起共同修饰一个变量,因为final变量是不可改的。

Lock/Condition机制

Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中,通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。

使用Lock的一般框架:

Lock lock = new ReetrantLock();
lock.lock();
try {
    // ...
}
finally {
    lock.unlock();
}

 

注意,try中的语句块是可能抛出异常的,因此,如果可能有异常抛出,那么lock.unlock()必须在finally语句块中,以确保JVM会释放锁。通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象。

Condition的功能更类似于传统多线程技术中的Object.wait()(Condition.await())和Object.notifyAll()(Condition.signal()),用于实现线程间通信,一个Lock锁可以支持多个Condition。

一个使用Lock/Condition机制的例子:

class AA implements Runnable {
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        lock.lock();
        System.out.println("get lock");
        // ...
        System.out.println("release lock");
        lock.unlock();
    }
}

 

在这里面,可以配合使用Condition完成较复杂的线程管理和调度。在Java多线程之Condition接口的实现一文中,作者使用Condition/Lock机制来实现了经典的“生产者-消费者”调度模型。在很多情景下,Condition提供了一种更加高效的、更有针对性的线程调度和同步方式。

BlockingQueue的使用

BlockingQueue,阻塞队列。BlockingQueue是一种特殊的Queue,若BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态直到BlocingkQueue进了新货才会被唤醒。同样,如果BlockingQueue是满的任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有新的空间才会被唤醒继续操作。

BlockingQueue提供的方法主要有:

  1. add(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则抛出IllegalStateException异常。
  2. offer(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则返回false。
  3. put(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue没有空间,调用此方法的线程被阻断直到BlockingQueue里有新的空间再继续。
  4. poll(time): 取出BlockingQueue里排在首位的对象,若不能立即取出可等time参数规定的时间。取不到时返回null。
  5. take(): 取出BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。

根据不同的需要BlockingQueue有4种具体实现:

  1. ArrayBlockingQueue

规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。

  1. LinkedBlockingQueue

大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制。若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO(先入先出)顺序排序的。LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue。

  1. PriorityBlockingQueue

类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

  1. SynchronousQueue

特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

BlockingQueue特别适用于线程间共享缓冲区的场景。BlockingQueue的四种实现也能够满足大多数的缓冲区调度需求。

Fork/Join

Fork/Join机制是JDK 7新增加的多线程框架,如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。下图可大概说明Fork/Join模式的结构:

Fork/Join模式

Fork/Join框架的几个核心类:

  1. ForkJoinPool: 线程池实现。实现了Work-stealing(工作窃取)算法,线程会主动寻找新创建的任务去执行,从而保证较高的线程利用率。程序运行时只需要唯一的一个ForkJoinPool。
  2. ForkJoinTask: 任务类,有两个子类实现具体的Task:
    1. RecursiveAction: 无返回值;
    2. RecursiveTask: 有返回值。

使用Fork/Join

首先,我们需要为程序创建一个ForkJoinPool:

Creates a ForkJoinPool with parallelism equal to java.lang.Runtime.availableProcessors,

private static ForkJoinPool pool = new ForkJoinPool();

 

这儿ForkJoinPool的构造函数有重载方法,可以通过参数设置线程数量(并行级别,parallelism level)以及ThreadFactory。

Task类需要实现compute方法,ForkJoinTask的代码框架:

If (problem size > default size){
    task s = divide(task);
    execute(tasks);
} 
else {
    resolve problem using another algorithm;
}

 

无返回值Task的示例

通过并行,将一个数组中每个元素的值都置为其索引值,即令a[i] = i

class Task {
    // Creates a ForkJoinPool with parallelism equal to
    // java.lang.Runtime.availableProcessors,
    private static ForkJoinPool pool = new ForkJoinPool();
    private static final int default_size = 10;

    public void solve() {
        System.out.println("available processor number: " +
                java.lang.Runtime.getRuntime().availableProcessors());
        int[] a = new int[100];
        SubTask task = new SubTask(a, 0, 100);
        System.out.println("task start!");
        pool.invoke(task);
        System.out.println("task finish!");
    }

    class SubTask extends RecursiveAction {
        int[] a;
        int l, r;

        public SubTask(int[] a, int l, int r) {
            this.a = a;
            this.l = l;
            this.r = r;
        }

        @Override
        protected void compute() {
            System.out.println("Thread id: " + Thread.currentThread().getId());
            if (r - l > BB.default_size) {
                int mid = (l + r) / 2;
                invokeAll(new SubTask(a, l, mid + 1), new SubTask(a, mid, r));
            }
            else {
                for (int i = l; i < r; ++i) {
                    a[i] = i;
                }
            }
        }
    }
}

 

有返回值的Task

如果子任务有返回值,只需要改成继承RecursiveTask类,然后compute方法返回对应类型的返回值即可。例如:

class SubTask extends RecursiveTask<Integer> {
    public Integer compute() {
        // ...
    }
}

SubTask task = new SubTask(...);
Integer result = task.get()

 

在fork子任务时,只需要:

SubTask t1 = new SubTask(...);
SubTask t2 = new SubTask(...);
invokeAll(t1, t2);
try {
    result = t1.get()+t2.get();  
} catch (InterruptedException | ExecutionException e) {  
    e.printStackTrace();  
}
return result;

 

异步执行Task

上面两个示例都是同步执行的,invoke与invokeAll都是阻塞当前线程的。当Task线程运行时会阻塞父线程,而在很多场合,我们需要Task线程异步执行。这是需要使用到execute或者submit方法。execute方法直接执行task,而submit方法是将task提交到任务队列里边去。而shutdown方法则表示线程池不再接收新的task(ForkJoinPool是有守护线程的)(shutdown之后再submit后产生RejectedExecutionException)。ForkJoinPool线程池提供了execute()方法来异步启动任务,而作为任务本身,可以调用fork()方法异步启动新的子任务,并调用子任务的join()方法来取得计算结果。通过通过task.isDone()方法来判断任务是否结束。

public boolean allDone(List<SubTask> tasks) {
    for(SubTask task: tasks) {
        if(!task.isDone()) {
            return false;
        }
    }
    return true;
}

 

当使用Fork/Join框架时,如果主线程(main方法)先于task线程结束了,那么task线程也会结束,而不会等待执行完。这也是与Java中传统的Thread/Runnable有区别的地方。至于原因,应该是主线程(main方法)结束导致ForkJoinPool的守护线程结束了。此外,ForkJoinPoolawaitTermination方法也值得注意。execute/submit/fork/join也可以与invoke/invokeAll配合使用,来调整线程间的阻塞关系。

JDK 7 中的 Fork/Join 模式一文中,作者使用Fork/Join模式实现了并行快速排序算法,很值得参考。其实Fork/Join与Callable/Future/ExecutorService挺像的,ExecutorService与ForkJoinPool的API也很类似,ForkJoinPool比ExecutorService多出了Work-stealing(工作窃取)算法的调度,线程池和服务(Service)的概念对应地也能很好。在Fork/Join机制中,Task也是可以取消(cancel)的。

invoke与fork差异

这两个方法有很大的区别,当使用同步方法,调用这些方法(比如:invokeAll()方法)的任务将被阻塞,直到提交给线程池的任务完成它的执行。这允许ForkJoinPool类使用work-stealing算法,分配一个新的任务给正在执行睡眠任务的工作线程。反之,当使用异步方法(比如:fork()方法),这个任务将继续它的执行,所以ForkJoinPool类不能使用work-stealing算法来提高应用程序的性能。在这种情况下,只有当你调用join()或get()方法来等待任务的完成时,ForkJoinPool才能使用work-stealing算法。

Work-stealing

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。核心思想在于以下两点:

  1. 将一个任务分解为多个互不依赖的子任务。
  2. 当一个线程完成自己队列中的任务后,将其他线程的任务队列里的任务取出来执行。

Work-stealing算法示意图

Disruptor机制

单核情形下提高CPU利用率。

参考

  1. Java 理论与实践: 正确使用 Volatile 变量
  2. 用happen-before规则重新审视DCL
  3. Java多线程之Condition接口的实现
  4. ReentrantLock(重入锁)以及公平性
  5. JDK 7 中的 Fork/Join 模式
  6. java 7 fork-join framework and closures
  7. 聊聊并发(八)——Fork/Join框架介绍
  8. Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
  9. Fork and Join: Java Can Excel at Painless Parallel Programming Too!
  10. Java线程之fork/join框架
  11. Java中不同的并发实现的性能比较
  12. Java并发的四种风味:Thread、Executor、ForkJoin和Actor

via: http://sighingnow.github.io

App兼容性设计

App兼容性设计

从今年的4月份开始,一直在开发公司的android app项目,到现在终于告一段落(轻松了点,不用每天加班加点的)。所以有闲趣来写这篇文章。

现在互联网APP开发都处于快速迭代开发的状态,所以APP能正常升级到最新版本是比较重要的事情。这里从服务器和移动端的角度来谈谈如果做到APP升级的兼容性设计。

基本概念

这里写一些通用的概念:

APP版本废弃: 活跃用户不在这个被废弃的APP版本,并且服务器限制该版本APP使用。

服务器

在保持兼容的过程中,服务器的兼容性还是比较好处理的,因为服务器的升级完全处于我们自己的控制中,不会出现服务器处于不同的版本状态。所以,只要处理好数据库,HTTP接口,推送协议,基本能保证APP升级无副作用。

数据库

在业务的开发过程中,不可避免的就是添加新的字段和删除原来旧的字段,对于新的字段添加,基本上可以保证旧的业务正常的运行,而删除字段,则可能导致旧的业务不能正常的运行,所以需要慎重选择。当然我们也不是不能删除字段,一般的做法是先保留旧的字段,然后等待APP版本废弃之后就可以进行删除该字段了。

HTTP接口

    对于HTTP接口, 还是采用 module.online.com/v1/service.do 的格式进行编码。其中添加v1的主要好处在于,能保证前后的兼容性。比如说,业务中出现一个重大的改动,为了保证旧业务依然可以使用,那么,我们就开放module.online.com/v2/service.do 进行开发。 现在只需要等待APP版本废弃后,就可以关闭V1的服务了。而交互的数据报文通常采用JSON格式:{code:-1 | 0 | 1 ,msg: Any}

推送协议

开发移动APP的时候,不可避免的就是使用长连接推送一些及时消息,比如说新闻,广告,强制下线通知等。为了保证兼容性,通常需要在协议中添加固定的版本号如: { module:msg,  version:1, op:pushMsg, extra:{msg:”hello world”}}。 当APP端接收到推送的时候。采用如下的步骤处理推送:

  1. 判断业务模块(module)是否能处理

  2. 判断协议版本(version)能否处理

  3. 判断OP是否能进行处理

  4. extra中是给定的额外数据

处理过程中,注意使用

App兼容性设计

避免处理过程中,导致APP奔溃。

可以发现,这个协议有些类似 HTTP接口。其实就是HTTP接口的协议化,处理过程由tomcat变成了手动了而已。

移动端

移动端升级,主要涉及到本地数据(数据库,磁盘文件),APP全局升级补丁,协议兼容性(HTTP,推送),以及APP版本检测,而JAVA代码和资源文件,当然就不需要进行处理了。

为了使得APP有一个统一的版本管理,我们首先需要一个统一的标识,即版本号。

  版本名称(versionName): 通常使用x.y.z的格式,x为主版本号,y为副版本号,z为修复补丁版本号。而版本名称是供外部使用的。

版本代码(versionCode):  版本代码是用于系统内部使用的,毕竟x.y.z不是非常好的支持版本更新。 通常采用算法:versionCode = x * 100000 y * 1000 z 的方式。这种算法,基本保证了不同版本之间不会出现versionCode冲突的问题。

数据库

数据库升级还是比较方便的,这里我们采用APP版本代码(versionCode)作为SQLite的版本号,SQLite已经为我们提供好了onUpgrade接口,在升级的时候,SQLite会给出旧的版本号,和最新的版本号,此时我们只要做App兼容性设计

就可以保证对数据库的正确升级了。

App全局升级补丁

除了数据库升级,对于磁盘文件,升级后的引导页面等需要处理的杂项,可以在APP全局升级补丁中进行处理。比如说,某个版本,可以删除某些磁盘文件,那么,就可以在全局补丁中编写代码。

具体的做法为:

  1. 每次打开APP初始化application的时候onCreate(),读取记录在本地数据库的APP版本号和APK中的versionCode进行对比

  2. 如果不一致,那么,就有可能是升级后,重新打开APP的情况,那么,我们就可以进行升级补丁操作

  3. 升级完成后, 把最新的APK中的versionCode写入本地数据库,避免下一次打开APP的时候,重复升级。

升级代码大致如下:

App兼容性设计

可以发现,使用versionCode作为升级代码点,能比较清晰的区分出不同版本做了什么事情。而且对于从比较老的版本直接升级过来,也是OK的。

协议兼容性

之前,我们就写了一下服务器对于升级兼容性的要求,而APP端可能还需要做出特殊的处理。

例子一 : 朋友圈主题不同类型

在最早的时候,朋友圈还是只能处于发发图片和文字的状态,后来加入了一些额外的元素,如 音乐,视频,图文等内容。然而在加入了新的主题类型后,旧的用户不一定会马上升级,所以要保持朋友圈的兼容性。有大致两个做法:

    方案一:新开API 版本,切换到V2

    方案二:APP端做特殊工作。

方案一,就不阐述了,基本工作都在服务器。我们来说一下方案二。 首先我们发现,业务中出现变化的是新添加了几种不同的主题类型,所以我们只要做到对不能识别类型进行过滤,就可以保证之后扩张主题类型后,旧用户依旧可以使用APP。SO,开发这种具有明显类型变化的业务场景的时候,都会引入一个TYPE字段,用来标记类型。如果发现不能识别的TYPE,APP直接抛弃该数据,既可以保证旧版本依旧可以使用,并且也不会导致服务器进行大改。

协议兼容性,在API开发过程中,还是比较重要的内容,需要对业务抽象处理。

APP版本检测

APP版本检测和是升级是比较核心的功能模块。主要实现的功能有:

        1. 定时检测 APP 是否需要升级,并且进行提示。

        2. 判断用户是否可以继续使用该APP,即当前APP需要强制升级。

所以,APP检测的时候,给定服务器最新的版本信息数据即可,格式基本是这个样子:

App兼容性设计

versionName:最新版本名称 x.y.z

versionCode:  最新版本代码

deadVersionCode:如果APP#versionCode <= Server#deadVersionCode,需要进行强制升级,不能再使用其他功能。

当检测到新的APP版本后,还需要在界面上进行ALERT,通知用户进行升级。强制升级的话,需要禁用所有的功能(避免APP奔溃)。然后进行ALERT用户。

APP版本检测功能,应该算是APP快速迭代的保证。可以让服务端来控制APP是否可以继续使用和升级。

总结

写了这么多,大致阐述了这几个月对APP兼容设计的工作经验, 如有不对之处,望指正。

 

via: http://my.oschina.net/

OWNER:Java配置文件解决方案

OWNER是一个Java库,目标是最大限度的减少应用程序中处理Java properties的代码。

自动草稿

主要功能

OWNER的设计遵循以下座右铭: 功能丰富的API,新功能添加的同时不忘保持极简的基本用法。

  • 加载策略:OWNER通过匹配接口类名和properties文件名自动解析并映射;也可以通过注解定制properties文件名。
  • 导入properties:另外一种加载properties文件到映射接口的方法。
  • 参数化properties:另外一个实用功能,给接口方法提供参数,通过参数配置。
  • 类型转换:支持从String类型到基本类型和枚举类型的转换。
  • 变量扩展:引用properties中的其他属性。
  • 热加载:支持热加载。
  • 可访问和可变:可以继承Config的子接口Accessible或者Mutable实现属性的可访问和可变。
  • 调试:支持调试功能。
  • 禁用功能:可禁用引起问题的功能。
  • 配置ConfigFactory:ConfigFactory也是可配置的。
  • XML支持:支持XML配置。
  • 事件支持:OWNER实现了功能丰富的事件系统,使你知道热加载的发生和属性变化。
  • 单例模式:配置信息在一个应用中是单例的。

官方文档

官方网站:http://owner.aeonbits.org/
开源地址:https://github.com/lviggiano/owner

Java资源大全中文版

from: https://github.com

我想很多程序员应该记得 GitHub 上有一个 Awesome – XXX 系列的资源整理。awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容包括:构建工具、数据库、框架、模板、安全、代码分析、日志、第三方库、书籍、Java 站点等等。伯乐在线已经把 awesome-java 资源列表翻成中文后发布于 ImportNew

Awesome 系列虽然挺全,但基本只对收录的资源做了极为简要的介绍,如果有更详细的中文介绍,对相应开发者的帮助会更大。这也是我们发起这个开源项目的初衷。


我们要做什么?


如何参与本项目?

从下面的目录来看,本项目的工作量小不了,所以非常期待能有更多程序员一起来参与。

不过加入前,有几个小要求:

  • 英文还不错,能读懂英文并用自己的话复述;
  • 在用 Java;

如有兴趣,请加 QQ:50872495。加 Q 时请注明「Java大全」


本项目的参与者

注:名单不分排名,不定期补充更新


目录

古董级工具

这些工具伴随着Java一起出现,在各自辉煌之后还在一直使用。

  • Apache Ant:基于XML的构建管理工具。
  • cglib:字节码生成库。
  • GlassFish:应用服务器,由Oracle赞助支持的Java EE参考实现。
  • Hudson :持续集成服务器,目前仍在活跃开发。
  • JavaServer Faces:Mojarra是JSF标准的一个开源实现,由Oracle开发。
  • JavaServer Pages:支持自定义标签库的网站通用模板库。
  • Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。

构建工具

构建及应用依赖关系处理工具。

  • Apache Maven :Maven是一款声明式构建及依赖管理工具,采用约定优于配置方式进行管理。相对Apache Ant更推荐使用Maven,前者采用了过程式管理,维护相对困难。
  • Bazel:来自Google的构建工具,可以快速、可靠地构建代码。
  • Gradle:使用Groovy(非XML)进行增量构建,可以很好地与Maven依赖管理配合工作。

字节码操作

编程方式操作字节码的开发库。

  • ASM:通用底层字节码操作和分析开发库。
  • Byte Buddy:使用流式API进一步简化字节码生成。
  • Byteman:在运行时通过DSL(规则)操作字节码进行测试和故障排除。
  • Javassist:一个简化字节码编辑尝试。

集群管理

在集群内动态管理应用程序的框架。

  • Apache Aurora:Apache Aurora是一个Mesos框架,用于长时间运行服务和定时任务(cron job)。
  • Singularity:Singularity是一个Mesos框架,方便部署和操作。它支持Web Service、后台运行、调度作业和一次性任务。

代码分析

测量代码指标和质量工具。

  • Checkstyle:代码编写规范和标准静态分析工具。
  • Error Prone:将常见编程错误作为运行时错误报告。
  • FindBugs:通过字节码静态分析查找隐藏bug。
  • jQAssistant:使用基于Neo4J查询语言进行代码静态分析。
  • PMD:对源代码分析查找不良的编程习惯。
  • SonarQube:通过插件集成其它分析组件,对过去一段时间内的数据进行统计。

编译器生成工具

用来创建解析器、解释器或编译器的框架。

  • ANTLR:复杂的全功能自顶向下解析框架。
  • JavaCC:JavaCC是更加专门的轻量级工具,易于上手且支持语法超前预测。

外部配置工具

支持外部配置的开发库。

  • config:针对JVM语言的配置库。
  • owner:减少冗余配置属性。

约束满足问题求解程序

帮助解决约束满足问题的开发库。

  • Choco:可直接使用的约束满足问题求解程序,使用了约束规划技术。
  • JaCoP:为FlatZinc语言提供了一个接口,可以执行MiniZinc模型。
  • OptaPlanner:企业规划与资源调度优化求解程序。
  • Sat4J:逻辑代数与优化问题最先进的求解程序。

持续集成

  • Bamboo:Atlassian解决方案,可以很好地集成Atlassian的其他产品。可以选择开源许可,也可以购买商业版。
  • CircleCI:提供托管服务,可以免费试用。
  • Codeship:提供托管服务,提供有限的免费模式。
  • fabric8:容器集成平台。
  • Go:ThoughtWork开源解决方案。
  • Jenkins:支持基于服务器的部署服务。
  • TeamCity:JetBrain的持续集成解决方案,有免费版。
  • Travis:通常用作开源项目的托管服务。

CSV解析

简化CSV数据读写的框架与开发库

  • uniVocity-parsers:速度最快功能最全的CSV开发库之一,同时支持TSV与固定宽度记录的读写。

数据库

简化数据库交互的相关工具。

  • Apache Phoenix:HBase针对低延时应用程序的高性能关系数据库层。
  • Crate:实现了数据同步、分片、缩放、复制的分布式数据存储。除此之外还可以使用基于SQL的语法跨集群查询。
  • Flyway:简单的数据库迁移工具。
  • H2:小型SQL数据库,以可以作为内存数据库使用著称。
  • HikariCP:高性能JDBC连接工具。
  • JDBI:便捷的JDBC抽象。
  • jOOQ:为SQL schema生成typesafe代码。
  • MapDB:以磁盘或堆内存中并发集合为基础的嵌入式数据库引擎。
  • Presto:针对大数据的分布式SQL查询引擎。
  • Querydsl:Typesafe统一查询。

数据结构

  • Apache Parquet:Google Dremel论文中发布的基于组装算法的列式(Columnar)存储格式。
  • Protobuf:Google数据交换格式。
  • SBE:简单二进制编码,是最快速的消息格式之一。
  • Wire:整洁轻量级协议缓存。

时间日期工具库

处理时间和日期的开发库。

  • Joda-Time:在Java 8发布前,Joda-Time是实际使用的时间日期库标准。
  • Time4J:高级时间和日期库。

依赖注入

帮助实现依赖翻转范式的开发库。

  • Apache DeltaSpike:CDI扩展框架。
  • Dagger2:编译时注入框架,不需要使用反射。
  • Guice:可以匹敌Dagger的轻量级注入框架。
  • HK2:轻量级动态依赖注入框架。

开发流程增强工具

从最基本的层面增强开发流程。

  • ADT4J:针对代数数据类型的JSR-269代码生成器。
  • AspectJ:面向切面编程(AOP)的无缝扩展。
  • Auto:源代码生成器集合。
  • DCEVM:通过修改JVM在运行时支持对已加载的类进行无限次重定义。
  • HotswapAgent:支持无限次重定义运行时类与资源。
  • Immutables:类似Scala的条件类。
  • JHipster:基于Spring Boot与AngularJS应用程序的Yeoman源代码生成器。
  • JRebel:无需重新部署,可以即时重新加载代码与配置的商业软件。
  • Lombok:减少冗余的代码生成器。
  • Spring Loaded:类重载代理。
  • vert.x:多语言事件驱动应用框架。

分布式应用

用来编写分布式容错应用的开发库和框架。

  • Akka:用来编写分布式容错并发事件驱动应用程序的工具和运行时。
  • Apache Storm:实时计算系统。
  • Apache ZooKeeper:针对大型分布式系统的协调服务,支持分布式配置、同步和名称注册。
  • Hazelcast:高可扩展内存数据网格。
  • Hystrix:提供延迟和容错。
  • JGroups:提供可靠的消息传递和集群创建的工具。
  • Orbit:支持虚拟角色(Actor),在传统角色的基础上增加了另外一层抽象。
  • Quasar:为JVM提供轻量级线程和角色。

分布式数据库

对应用程序而言,在分布式系统中的数据库看起来就像是只有一个数据源。

  • Apache Cassandra:列式数据库,可用性高且没有单点故障。
  • Apache HBase:针对大数据的Hadoop数据库。
  • Druid:实时和历史OLAP数据存储,在聚集查询和近似查询方面表现不俗。
  • Infinispan:针对缓存的高并发键值对数据存储。

发布

以本机格式发布应用程序的工具。

  • Bintray:发布二进制文件版本控制工具。可以于Maven或Gradle一起配合使用。提供开源免费版本和几种商业收费版本。
  • Central Repository:最大的二进制组件仓库,面向开源社区提供免费服务。Apache Maven默认使用Central Repository,也可以在所有其他构建工具中使用。
  • IzPack:为跨平台部署建立创作工具(Authoring Tool)。
  • JitPack:打包GitHub仓库的便捷工具。可根据需要构建Maven、Gradle项目,发布可立即使用的组件。
  • Launch4j:将JAR包装为轻量级本机Windows可执行程序。
  • Nexus:支持代理和缓存功能的二进制管理工具。
  • packr:将JAR、资源和JVM打包成Windows、Linux和Mac OS X本地发布文件。

文档处理工具

处理Office文档的开发库。

  • Apache POI:支持OOXML规范(XLSX、DOCX、PPTX)以及OLE2规范(XLS、DOC、PPT)。
  • documents4j:使用第三方转换器进行文档格式转换,转成类似MS Word这样的格式。
  • jOpenDocument:处理OpenDocument格式(由Sun公司提出基于XML的文档格式)。

函数式编程

函数式编程支持库。

  • Cyclops:支持一元(Monad)操作和流操作工具类、comprehension(List语法)、模式匹配、trampoline等特性。
  • Fugue:Guava的函数式编程扩展。
  • Functional Java:实现了多种基础和高级编程抽象,用来辅助面向组合开发(composition-oriented development)。
  • Javaslang:一个函数式组件库,提供持久化数据类型和函数式控制结构。
  • jOOλ:旨在填补Java 8 lambda差距的扩展,提供了众多缺失的类型和一组丰富的顺序流API。

游戏开发

游戏开发框架。

  • jMonkeyEngine:现代3D游戏开发引擎。
  • libGDX:全面的跨平台高级框架。
  • LWJGL:对OpenGL/CL/AL等技术进行抽象的健壮框架。

GUI

现代图形化用户界面开发库。

高性能计算

涵盖了从集合到特定开发库的高性能计算相关工具。

  • Agrona:高性能应用中常见的数据结构和工具方法。
  • Disruptor:线程间消息传递开发库。
  • fastutil:快速紧凑的特定类型集合(Collection)。
  • GS Collections:受Smalltalk启发的集合框架。
  • HPPC:基础类型集合。
  • Javolution:实时和嵌入式系统的开发库。
  • JCTools:JDK中缺失的并发工具。
  • Koloboke:Hash set和hash map。
  • Trove:基础类型集合。

IDE

简化开发的集成开发环境。

  • Eclipse:老牌开源项目,支持多种插件和编程语言。
  • IntelliJ IDEA:支持众多JVM语言,是安卓开发者好的选择。商业版主要针对企业客户。
  • NetBeans:为多种技术提供集成化支持,包括Java SE、Java EE、数据库访问、HTML5等。

图像处理

创建、评价和操作图片的支持库。

  • Imgscalr:纯Java 2D实现,简单、高效、支持硬件加速的图像缩放开发库。
  • Picasso:安卓图片下载和图片缓存开发库。
  • Thumbnailator:Thumbnailator是一个高质量Java缩略图开发库。
  • ZXing:支持多种格式的一维、二维条形码图片处理开发库。

JSON

简化JSON处理的开发库。

  • Genson:强大且易于使用的Java到JSON转换开发库。
  • Gson:支持在对象与JSON之间双向序列化,性能良好且可以实时调用。
  • Jackson:与GSON类似,在频繁使用时性能更佳。
  • LoganSquare:基于Jackson流式API,提供对JSON解析和序列化。比GSON与Jackson组合方式效果更好。

JVM与JDK

目前的JVM和JDK实现。

  • JDK 9:JDK 9的早期访问版本。
  • OpenJDK:JDK开源实现。

基于JVM的语言

除Java外,可以用来编写JVM应用程序的编程语言。

  • Scala:融合了面向对象和函数式编程思想的静态类型编程语言。
  • Groovy:类型可选(Optionally typed)的动态语言,支持静态类型和静态编译。目前是一个Apache孵化器项目。
  • Clojure:可看做现代版Lisp的动态类型语言。
  • Ceylon:RedHat开发的面向对象静态类型编程语言。
  • Kotlin:JetBrain针对JVM、安卓和浏览器提供的静态类型编程语言。

日志

记录应用程序行为日志的开发库。

  • Apache Log4j 2:使用强大的插件和配置架构进行完全重写。
  • kibana:分析及可视化日志文件。
  • Logback:强健的日期开发库,通过Groovy提供很多有趣的选项。
  • logstash:日志文件管理工具。
  • Metrics:通过JMX或HTTP发布参数,并且支持存储到数据库。
  • SLF4J:日志抽象层,需要与具体的实现配合使用。

机器学习

提供具体统计算法的工具。其算法可从数据中学习。

  • Apache Flink:快速、可靠的大规模数据处理引擎。
  • Apache Hadoop:在商用硬件集群上用来进行大规模数据存储的开源软件框架。
  • Apache Mahout:专注协同过滤、聚类和分类的可扩展算法。
  • Apache Spark:开源数据分析集群计算框架。
  • DeepDive:从非结构化数据建立结构化信息并集成到已有数据库的工具。
  • Deeplearning4j:分布式多线程深度学习开发库。
  • H2O:用作大数据统计的分析引擎。
  • Weka:用作数据挖掘的算法集合,包括从预处理到可视化的各个层次。

消息传递

在客户端之间进行消息传递,确保协议独立性的工具。

  • Aeron:高效可扩展的单播、多播消息传递工具。
  • Apache ActiveMQ:实现JMS的开源消息代理(broker),可将同步通讯转为异步通讯。
  • Apache Camel:通过企业级整合模式(Enterprise Integration Pattern EIP)将不同的消息传输API整合在一起。
  • Apache Kafka:高吞吐量分布式消息系统。
  • Hermes:快速、可靠的消息代理(Broker),基于Kafka构建。
  • JBoss HornetQ:清晰、准确、模块化,可以方便嵌入的消息工具。
  • JeroMQ:ZeroMQ的纯Java实现。
  • Smack:跨平台XMPP客户端函数库。

杂项

未分类其它资源。

  • Design Patterns:实现并解释了最常见的设计模式。
  • Jimfs:内存文件系统。
  • Lanterna:类似curses的简单console文本GUI函数库。
  • LightAdmin:可插入式CRUD UI函数库,可用来快速应用开发。
  • OpenRefine:用来处理混乱数据的工具,包括清理、转换、使用Web Service进行扩展并将其关联到数据库。
  • RoboVM:Java编写原生iOS应用。

应用监控工具

监控生产环境中应用程序的工具。

  • AppDynamics:性能监测商业工具。
  • JavaMelody:性能监测和分析工具。
  • Kamon:Kamon用来监测在JVM上运行的应用程序。
  • New Relic:性能监测商业工具。
  • SPM:支持对JVM应用程序进行分布式事务追踪的性能监测商业工具。
  • Takipi:产品运行时错误监测及调试商业工具。

原生开发库

用来进行特定平台开发的原生开发库。

  • JNA:不使用JNI就可以使用原生开发库。此外,还为常见系统函数提供了接口。

自然语言处理

用来专门处理文本的函数库。

  • Apache OpenNLP:处理类似分词等常见任务的工具。
  • CoreNLP:斯坦佛CoreNLP提供了一组基础工具,可以处理类似标签、实体名识别和情感分析这样的任务。
  • LingPipe:一组可以处理各种任务的工具集,支持POS标签、情感分析等。
  • Mallet:统计学自然语言处理、文档分类、聚类、主题建模等。

网络

网络编程函数库。

  • Async Http Client:异步HTTP和WebSocket客户端函数库。
  • Grizzly:NIO框架,在Glassfish中作为网络层使用。
  • Netty:构建高性能网络应用程序开发框架。
  • OkHttp:一个Android和Java应用的HTTP+SPDY客户端。
  • Undertow:基于NIO实现了阻塞和非阻塞API的Web服务器,在WildFly中作为网络层使用。

ORM

A处理对象持久化的API。

  • Ebean:支持快速数据访问和编码的ORM框架。
  • EclipseLink:支持许多持久化标准,JPA、JAXB、JCA和SDO。
  • Hibernate:广泛使用、强健的持久化框架。Hibernate的技术社区非常活跃。
  • MyBatis:带有存储过程或者SQL语句的耦合对象(Couples object)。
  • OrmLite:轻量级开发包,免除了其它ORM产品中的复杂性和开销。

PDF

用来帮助创建PDF文件的资源。

  • Apache FOP:从XSL-FO创建PDF。
  • Apache PDFBox:用来创建和操作PDF的工具集。
  • DynamicReports:JasperReports的精简版。
  • flyingsaucer:XML/XHTML和CSS 2.1渲染器。
  • iText:一个易于使用的PDF函数库,用来编程创建PDF文件。注意,用于商业用途时需要许可证。
  • JasperReports:一个复杂的报表引擎。

性能分析

性能分析、性能剖析及基准测试工具。

响应式开发库

用来开发响应式应用程序的开发库。

  • Reactive Streams:异步流处理标准,支持非阻塞式反向压力(backpressure)。
  • Reactor:构建响应式快速数据(fast-data)应用程序的开发库。
  • RxJava:通过JVM可观察序列(observable sequence)构建异步和基于事件的程序。

REST框架

用来创建RESTful 服务的框架。

  • Dropwizard:偏向于自己使用的Web框架。用来构建Web应用程序,使用了Jetty、Jackson、Jersey和Metrics。
  • Feign:受Retrofit、JAXRS-2.0和WebSocket启发的HTTP客户端连接器(binder)。
  • Jersey:JAX-RS参考实现。
  • RESTEasy:经过JAX-RS规范完全认证的可移植实现。
  • RestExpress:一个Java类型安全的REST客户端。
  • RestX:基于朱洁处理和编译时源码生成的框架。
  • Retrofit:类型安全的REST客户端。
  • Spark:受到Sinatra启发的Java REST框架。
  • Swagger:Swagger是一个规范且完整的框架,提供描述、生产、消费和可视化RESTful Web Service。

科学计算与分析

用于科学计算和分析的函数库。

  • DataMelt:用于科学计算、数据分析及数据可视化的开发环境。
  • JGraphT:支持数学图论对象和算法的图形库。
  • JScience:用来进行科学测量和单位的一组类。

文档索引引擎,用于搜索和分析。

  • Apache Solr:一个完全的企业搜索引擎。为高吞吐量通信进行了优化。
  • Elasticsearch:一个分布式、支持多租户(multitenant)全文本搜索引擎。提供了RESTful Web接口和无schema的JSON文档。

安全

用于处理安全、认证、授权或会话管理的函数库。

  • Apache Shiro:执行认证、授权、加密和会话管理。
  • Bouncy Castle:多用途加密开发库。支持JCA提供者(JCA provider),涵盖了从基础的帮助函数到PGP/SMIME操作。
  • Cryptomator:在云上进行客户端跨平台透明加密。
  • Keycloak:为浏览器应用和RESTful Web Service集成SSO和IDM。目前还处于beta版本,但是看起来非常有前途。
  • PicketLink:PicketLink是一个针对Java应用进行安全和身份认证管理的大型项目(Umbrella Project)。

序列化

用来高效处理序列化的函数库。

  • FlatBuffers:高效利用内存的序列化函数库,无需解包和解析即可高效访问序列化数据。
  • Kryo:快速、高效的对象图形序列化框架。
  • FST:提供兼容JDK的高性能对象图形序列化。
  • MessagePack:一种高效的二进制序列化格式。

应用服务器

用来部署应用程序的服务器。

  • Apache Tomcat:针对Servlet和JSP的应用服务器,健壮性好且适用性强。
  • Apache TomEE:Tomcat加Java EE。
  • Jetty:轻量级、小巧的应用服务器,通常会嵌入到项目中。
  • WebSphere Liberty:轻量级、模块化应用服务器,由IBM开发。
  • WildFly:之前被称作JBoss,由Red Hat开发。支持很多Java EE功能。

模板引擎

在模板中替换表达式的工具。

  • Apache Velocity:提供HTML页面模板、email模板和通用开源代码生成器模板。
  • FreeMarker:通用模板引擎,不需要任何重量级或自己使用的依赖关系。
  • Handlebars.java:使用Java编写的模板引擎,逻辑简单,支持语义扩展(semantic Mustache)。
  • Thymeleaf:旨在替换JSP,支持XML文件的工具。

测试

测试内容从对象到接口,涵盖性能测试和基准测试工具。

  • Apache JMeter:功能性测试和性能评测。
  • Arquillian:集成测试和功能行测试平台,集成Java EE容器。
  • AssertJ:支持流式断言提高测试的可读性。
  • Awaitility:用来同步异步操作的DSL。
  • Cucumber:BDD测试框架。
  • Gatling:设计为易于使用、可维护的和高性能负载测试工具。
  • Hamcrest:可用来灵活创建意图(intent)表达式的匹配器。
  • JMockit:用来模拟静态、final方法等。
  • JUnit:通用测试框架。
  • Mockito:在自动化单元测试中创建测试对象,为TDD或BDD提供支持。
  • PowerMock: 支持模拟静态方法、构造函数、final类和方法、私有方法以及移除静态初始化器的模拟工具。
  • REST Assured:为REST/HTTP服务提供方便测试的Java DSL。
  • Selenide:为Selenium提供精准的周边API,用来编写稳定且可读的UI测试。
  • Selenium:为Web应用程序提供可移植软件测试框架。
  • Spock:JUnit-compatible framework featuring an expressive Groovy-derived specification language.兼容JUnit框架,支持衍生的Groovy范的语言。
  • TestNG:测试框架。
  • Truth:Google的断言和命题(proposition)框架。
  • Unitils:模块化测试函数库,支持单元测试和集成测试。
  • WireMock:Web Service测试桩(Stub)和模拟函数。

通用工具库

通用工具类函数库。

  • Apache Commons:提供各种用途的函数,比如配置、验证、集合、文件上传或XML处理等。
  • args4j:命令行参数解析器。
  • CRaSH:为运行进行提供CLI。
  • Gephi:可视化跨平台网络图形化操作程序。
  • Guava:集合、缓存、支持基本类型、并发函数库、通用注解、字符串处理、I/O等。
  • JADE:构建、调试多租户系统的框架和环境。
  • javatuples:正如名字表示的那样,提供tuple支持。尽管目前tuple的概念还有留有争议。
  • JCommander:命令行参数解析器。
  • Protégé:提供存在论(ontology)编辑器以及构建知识系统的框架。

网络爬虫

用于分析网站内容的函数库。

  • Apache Nutch:可用于生产环境的高度可扩展、可伸缩的网络爬虫。
  • Crawler4j:简单的轻量级网络爬虫。
  • JSoup:刮取、解析、操作和清理HTML。

Web框架

用于处理Web应用程序不同层次间通讯的框架。

  • Apache Tapestry:基于组件的框架,使用Java创建动态、强健的、高度可扩展的Web应用程序。
  • Apache Wicket:基于组件的Web应用框架,与Tapestry类似带有状态显示GUI。
  • Google Web Toolkit:一组Web开发工具集,包含在客户端将Java代码转为JavaScript的编译器、XML解析器、RCP API、JUnit集成、国际化支持和GUI控件。
  • Grails:Groovy框架,旨在提供一个高效开发环境,使用约定而非配置、没有XML并支持混入(mixin)。
  • Ninja:Java全栈Web开发框架。非常稳固、快速和高效。
  • Pippo:小型、高度模块化的类Sinatra框架。
  • Play:使用约定而非配置,支持代码热加载并在浏览器中显示错误。
  • PrimeFaces:JSF框架,提供免费和带支持的商业版本。包括若干前端组件。
  • Ratpack:一组Java开发函数库,用于构建快速、高效、可扩展且测试完备的HTTP应用程序。
  • Spring Boot:微框架,简化了Spring新程序的开发过程。
  • Spring:旨在简化Java EE的开发过程,提供依赖注入相关组件并支持面向切面编程。
  • Vaadin:基于GWT构建的事件驱动框架。使用服务端架构,客户端使用Ajax。

资源

社区

有影响力的书

具有广泛影响且值得阅读的Java经典书籍。

播客

可以一边编程一边听的东西。

微博、微信公众号

  • ImportNew:是最受欢迎的、专注Java技术分享的微信公众号。专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
  • ImportNew 微博:@ImportNew

Twitter

  • Adam Bien:自由职业者、作家、JavaONE明星演讲者、顾问、Java Champion。
  • Antonio Goncalves:Java Champion、JUG Leader、Devoxx France、Java EE 6/7、JCP、作家。
  • Arun Gupta:Java Champion、JavaONE明星演讲者、JUG Leader、Devoxx4Kids成员、Red Hatter。
  • Bruno Borges:Oracle产品经理、Java Jock。
  • Ed Burns:Oracle技术团队顾问。
  • Eugen Paraschiv:Spring安全课程作者。
  • James Weaver:Java、JavaFX、IoT开发者、作者和演讲者。
  • Java EE:Java EE Twitter官方账号。
  • Java Magazine:Java杂志官方账号。
  • Java.net:Java.net官方账号。
  • Java:Java Twitter官方账号。
  • Javin Paul:知名Java博客作者。
  • Lukas Eder:Data Geekery(jOOQ)创始人兼CEO。
  • Mario Fusco:RedHatter、JUG协调、活跃讲师和作者。
  • Mark Reinhold:Oracle首席架构师、Java平台开发组。
  • Martijn Verburg:London JUG co-leader、演讲者、作家、Java Champion等。
  • OpenJDK:OpenJDK官方账号。
  • Reza Rahman:Java EE、GlassFish、WebLogic传道者、作家、演讲者、开源黑客。
  • Simon Maple:Java Champion、virtualJUG创始人、LJC leader、RebelLabs作者。
  • Stephen Colebourne: Java Champion、演讲者。
  • Tim Boudreau:作家、NetBeans大牛。
  • Trisha Gee:Java Champion、演讲者。

知名网站

值得关注的Java技术站点。

中文站点

  • ImportNew(ImportNew 专注 Java 技术)

英文站点

使用油猴子配合CasperJS的开发调试

casperjs 可以执行网页内容,尤其在解析那些需要执行JS才能出现内容的网页特别有效。

但是实际情况是,casperjs在后台执行,你根本无法预知写出来的脚本的对错啊。

还好有另外途径,就是使用油猴子,提前将你写好的代码注入到网页中,然后可以获得在浏览器中调试的机会

// ==UserScript==
// @name         GDYD-B2B
// @namespace    http://your.homepage/
// @version      0.1
// @description  enter something useful
// @author       You
// @match        http://10.15.224.23:8081/*
// @require      http://lib.sinaapp.com/js/jquery/1.10.2/jquery-1.10.2.min.js
// @grant        none
// ==/UserScript==
function injectJs(link) {
    var scr = document.createElement('script');
    scr.type="text/javascript";
    scr.src=link;
    document.getElementsByTagName('head')[0].appendChild(scr)
    //document.body.appendChild(scr);
}
//debugger;//网页中如果有改内容的话,Chrome会在这个地方断点的
var t = (new Date()).getTime(); //避免开发时候的浏览器缓存文件
injectJs('http://10.133.96.175/test/b2b.js?t='+t);

这样的话,你只需要关注编写b2b.js的内容就可以了,将内容封装成可执行的function。 可以hen方便的将成功移植到后台。

使用Charles抓取App网络请求

最近开发App的时候需要用到大量其他应用的数据,但接口不公开,所以想到了抓取。差不多要读到5W的用户数据,采用的是找到数据接口,然后不停发请求的方式。用到的抓取工具是Charles,本文讲解的应用是WEAR,一款鬼子的应用。

前期准备:

下载Charles,本文不会讲解Charles的使用,不会用的可以看巧哥的文章

环境信息:

Mac OS X 10.10.3

Xcode 6.3

iOS 8.3

Charles 3.10.1

正文:

一、基本设置

在抓包之前,需要对Charles与iPhone进行基本设置,这些设置均转自唐巧的文章《截取iPhone上的网络封包》部分。

1. Charles上的设置

打开Charles->Proxy->Proxy Setting,设置代理端口为8888,并勾选Enable transparent HTTP proxying。

使用Charles抓取App网络请求

Charles配置

2. iPhone上的设置

1). 获取Mac上的IP

打开终端,输入命令:


ifconfig en0

找到里面的IP地址:

使用Charles抓取App网络请求

找到本机IP

2). 设置iPhone HTTP代理

打开iPhone->设置->无线局域网->当前连接的网络->手动,将上一步获得的服务器地址填入对应输入框,端口号填写8888即可。

使用Charles抓取App网络请求

iPhone配置

二、抓取WEAR中的数据

1. 打开WEAR

如果是第一次,可以看到Charles弹出的请求连接确认框,点击Allow。

使用Charles抓取App网络请求

第一次连接的确认框

2. 查看请求

可以看到,在进入WEAR以后,就开始进行主页模块的数据请求,这时就需要通过请求的响应次数来粗略判断下具体的请求地址了。在Charles中,每有请求响应,地址列表就会有黄色高亮。

使用Charles抓取App网络请求

黄色高亮表示有新的响应信息

我发现http://www.wearzozo.cn/coordinate/9k8r0s/这个地址闪得很频繁,打开查看以后可以看到这是主页模块所有的图片地址。不难看出,WEAR如此庞大的图片数据有独立的服务器作为支撑。

3. 查看用户数据

图片找到了并不能达到最终目的,因为我需要的5W用户的数据,所以继续找。然后找到了一个域名为http://api.wear.jp的请求地址,根据命名经验,可以很直观的看出这是一个专门给客户端提供接口的服务器地址。

展开v1发现里面有两个子目录ranking与timeline,这两个子目录应该分别对应了搭配与新动态模块。

下面就以ranking为例:

在Overview中可以看到ranking的请求地址为:

http://api.wear.jp/v1/ranking/snaps?pageno=1&pagesize=33&group_id=1&period=1&country_id=13

请求方式为:

GET

内容格式为:

application/json

使用Charles抓取App网络请求

ranking中的请求信息

然后在到Response中可以看到返回的数据格式是Json,并且从它“见名知意”的key中,可以直接推断出每一个字段的含义。

使用Charles抓取App网络请求

返回的Json值

三、保存信息到本地

既然已经找到的用户列表,那么抓取用户信息就easy了。可以写脚本来抓取,当然作为iOS开发者,也可以在Xcode中写一个发起网络请求的App来进行抓取,读到数据以后,保存到模拟器沙盒就行了。

具体的抓取步骤就不讲解了,也不清楚是否侵权,所以还是慎用得好。

四、Charles其他用法

关于Charles的其他功能,有空会在后续文章中讲解到。


via: http://www.brighttj.com/

iOS开发工具——网络封包分析工具Charles

iOS开发工具——网络封包分析工具Charles

简介

Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析。Charles通过将自己设置成系统的网络访问代理服务器,使得所有的网络访问请求都通过它来完成,从而实现了网络封包的截取和分析。

Charles是收费软件,可以免费试用30天。试用期过后,未付费的用户仍然可以继续使用,但是每次使用时间不能超过30分钟,并且启动时将会有10秒种的延时。

因此,该付费方案对广大用户还是相当友好的,即使你长期不付费,也能使用完整的软件功能。只是当你需要长时间进行封包调试时,会因为Charles强制关闭而遇到影响。

Charles主要的功能包括:

  1. 支持SSL代理。可以截取分析SSL的请求。
  2. 支持流量控制。可以模拟慢速网络以及等待时间(latency)较长的请求。
  3. 支持AJAX调试。可以自动将json或xml数据格式化,方便查看。
  4. 支持AMF调试。可以将Flash Remoting 或 Flex Remoting信息格式化,方便查看。
  5. 支持重发网络请求,方便后端调试。
  6. 支持修改网络请求参数。
  7. 支持网络请求的截获并动态修改。
  8. 检查HTML,CSS和RSS内容是否符合W3C标准

安装Charles

去Charles的官方网站(http://www.charlesproxy.com)下载最新版的Charles安装包,是一个dmg后缀的文件。打开后将Charles拖到Application目录 下即完成安装。

安装SSL证书

如果你需要截取分析SSL协议相关的内容。那么需要安装Charles的CA证书。具体步骤如下:

  1. http://www.charlesproxy.com/ssl.zip 下载CA证书文件。
  2. 解压该zip文件后,双击其中的.crt文件,这时候在弹出的菜单中选择“总是信任”,如下所示:iOS开发工具——网络封包分析工具Charles
  3. 从钥匙串访问中即可看到添加成功的证书。如下所示:iOS开发工具——网络封包分析工具Charles

将Charles设置成系统代理

之前提到,Charles是通过将自己设置成代理服务器来完成封包截取的,所以使用Charles的第一步是将其设置成系统的代理服务器。

启动Charles后,第一次Charles会请求你给它设置系统代理的权限。你可以输入登录密码授予Charles该权限。你也可以忽略该请求,然后在需要将Charles设置成系统代理时,选择菜单中的 “Proxy” -> “Mac OS X Proxy”来将Charles设置成系统代理。如下所示:

iOS开发工具——网络封包分析工具Charles

之后,你就可以看到源源不断的网络请求出现在Charles的界面中。

Charles主界面介绍

iOS开发工具——网络封包分析工具Charles

Charles主要提供2种查看封包的视图,分别名为“Structure”和”Sequence”。

  1. Structure视图将网络请求按访问的域名分类。
  2. Sequence视图将网络请求按访问的时间排序。

大家可以根据具体的需要在这两种视图之前来回切换。

对于某一个具体的网络请求,你可以查看其详细的请求内容和响应内容。如果响应内容是JSON格式的,那么Charles可以自动帮你将JSON内容格式化,方便你查看。

过滤网络请求

通常情况下,我们需要对网络请求进行过滤,只监控向指定目录服务器上发送的请求。对于这种需求,我们有2种办法。

  1. 在主界面的中部的Filter栏中填入需要过滤出来的关键字。例如我们的服务器的地址是:http://yuantiku.com,那么只需要在Filter栏中填入yuantiku即可。
  2. 在Charles的菜单栏选择”Proxy”->”Recording Settings”,然后选择Include栏,选择添加一个项目,然后填入需要监控的协议,主机地址,端口号。这样就可以只截取目标网站的封包了。如下图所示:

iOS开发工具——网络封包分析工具Charles

通常情况下,我们使用方法1做一些临时性的封包过滤,使用方法2做一些经常性的封包过滤。

截取iPhone上的网络封包

Charles通常用来截取本地上的网络封包,但是当我们需要时,我们也可以用来截取其它设备上的网络请求。下面我就以iPhone为例,讲解如何进行相应操作。

Charles上的设置

要截取iPhone上的网络请求,我们首先需要将Charles的代理功能打开。在Charles的菜单栏上选择“Proxy”->”Proxy Settings”,填入代理端口8888,并且勾上”Enable transparent HTTP proxying” 就完成了在Charles上的设置。如下图所示:

iOS开发工具——网络封包分析工具Charles

iPhone上的设置

首先我们需要获取Charles运行所在电脑的IP地址,打开Terminal,输入ifconfig en0, 即可获得该电脑的IP,如下图所示:

iOS开发工具——网络封包分析工具Charles

在iPhone的 “设置”->“无线局域网“中,可以看到当前连接的wifi名,通过点击右边的详情键,可以看到当前连接上的wifi的详细信息,包括IP地址,子网掩码等信息。在其最底部有“HTTP代理”一项,我们将其切换成手动,然后填上Charles运行所在的电脑的IP,以及端口号8888,如下图所示:

iOS开发工具——网络封包分析工具Charles

设置好之后,我们打开iPhone上的任意需要网络通讯的程序,就可以看到Charles弹出iPhone请求连接的确认菜单(如下图所示),点击“Allow”即可完成设置。

iOS开发工具——网络封包分析工具Charles

截取SSL信息

Charles默认并不截取SSL的信息,如果你想对截取某个网站上的所有SSL网络请求,可以在该请求上右击,选择SSL proxy,如下图所示:

iOS开发工具——网络封包分析工具Charles

这样,对于该Host的所有SSL请求可以被截取到了。

模拟慢速网络

在做iPhone开发的时候,我们常常需要模拟慢速网络或者高延迟的网络,以测试在移动网络下,应用的表现是否正常。Charles对此需求提供了很好的支持。

在Charles的菜单上,选择”Proxy”->”Throttle Setting”项,在之后弹出的对话框中,我们可以勾选上“Enable Throttling”,并且可以设置Throttle Preset的类型。如下图所示:

iOS开发工具——网络封包分析工具Charles

如果我们只想模拟指定网站的慢速网络,可以再勾选上图中的”Only for selected hosts”项,然后在对话框的下半部分设置中增加指定的hosts项即可。

修改网络请求内容

有些时候为了调试服务器的接口,我们需要反复尝试不同参数的网络请求。Charles可以方便地提供网络请求的修改和重发功能。只需要在以往的网络请求上点击右键,选择“Edit”,即可创建一个可编辑的网络请求。如下所示:

iOS开发工具——网络封包分析工具Charles

我们可以修改该请求的任何信息,包括url地址,端口,参数等,之后点击“Execute”即可发送该修改后的网络请求(如下图所示)。Charles支持我们多次修改和发送该请求,这对于我们和服务器端调试接口非常方便。

iOS开发工具——网络封包分析工具Charles

总结

通过Charles软件,我们可以很方便地在日常开发中,截取和调试网络请求内容,分析封包协议以及模拟慢速网络。用好Charles可以极大的方便我们对于带有网络请求的App的开发和调试。

参考链接:

  1. Charles主要的功能列表
  2. Charles官网

感谢李永伦对本文的审校。

via: http://www.infoq.com