Hibernate使用count(*)取得表中记录总数 - 风之音 - ITeye技术网站

mikel阅读(1163)

来源: Hibernate使用count(*)取得表中记录总数 – 风之音 – ITeye技术网站

Java代码  收藏代码
  1. /**
  2.   * @TODO:查询某一年度的所有计划数量
  3.   */
  4. public int findCountByYear(String currYear) {
  5.     String hqlString = “select count(*) from WaterPlan as p where p.planYear ='”+currYear+“‘”;
  6.     Query query = this.getSession().createQuery(hqlString);
  7.     return ((Number)query.uniqueResult()).uniqueResult();
  8. }

从Hibernate 3.0.x/3.1.x升级到最新的3.2版之后,3.2版的很多SQL函数如count(), sum()的唯一返回值已经从Integer变为Long,如果不升级代码,会得到一个ClassCastException。

这个变化主要是为了兼容JPA,可以在hibernate.org的最新文档中找到说明。

Hibernate Team也提供了一个与原来兼容的解决方案:

Java代码  收藏代码
  1. Configuration classicCfg = new Configuration();
  2. classicCfg.addSQLFunction( “count”new ClassicCountFunction());
  3. classicCfg.addSQLFunction( “avg”new ClassicAvgFunction());
  4. classicCfg.addSqlFunction( “sum”new ClassicSumFunction());
  5. SessionFactory classicSf = classicCfg.buildSessionFactory();

Java代码  收藏代码
  1. //int count = ((Integer)query.uniqueResult()).intValue(); 
  2. //改成 
  3. int count = ((Number)query.uniqueResult()).intValue();
  4. //这样就可以两个版本同时兼容. 

 

Java代码  收藏代码
  1. //参考代码
  2. //第一种方法:
  3.   String hql = “select count(*) from User as user”;
  4.   Integer count = (Integer)getHibernateTemplate().find(hql).listIterator().next();
  5.   return count.intValue();
  6. //第二种方法:
  7.  String hql = “select count(*) from User as user”;
  8.   return ((Integer)getHibernateTemplate().iterate(hql).next()).intValue();
  9. //第三种方法:
  10.  String hql = “select count(*) from User as user”;
  11.  Query query =  getHibernateTemplate().createQuery( getSession(),hql);
  12.  return ((Integer)query.uniqueResult()).intValue();

REST Web Service开发实践--Jersey,Google Protocol Buffer, Tomcat结合样例 - - ITeye技术网站

mikel阅读(1108)

来源: REST Web Service开发实践–Jersey,Google Protocol Buffer, Tomcat结合样例 – – ITeye技术网站

一 综述:

当前REST化的WebService大行其道,Jersey是Sun推出的REST WEB Service参考实现,而Google Protocol Buffer由于其高效,短小,代码自动生成而成为分布式系统数据交互的优良选择, tomcat以市场占有率第一Servlet容器而知名,所以本文就讲叙怎么用Jersey, Google PB, Tomcat这三大法宝开发REST式的WEB service.

二 环境准备:

jersey版本:jersey-archive-1.2-SNAPSHOT
Google PB版本: protobuf-2.3.0
tomcat: tomcat6.0
Eclipse: 3.5.1 + tomcat plug in:http://www.eclipsetotale.com/tomcatPlugin.html

三 项目依赖的lib

sm-3.1.jar
jersey-server-1.2-SNAPSHOT.jar
jackson-core-asl-1.1.1.jar
jersey-spring-1.2-SNAPSHOT.jar
jersey-client-1.2-SNAPSHOT.jar
jettison-1.1.jar
jersey-core-1.2-SNAPSHOT.jar
jsr311-api-1.1.1.jar
jersey-json-1.2-SNAPSHOT.jar
protobuf-java-2.3.0.jar

请到jersey的包中和pb的包中找上述的lib

四 动手吧

4.1 在Eclipse新建一个tomcat工程, Eclipse的workspace为D:/workspace:
工程展开如图

4.2 图中元素说明:

addressbook.proto                            pb自带的电话本message定义文件

compile.bat                                       自己写的message编译脚本

protoc.exe                                         pb的编译命令行工具

ContactClient.java                             访问REST service的client端代码

CreatePerson.java                             自己写的生成Person PB结构,并存在D:盘根目录下的小工具类

AddressBookResource                        REST Service核心Resource类

HelloResource.java                             Hello world Resource类

AddressBookStore.java                      自己写的一个服务器段电话簿存储类,以D:/addressBooks.txt为存储文件

AddressBookProtos.java                    addressbook.proto 被PB命令行工具编译后生成的java类

ProtobufMessageBodyReader.java    服务器把Request输入流转化成PB结构的Provider类,用以把用户上穿的字节流转成PB结构

ProtobufMessageBodyWriter.java      服务器把PB机构转化成输出流的Provider类,用以把PB结构转成字节流输出给用户

4.3 代码:

addressbook.proto

package demo;

option java_package = “sample.pb”;
option java_outer_classname = “AddressBookProtos”;

message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}

repeated PhoneNumber phone = 4;
}

message AddressBook {
repeated Person person = 1;
}

———————————————————————-

compile.bat

protoc -I=D:/workspace/jerseydemo/src –java_out=D:/workspace/jerseydemo/src D:/workspace/jerseydemo/src/demo/pb/addressbook.proto

————————————————————————————–

ContactClient.java

package sample.hello.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

import sample.pb.AddressBookProtos.Person;
import sample.pb.AddressBookProtos.Person.PhoneNumber;

public class ContactClient {

public static void main(String[] args) throws IOException {
String url1 = “http://localhost:8080/jerseydemo/rest/addressbook/”;
putContacts(url1, “hery”);
String url2 = “http://localhost:8080/jerseydemo/rest/addressbook/hery”;
getContacts(url2);
}
public static void putContacts(String url, String name) throws IOException {
File file = new File(“D:/”+name+”.per”);
Person p = Person.parseFrom(new FileInputStream(file));
byte[] content = p.toByteArray();

URL target = new URL(url);
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod(“PUT”);
conn.setRequestProperty(“Content-Type”, “application/x-protobuf”);
conn.setRequestProperty(“Accept”, “application/x-protobuf;q=0.5”);

conn.setRequestProperty(“Content-Length”, Integer.toString(content.length));
// set stream mode to decrease memory usage
conn.setFixedLengthStreamingMode(content.length);
OutputStream out = conn.getOutputStream();
out.write(content);
out.flush();
out.close();
conn.connect();
// check response code
int code = conn.getResponseCode();
boolean success = (code >= 200) && (code < 300);
System.out.println(“put person:”+name+” “+(success?”successful!”:”failed!”));
}
public static void getContacts(String url) throws IOException {
URL target = new URL(url);
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod(“GET”);
conn.setRequestProperty(“Content-Type”, “application/x-protobuf”);
conn.setRequestProperty(“Accept”, “application/x-protobuf”);
conn.connect();
// check response code
int code = conn.getResponseCode();
boolean success = (code >= 200) && (code < 300);
InputStream in = success ? conn.getInputStream() : conn.getErrorStream();

int size = conn.getContentLength();

byte[] response = new byte[size];
int curr = 0, read = 0;

while (curr < size) {
read = in.read(response, curr, size – curr);
if (read <= 0) break;
curr += read;
}

Person p = Person.parseFrom(response);
System.out.println(“id:”+p.getId());
System.out.println(“name:”+p.getName());
System.out.println(“email:”+p.getEmail());
List<PhoneNumber> pl = p.getPhoneList();
for(PhoneNumber pn : pl) {
System.out.println(“number:”+pn.getNumber()+” type:”+pn.getType().name());
}
}
}
————————————————————————

CreatePerson.java

package sample.hello.client;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

import sample.pb.AddressBookProtos.Person;

public class CreatePerson {
// This function fills in a Person message based on user input.
static Person create(BufferedReader stdin,
PrintStream stdout) throws IOException {
Person.Builder person = Person.newBuilder();

stdout.print(“Enter person ID: “);
person.setId(Integer.valueOf(stdin.readLine()));

stdout.print(“Enter name: “);
person.setName(stdin.readLine());

stdout.print(“Enter email address (blank for none): “);
String email = stdin.readLine();
if (email.length() > 0) {
person.setEmail(email);
}

while (true) {
stdout.print(“Enter a phone number (or leave blank to finish): “);
String number = stdin.readLine();
if (number.length() == 0) {
break;
}

Person.PhoneNumber.Builder phoneNumber =
Person.PhoneNumber.newBuilder().setNumber(number);

stdout.print(“Is this a mobile, home, or work phone? “);
String type = stdin.readLine();
if (type.equals(“mobile”)) {
phoneNumber.setType(Person.PhoneType.MOBILE);
} else if (type.equals(“home”)) {
phoneNumber.setType(Person.PhoneType.HOME);
} else if (type.equals(“work”)) {
phoneNumber.setType(Person.PhoneType.WORK);
} else {
stdout.println(“Unknown phone type.  Using default.”);
}

person.addPhone(phoneNumber);
}

return person.build();
}

// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
public static void main(String[] args) throws Exception {

Person p = create(new BufferedReader(new InputStreamReader(System.in)),
System.out);

// Write the new address book back to disk.
FileOutputStream output = new FileOutputStream(“D:/”+p.getName()+”.per”);
p.writeTo(output);
output.close();
}
}
——————————————————————

AddressBookResource.java

package sample.hello.resources;

import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

import sample.hello.util.AddressBookStore;
import sample.pb.AddressBookProtos.Person;

@Path(“/addressbook”)
public class AddressBookResource {
@PUT
public Response putPerson(Person person) {
AddressBookStore.store(person);
return Response.ok().build();
}

@GET
@Path(“/{name}”)
public Response getPerson(@PathParam(“name”) String name) {
Person p = AddressBookStore.getPerson(name);
return Response.ok(p, “application/x-protobuf”).build();
}
}
——————————————————————————

AddressBookStore.java

package sample.hello.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import sample.pb.AddressBookProtos.AddressBook;
import sample.pb.AddressBookProtos.Person;

public class AddressBookStore {
static AddressBook addressBook = null;
static {
try {
addressBook =
AddressBook.parseFrom(new FileInputStream(“D:/addressBooks.txt”));
} catch (FileNotFoundException e) {

e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static Person getPerson(String name) {
for(Person person: addressBook.getPersonList()) {
if(person.getName().equals(name)) {
return person;
}
}
return null;
}

public static void store(Person p) {
AddressBook.Builder addressBookBuilder = AddressBook.newBuilder();
try {
String fileName = “D:/addressBooks.txt”;
File file = new File(fileName);
if(!file.exists()) {
file.createNewFile();
}
addressBookBuilder.mergeFrom(new FileInputStream(fileName));
addressBookBuilder.addPerson(p);
addressBookBuilder.build().writeTo(new FileOutputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}

————————————————————————–

AddressBookProtos.java

自动生成的,略

—————————————————————————–

ProtobufMessageBodyReader.java

package sample.pb;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;

import sample.pb.AddressBookProtos.Person;

@Provider
@Consumes(“application/x-protobuf”)
public class ProtobufMessageBodyReader implements MessageBodyReader<Object> {
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType) {
return mediaType.toString().equals(“application/x-protobuf”);
}

@Override
public Object readFrom(Class<Object> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream inputStream)
throws IOException, WebApplicationException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int read;
long total = 0L;
do {
read = inputStream.read(buffer, 0, buffer.length);
if (read > 0) {
baos.write(buffer, 0, read);
total += read;
}
} while (read > 0);

return Person.parseFrom(baos.toByteArray());
}
}
———————————————————————————————

ProtobufMessageBodyWriter.java

package sample.pb;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.WeakHashMap;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import sample.pb.AddressBookProtos.Person;

@Provider
@Produces(“application/x-protobuf”)
public class ProtobufMessageBodyWriter implements MessageBodyWriter<Object> {
/** a cache to save the cost of duplicated call(getSize, writeTo) to one object. */
private Map<Object, byte[]> buffer = new WeakHashMap<Object, byte[]>();

@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType) {
// it will handle all model classes
return mediaType.toString().equals(“application/x-protobuf”);
}

@Override
public long getSize(Object message, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
Person p = (Person)message;
byte[] bytes = p.toByteArray();
buffer.put(message, bytes);
return bytes.length;
}

@Override
public void writeTo(Object message, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
entityStream.write(buffer.remove(message));
}
}
————————————————————————————–

web.xml

<?xml version=”1.0″ encoding=”UTF-8″?>

<web-app xmlns=”http://java.sun.com/xml/ns/javaee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”
version=”2.5″>
<servlet>
<servlet-name>ServletAdaptor</servlet-name>
<servlet-class>com.sun.jersey.server.impl.container.servlet.ServletAdaptor</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>ServletAdaptor</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

</web-app>
——————————————————————————————

五 测试过程:

5.1 设定web application的context name为:

/jerseydemo

web application的root 为

/context

使用Eclipse的tomcat plugin的菜单

会在%TOMACT_HOME%\conf\Catalina\localhost下生成文件:

jerseydemo.xml

内容如下:

<Context path=”/jerseydemo” reloadable=”true” docBase=”D:\workspace\jerseydemo\context” workDir=”D:\workspace\jerseydemo\work” />

5.2 运行CreatePerson生成Person并写入磁盘:

在console进行如下输入:

查看D:盘根目录,发现新生成的  hery.per 文件.

5.3 启动tomcat(我就是点下Eclipse上的那只小猫)

5.4 运行ContactClient, 执行结果如下:

可见我们上传成功,并且能按名字查询.

D盘根目录下,同时生成了文件addressBooks.txt,这个为server段保留电话簿的地方.

六 完整的工程打包文件

使用hessian+protocol buffer+easyUI综合案例--登陆 - 推酷

mikel阅读(1127)

使用hessian+protocol buffer+easyUI综合案例–登陆

来源: 使用hessian+protocol buffer+easyUI综合案例–登陆 – 推酷

首先先简单介绍下hessian ,protocol buffer, easyUI框架

hessian:

Hessian是一个轻量级的remoting on http工具,采用的是Binary RPC协议,所以它很适合于发送二进制数据,同时又具有防火墙穿透能力。Hessian一般是通过Web应用来提供服务,因此非常类似于平时我们用的 WebService。只是它不使用SOAP协议,但相比webservice而言更简单、快捷。Hessian官 网:http://hessian.caucho.com/

Hessian 可通过Servlet提供远程服务,需要将匹配某个模式的请求映射到Hessian服务。也可Spring框架整合,通过它的 DispatcherServlet可以完成该功能,DispatcherServlet可将匹配模式的请求转发到Hessian服务。Hessian的 server端提供一个servlet基类, 用来处理发送的请求,而Hessian的这个远程过程调用,完全使用动态代理来实现的,,建议采用面向接口编程,Hessian服务通过接口暴露。

Hessian处理过程示意图:客户端——>序列化写到输出流——>远程方法(服务器端)——>序列化写到输出流 ——>客户端读取输入流——>输出结果

使用hessian所要下载的包: hessian-4.0.37.jar

protocol buffer:

protocol buffer (以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使 用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式, 可以用于诸如网络传输、配置文件、数据存储等诸多领域。

EasyUI:

JQuery EasyUI是一组基于JQuery的UI插件集合体,而jQuery EasyUI的目标就是帮助web开发者更轻松的打造出功能丰富并且美观的UI界面。开发者不需要编写复杂的JavaScript,也不需要对css样式 有深入的了解,开发者需要了解的只有一些简单的html标签。

案例如下:

需求介绍:

实现Server1上的用户账号可在其他应用(Server2)上登录的功能,达到一号多用和用户数据共享的目的。

主要功能点如下:

① 用户登录

② 显示用户信息

登录流程

①用户通过浏览器访问Server2的登陆界面。

②用户输入账号密码,点击登陆按钮。

③Server2收到用户的登录请求,调用Server1的账号验证接口。

④Server1验证Server2发送过来的账号信息(用户名、密码)后,返回验证结果。

⑤Server2收到并处理Server1返回的验证结果,再将相关信息返回给用户(提示登录失败或者显示用户信息)。

技术需求

①所有网页界面均采用easyui编写。

②服务器之间(Server1和Server2)的通信基于protobuf和 hessian(protobuf用于数据传输,hessian用于远程接口调用)。

③hessian远程接口方法的入参和返回值类型均为字节数组。

Server2调用Server1的接口时,先构造protobuf对象,将属性填充完毕后,将该对象序列化得到的字节数组填入接口方法传给 Server1;Server1收到该请求,将Server2传过来的字节数组反序列化成protobuf对象,获取其中的属性值(比如用户帐号、密 码),处理完成后,将处理结果填入protobuf对象,并返回该对象的序列化结果(字节数组)。

流程图:

具体实现:

先下载所必须的包 hessioan-4.0.37.jar,必须安装protocol buffer,下载easyUI包

首先先写服务端:

创建web项目hessianServer

目录如下:

在protocol安装目录下的examples下创建user.proto文件

package com.hessian.model;
option java_package="com.hessian.model";
option java_outer_classname="UserProtos";
message User{
  required string name=1;
  required string password=2;
  required string birth=3;
  optional string email = 4;
  optional int64 phone=5;
}

进入XXX\protobuf-2.4.1\examples目录,可以看到user.proto文件,执行命令 protoc –java_out=. user.proto 命令,如果生成com文件夹并在最终生成UserProtos类。

将UserProtos.java和XXX\protobuf-2.4.1\java\target目录下的protobuf-java-2.4.1.jar, hessioan-4.0.37.jar导入到web项目中

下面编写服务端代码

IService:

package com.hessian.service;

public interface IService {
    
    public Boolean login(byte[] user);
    
}

ServiceImpl

package com.hessian.service.impl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.hessian.model.UserProtos;
import com.hessian.model.UserProtos.User;
import com.hessian.service.IService;
public class ServiceImpl implements IService {
    public Boolean login(byte[] user) {
  UserProtos.User use=null;
  try {
       use=UserProtos.User.parseFrom(user); //将字节数组转化为对象
      System.out.println(use);
  } catch (InvalidProtocolBufferException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
  }
  //进行用户名,密码验证
  if(use.getName().equals("oumyye")&&use.getPassword().equals("oumyye")){
  return true;
  }
  else {
      return false;
  }
    }
}

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <!-- 配置 HessianServlet,Servlet的名字随便配置,例如这里配置成ServiceServlet-->
    <servlet-name>ServiceServlet</servlet-name>
    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
    <!-- 配置接口的具体实现类 -->
    <init-param>
      <param-name>service-class</param-name>
      <param-value>com.hessian.service.impl.ServiceImpl</param-value>
    </init-param>
  </servlet>
  <!-- 映射 HessianServlet的访问URL地址-->
  <servlet-mapping>
    <servlet-name>ServiceServlet</servlet-name>
    <url-pattern>/ServiceServlet</url-pattern>
  </servlet-mapping>
</web-app>

到此服务service1编写完成

进入http://localhost:8080/HessianServer/ServiceServlet出现

则编写成功,将src下的代码打包成hessian-common.jar文件。

下面进行service2客户端的编写loginClient

首先也要导入相关jar包,及目录如下:

编写前端代码:

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>ValidateBox - jQuery EasyUI Demo</title>
  <link rel="stylesheet" type="text/css" href="themes/default/easyui.css">
  <link rel="stylesheet" type="text/css" href="themes/icon.css">
  <link rel="stylesheet" type="text/css" href="demo.css">
  <style type="text/css">
    input,textarea{
      width:200px;
      border:1px solid #ccc;
      padding:2px;
    }
  </style>
  <script type="text/javascript" src="jquery/jquery-1.7.2.min.js"></script>
  <script type="text/javascript" src="jquery/jquery.easyui.min.js"></script>
</head>
<body>
  <h2>登陆</h2>
  ${info}
  <div>
  <form action="Login" method="post">
  <table>
    <tr>
      <td>用户名:</td>
      <td><input class="easyui-validatebox" data-options="required:true,validType:'length[1,3]'" name="name"></td>
    </tr>
    <tr>
      <td>密码:</td>
      <td><input class="easyui-validatebox" data-options="validType:'password'" name="password"></td>
    </tr>
    </table>
    <input type="submit"  value="登陆" style="width: 50px">
  </form>
    </div>
    </body>
    </html>

success.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>ValidateBox - jQuery EasyUI Demo</title>
  <link rel="stylesheet" type="text/css" href="themes/default/easyui.css">
  <link rel="stylesheet" type="text/css" href="themes/icon.css">
  <link rel="stylesheet" type="text/css" href="demo.css">
  <style type="text/css">
    input,textarea{
      width:200px;
      border:1px solid #ccc;
      padding:2px;
    }
  </style>
  <script type="text/javascript" src="jquery/jquery-1.7.2.min.js"></script>
  <script type="text/javascript" src="jquery/jquery.easyui.min.js"></script>
</head>
<body>
  <h2>登陆成功</h2>
  ${name}
    </body>
    </html>

然后编写servlet代码

package com.hessian.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.caucho.hessian.client.HessianProxyFactory;
import com.hessian.model.UserProtos.User.Builder;
import com.hessian.service.IService;
public class loginServlet extends HttpServlet {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
     public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
      doPost(request, response);
  }
  public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
      
      String url = "http://192.168.2.108:8080/HessianServer/ServiceServlet";
      HessianProxyFactory factory = new HessianProxyFactory();
      IService service = (IService) factory.create(IService.class, url);//创建IService接口的实例对象
      com.hessian.model.UserProtos.User.Builder ump=com.hessian.model.UserProtos.User.newBuilder();
      ump.setName(request.getParameter("name"));
      ump.setPassword(request.getParameter("password"));
      ump.setEmail("54654@qq.com");
      ump.setBirth("19931223");
      ump.setPhone(12313213);
      com.hessian.model.UserProtos.User info=ump.build();
      byte[] user=info.toByteArray();
      if(service.login(user)){
        RequestDispatcher dispatcher = request.getRequestDispatcher("success.jsp");
        dispatcher .forward(request, response);
    }else {
        request.setAttribute("info", "登陆失败!");
        RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp");
        dispatcher .forward(request, response);
    }
  }
}

编写web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
  <servlet-name>Login</servlet-name>
  <servlet-class>com.hessian.servlet.loginServlet</servlet-class>
  </servlet>
    <servlet-mapping>
    <servlet-name>Login</servlet-name>
    <url-pattern>/Login</url-pattern>
    </servlet-mapping>
</web-app>

这样整个代码就算编写完成了,下面进行测试:

http://localhost:8080/loginClient/ 可进行登陆,当用户名密码为oumyye时可登陆成功,跳转success.jsp页面

nginx buffered to a temporary 解决

mikel阅读(2582)

今天开启了nginx的error_log,发现了三个配置问题:

问题一:
2011/07/18 17:04:37 [warn] 2422#0: *171505004 an upstream response is buffered to a temporary file /opt/app/nginx/fastcgi_temp/9/80/0001539809 while reading upstream, client: 1.202.221.2, server: www.yongfu.com, request: “GET /recipe/all?cate=643&PHPSESSID=8c1135b85f1b5f90f55caab5ea7e1526 HTTP/1.0”, upstream: “fastcgi://127.0.0.1:9000”, host: “www.yongfu.com”
问题分析:nginx的fastcgi设置的fastcgi_buffers太小,导致将缓存写入磁盘。
问题解决:修改nginx的配置,将fastcgi_buffers调大,参考了该文:http://www.ruby-forum.com/topic/140625
fastcgi_buffers 32 8k;
ok,问题解决,但又出现新的问题:
问题二:
2011/07/18 17:10:21 [warn] 2898#0: *171505297 a client request body is buffered to a temporary file /opt/app/nginx/client_body_temp/0001539931, client: 114.237.110.152, server: www.yongfu.com, request: “POST /recipe/create/58884?do=uploadstep HTTP/1.1”, host: “www.yongfu.com”, referrer: “http://www.yongfu.com/recipe/create/58884”
问题分析:还是缓存写入磁盘的问题。
问题解决:修改nginx.conf的client_max_body_size & client_body_buffer_size选项。参考了:http://nginx.org/pipermail/nginx/2009-July/013634.html
client_max_body_size 500m;
client_body_buffer_size 1024k;
问题三:
2011/07/18 16:43:39 [warn] 18326#0: *28601 using uninitialized “rule_1” variable, client: 192.168.1.6, server: yongfu.com, request: “GET /user/notice.php?callback=jsonp1310978365248&_=1310978696380 HTTP/1.1”, host: “www.yongfu.com”, referrer: “http://www.yongfu.com/act/cooking?m=creview”
问题分析:这个是因为我的rewrite规则里的变量rule_1的问题。
if (!-f $document_root$uri){
set $rule_1 “1”;
}
if ($rule_1 = “1”){
rewrite ^/.*$ /rewrite.php last;
}
说是没有定义rule_1,但我在那里定义了呀,没明白。但分析这两个判断,可以使用以下代码代替:
问题解决:
if (!-f $document_root$uri){
rewrite ^/.*$ /rewrite.php last;
}

【重磅开源】Hawk-数据抓取工具:简明教程 - FerventDesert - 博客园

mikel阅读(1557)

来源: 【重磅开源】Hawk-数据抓取工具:简明教程 – FerventDesert – 博客园

Hawk: Advanced Crawler& ETL tool written in C#/WPF

1.软件介绍

HAWK是一种数据采集和清洗工具,依据GPL协议开源,能够灵活,有效地采集来自网页,数据库,文件, 并通过可视化地拖拽,
快速地进行生成,过滤,转换等操作。其功能最适合的领域,是爬虫和数据清洗。

Hawk的含义为“鹰”,能够高效,准确地捕杀猎物。

HAWK使用C# 编写,其前端界面使用WPF开发,支持插件扩展。通过图形化操作,能够快速建立解决方案。

GitHub地址:https://github.com/ferventdesert/Hawk

其Python等价的实现是etlpy:

http://www.cnblogs.com/buptzym/p/5320552.html

笔者专门为其开发的工程文件已公开在GitHub:

https://github.com/ferventdesert/Hawk-Projects

使用时,点击文件,加载工程即可加载。

不想编译的话,可执行文件在:

https://github.com/ferventdesert/Hawk/tree/master/Versions

编译路径在:
Hawk.Core\Hawk.Core.sln

2.gif-3330.9kB

以获取大众点评的所有北京美食为例,使用本软件可在10分钟内完成配置,在1小时之内自动并行抓取全部内容,并能监视子线程工作情况。而手工编写代码,即使是使用python,一个熟练的程序员也可能需要一天以上:

1.gif-1001.8kB

视频演示,复杂度由小到大:

链家二手房

微信公共平台

大众点评-北京美食

2.界面和组件介绍

2.1 界面介绍

软件采用类似Visual Studio和Eclipse的Dock风格,所有的组件都可以悬停和切换。包括以下核心组件:

QQ截图20160501105541.jpg-85.3kB

  • 左上角区域:主要工作区,可模块管理。
  • 下方: 输出调试信息,和任务管理,监控一项任务完成的百分比。
  • 右上方区域: 属性管理器,能对不同的模块设置属性。
  • 右下方区域: 显示当前已经加载的所有数据表和模块。

2.2 数据管理

能够添加来自不同数据源的连接器, 并对数据进行加载和管理:

QQ截图20160501105629.jpg-13.9kB

在空白处,点击右键,可增加新的连接器。在连接器的数据表上,双击可查看样例,点击右键,可以将数据加载到内存中。也可以选择加载虚拟数据集,此时系统会维护一个虚拟集合, 当上层请求分页数据时, 动态地访问数据库, 从而有效提升性能。

2.3 模块管理

目前系统仅仅提供了两个模块: 网页采集器和数据清洗ETL, 双击即可加载一个新的模块。

QQ截图20160501105646.jpg-6.8kB

之前配置好的模块,可以保存为任务, 双击可加载一个已有任务:

QQ截图20160501105700.jpg-10.5kB

2.4 系统状态管理

当加载了数据集或模块时,在系统状态管理中,就可对其查看和编辑:
点击右键,可以对数据集进行删除,修改名称等。也可以将数据集拖拽到下方的图标上,如拖到回收站,即可删除该模块。
双击数据集或模块,可查看模块的内容。 将数据集拖拽到数据清洗( 数据视图的下方第一个图标),可直接对本数据集做数据清洗。

QQ截图20160501105734.jpg-14.6kB

3.网页采集器

3.1 原理(建议阅读)

网页采集器的功能是获取网页中的数据(废话)。通常来说,目标可能是列表(如购物车列表),或是一个页面中的固定字段(如JD某商品的价格和介绍, 在页面中只有一个)。因此需要设置其读取模式。传统的采集器需要编写正则表达式,但方法过分复杂。如果认识到html是一棵树,只要找到了承载数据的节点 即可。XPath就是一种在树中描述路径的语法。指定XPath,就能搜索到树中的节点。

QQ截图20160501105743.jpg-20kB

手工编写XPath也很复杂,因此软件可以通过关键字,自动检索XPath,提供关键字,软件就会从树中递归搜索包含该数据的叶子节点。因此关键字最好是在页面中独一无二的。

如上图所示,只要提供“北京”和“42”这两个关键字,就能找到parent节点, 进而获取div[0]和div1这两个列表元素。通过div[0]和div1两个节点的比较,我们就能自动发现相同的子节点(name,mount)和不同的节点(北京:上海,37:42)。相同的节点会保存为属性名,不同的节点为属性值。但是,不能提供北京和37,此时,公共节点是div[0], 这不是列表。

软件在不提供关键字的情况下,也能通过html文档的特征,去计算最可能是列表父节点(如图中的parent)的节点,但当网页特别复杂时,猜测可能会出错,所以需要至少提供两个关键字( 属性)。

本算法原理是原创的,可查看源码或留言交流。

3.2 基本列表

我们以爬取链家二手房为例,介绍网页采集器的使用。首先双击图标,加载采集器:

QQ截图20160501121116.jpg-17.2kB

在最上方的地址栏中,输入要采集的目标网址,本次是http://bj.lianjia.com/ershoufang/。并点击刷新网页。此时,下方展示的是获取的html文本。原始网站页面如下:

由于软件不知道到底要获取哪些内容,因此需要手工给定几个关键字, 让Hawk搜索关键字, 并获取位置。

QQ截图20160501121150.jpg-88kB

以上述页面为例,通过检索820万和51789(单价,每次采集时都会有所不同),我们就能通过DOM树的路径,找出整个房源列表的根节点。

下面是实际步骤

QQ截图20160501121344.jpg-21.6kB

由于要抓取列表,所以读取模式选择List。 填入搜索字符700, 发现能够成功获取XPath, 编写属性为“总价” ,点击添加字段,即可添加一个属性。类似地,再填入30535,设置属性名称为“单价”,即可添加另外一个属性。

如果发现有错误,可点击编辑集合, 对属性进行删除,修改和排序。

你可以类似的将所有要抓取的特征字段添加进去,或是直接点击手气不错,系统会根据目前的属性,推测其他属性:

QQ截图20160501121405.jpg-138.5kB

属性的名称是自动推断的,如果不满意,可以修改列表第一列的属性名, 在对应的列中敲键盘回车提交修改。之后系统就会自动将这些属性添加到属性列表中。

工作过程中,可点击提取测试 ,随时查看采集器目前的能够抓取的数据内容。这样,一个链家二手房的网页采集器即可完成。可属性管理器的上方,可以修改采集器的模块名称,这样就方便数据清洗 模块调用该采集器。

4. 数据清洗

数据清洗模块,包括几十个子模块, 这些子模块包含四类:生成, 转换, 过滤和执行

QQ截图20160501121511.jpg-31.3kB

4.0 原理(可跳过)

4.0.1 C#版本的解释

数据清洗的本质是动态组装Linq,其数据链为IEnumerable<IFreeDocument>IFreeDocumentIDictionary<string, object>
接口的扩展。 Linq的Select函数能够对流进行变换,在本例中,就是对字典不同列的操作(增删改),不同的模块定义了一个完整的Linq
流:

result= source.Take(mount).where(d=>module0.func(d)).select(d=>Module1.func(d)).select(d=>Module2.func(d))….

借助于C#编译器的恩赐, Linq能很方便地支持流式数据,即使是巨型集合(上亿个元素),也能够有效地处理。

4.0.2 Python版本的解释

由于Python没有Linq, 因此组装的是生成器(generator), 对生成器进行操作,即可定义出类似Linq的完整链条:

for tool in tools:
    generator = transform(tool, generator)

详细源代码,可以参考Github上的开源项目https://github.com/ferventdesert/etlpy/

4.1 以链家为例的抓取

4.1.1构造url列表

在3.1节介绍了如何实现一个页面的采集,但如何采集所有二手房数据呢? 这涉及到翻页。

QQ截图20160501121520.jpg-3.1kB

还是以链家为例,翻页时,我们会看到页面是这样变换的:

http://bj.lianjia.com/ershoufang/pg2/
http://bj.lianjia.com/ershoufang/pg3/

因此,需要构造一串上面的url. 聪明的你肯定会想到, 应当先生成一组序列, 从1到100(假设我们只抓取前100页)。

  1. 双击数据清洗ETL左侧的搜索栏中搜索生成区间数, 将该模块拖到右侧上方的栏目中:

QQ截图20160501121554.jpg-29.8kB

  1. 在右侧栏目中双击生成区间数,可弹出设置窗口, 为该列起名字(id), 最大值填写为100,生成模式默认为Append:
    为什么只显示了前20个? 这是程序的虚拟化机制, 并没有加载全部的数据,可在ETL属性的调试栏目中,修改采样量(默认为20)。
  2. 将数字转换为url, 熟悉C#的读者,可以想到string.format, 或者python的%符号:搜索合并多列,并将其拖拽到刚才生成的id列, 编写format为下图的格式,即可将原先的数值列变换为一组url

QQ截图20160501121916.jpg-22.9kB

(如果需要多个列合并为一个列, 则在“其他项” 栏目中填写其他列的列名,用空格分割, 并在format中用{1},{2}..等表示)
(由于设计的问题,数据查看器的宽度不超过150像素,因此对长文本显示不全,可以在右侧属性对话框点击查看样例, 弹出的编辑器可支持拷贝数据和修改列宽。

4.1.2 使用配置好的网页采集器

生成这串URL之后,我们即可将刚才已经完成的网页采集器与这串url进行合并。

拖拽从爬虫转换到当前的url,双击该模块:将刚才的网页采集器的名称, 填入爬虫选择 栏目中。

之后,系统就会转换出爬取的前20条数据:

QQ截图20160501122007.jpg-127.3kB

可以看到, 数据中“属性3” 列包含html转义符, 拖拽html字符转义,到属性3列,即可自动转换所有转义符。

QQ截图20160501122026.jpg-81.4kB

如果要修改列名,在最上方的列名上直接修改, 点击回车即可修改名字。

where(面积)列中包含数字,想把数字提取出来,可以将提取数字模块拖拽到该列上,所有数字即可提取出来。

类似地,可以拖拽字符分割正则分割 到某一列,从而分割文本和替换文本。此处不再赘述。

有一些列为空,可以拖拽空对象过滤器 到该列,那么该列为空的话会自动过滤这一行数据。

4.1.4 保存和导出数据

需要保存数据时,可以选择写入文件,或者是临时存储(本软件的数据管理器),或是数据库。因此可以将“执行” 模块, 拖入清洗链的后端:
写入数据表到任意一列, 并填入新建表名(如链家二手房)

QQ截图20160501122057.jpg-32kB

下图是这次操作的所有子模块列表:

QQ截图20160501122110.jpg-14.1kB

之后,即可对整个过程进行操作:

选择串行模式并行模式, 并行模式使用线程池, 可设定最多同时执行的线程数(最好不要超过100)。推荐使用并行模式,

QQ截图20160501122136.jpg-12.5kB

点击执行按钮,即可在任务管理视图中采集数据。

QQ截图20160501122147.jpg-10.5kB

之后,在数据管理的数据表链家二手房上点击右键, 选择另存为, 导出到Excel,Json等,即可将原始数据导出到外部文件中。

QQ截图20160501122153.jpg-3.8kB

类似的, 你可以在清洗流程中拖入执行器,则保存的是中间过程,也可以在结尾拖入多个执行器,这样就能同时写入数据库或文件, 从而获得了极大的灵活性。

4.1.5 保存任务

在右下角的算法视图中的任意模块上点击右键,保存任务,即可在任务视图中保存新任务(任务名称与当前模块名字一致),下次可直接加载即可。如果存在同名任务, 则会对原有任务进行覆盖。
算法视图的空白处,点击保存所有模块,会批量保存所有的任务。

QQ截图20160501122208.jpg-12.1kB

你可以将一批任务,保存为一个工程文件(xml),并在之后将其加载和分发。

5.总结

上文以抓取房地产网站链家为例,介绍了软件的整体使用流程。当然,Hawk功能远远不限于这些。之后我们将通过一系列文章来介绍其使用方法。

值得提醒的是,由于软件功能在不断地升级,可能会出现视频和图片与软件中不一致的情况,因此所有的介绍和功能以软件的实际功能为准。

java处理日期时间 - ggjucheng - 博客园

mikel阅读(1450)

来源: java处理日期时间 – ggjucheng – 博客园

java.util.Calendar

Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。

 

简单示例

复制代码
// 通过格式化输出日期
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");

Calendar cal = Calendar.getInstance();// 取当前日期。
System.out.println("Today is:" + format.format(cal.getTime()));

cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -1);// 取当前日期的前一天.
System.out.println("yesterday is:" + format.format(cal.getTime()));

cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, +1);// 取当前日期的后一天.
System.out.println("nextday is:" + format.format(cal.getTime()));
复制代码

或者

java.util.Date today=new java.util.Date();   
java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd");
java.text.SimpleDateFormat dateTimeFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("Today is "+dateFormat.format(today));
System.out.println("Now is "+dateTimeFormat.format(today));

 

构造特定时间

java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");   

Calendar calendar = new GregorianCalendar(2007, 11, 25,0,0,0);   
Date date = calendar.getTime();   
System.out.println("2007 Christmas is:"+format.format(date));

GregorianCalendar构造方法参数依次为:年,月-1,日,时,分,秒.

或者

java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");   
java.util.Date date= format.parse("2007-12-25"); 
System.out.println("2007 Christmas is:"+format.format(date));

 

取日期的每部分

int year =calendar.get(Calendar.YEAR);   
int month=calendar.get(Calendar.MONTH)+1;   
int day =calendar.get(Calendar.DAY_OF_MONTH);   
int hour =calendar.get(Calendar.HOUR_OF_DAY);   
int minute =calendar.get(Calendar.MINUTE);   
int second =calendar.get(Calendar.SECOND);

取月份要加1

 

获取当前月份的最大天数

Calendar cal = Calendar.getInstance();   
int day=cal.getActualMaximum(Calendar.DAY_OF_MONTH);   
System.out.println(day);

取当月的最后一天

 

Calendar cal = Calendar.getInstance();   
int maxDay=cals.getActualMaximum(Calendar.DAY_OF_MONTH);   
java.text.Format formatter3=new java.text.SimpleDateFormat("yyyy-MM-"+maxDay);   
System.out.println(formatter3.format(cal.getTime()));

取当月的第一天

java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-01");   
java.util.Date firstDay=new java.util.Date();   
System.out.println("the month first day is "+formats.format(firstDay));

求两个日期之间相隔的天数

java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");   
java.util.Date beginDate= format.parse("2007-12-24");   
java.util.Date endDate= format.parse("2007-12-25");   
long day=(date.getTime()-mydate.getTime())/(24*60*60*1000);   
System.out.println("相隔的天数="+day);

一年前的日期

java.text.Format formatter=new java.text.SimpleDateFormat("yyyy-MM-dd");   
java.util.Date todayDate=new java.util.Date();   
long beforeTime=(todayDate.getTime()/1000)-60*60*24*365;   
todayDate.setTime(beforeTime*1000);   
String beforeDate=formatter.format(todayDate);   
System.out.println(beforeDate);

一年后的日期

java.text.Format formatter=new java.text.SimpleDateFormat("yyyy-MM-dd");   
java.util.Date todayDate=new java.util.Date();   
long afterTime=(todayDate.getTime()/1000)+60*60*24*365;   
todayDate.setTime(afterTime*1000);   
String afterDate=formatter.format(todayDate);   
System.out.println(afterDate);

10小时后的时间

java.util.Calendar Cal=java.util.Calendar.getInstance();   
Cal.setTime(dateOper);   
Cal.add(java.util.Calendar.HOUR_OF_DAY,10);   
System.out.println("date:"+forma.format(Cal.getTime()));

10小时前的时间

java.util.Calendar Cal=java.util.Calendar.getInstance();   
Cal.setTime(dateOper);   
Cal.add(java.util.Calendar.HOUR_OF_DAY,-10);   
System.out.println("date:"+forma.format(Cal.getTime()));

 

当前日期的星期一和星期天

复制代码
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
GregorianCalendar cal = new GregorianCalendar();


int dayInWeek = cal.get(Calendar.DAY_OF_WEEK);
int offset = 0;
if (dayInWeek == 1) {
    // 星期天
    offset = 6;
} else {
    // 星期一至星期六
    offset = dayInWeek - 2;
}
cal.add(GregorianCalendar.DAY_OF_MONTH, -offset);
String sday = dateFormat.format(cal.getTime());
cal.add(GregorianCalendar.DAY_OF_MONTH, 6);
String eday = dateFormat.format(cal.getTime());

System.out.println("这个星期的星期一:" + sday);
System.out.println("这个星期的星期天:" + eday);
复制代码

 

获取当前日期所在的星期属于今年的第几周

GregorianCalendar cal = new GregorianCalendar();
int weekOfYear = cal.get(Calendar.WEEK_OF_YEAR);
System.out.println("这个星期属于第几周:" + weekOfYear);

 

参考 http://alexfc.iteye.com/blog/363185

Java中的日期操作 - jediael_lu的专栏 - 博客频道 - CSDN.NET

mikel阅读(1247)

来源: Java中的日期操作 – jediael_lu的专栏 – 博客频道 – CSDN.NET

在日志中常用的记录当前时间及程序运行时长的方法:

  1. public void inject(Path urlDir) throws Exception {
  2.   SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
  3.   long start = System.currentTimeMillis();
  4.   LOG.info(“InjectorJob: starting at “ + sdf.format(start));
  5.   LOG.info(“InjectorJob: Injecting urlDir: “ + urlDir);
  6.   run(ToolUtil.toArgMap(Nutch.ARG_SEEDDIR, urlDir));
  7.   long end = System.currentTimeMillis();
  8.   LOG.info(“Injector: finished at “ + sdf.format(end) + “, elapsed: “
  9.       + TimingUtil.elapsedTime(start, end));
  10. }

关键点是:

1、定义日期显示格式

  1. SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);

2、获取当前时间

  1. long start = System.currentTimeMillis();

3、使用某种格式显示当前时间

  1. LOG.info(“InjectorJob: starting at “ + sdf.format(start));

 

以下内容转载自:http://lavasoft.blog.51cto.com/62575/52975/

Java日期时间使用总结
一、Java中的日期概述
日期在Java中是一块非常复杂的内容,对于一个日期在不同的语言国别环境中,日期的国际化,日期和时间之间的转换,日期的加减运算,日期的展示格式都是非常复杂的问题。
在Java中,操作日期主要涉及到一下几个类:
1、java.util.Date
        类 Date 表示特定的瞬间,精确到毫秒。从 JDK 1.1 开始,应该使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。Date 中的把日期解释为年、月、日、小时、分钟和秒值的方法已废弃。
2、java.text.DateFormat(抽象类)
        DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并分析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年,1 月 1 日 00:00:00 这一刻开始的毫秒数。
3、java.text.SimpleDateFormat(DateFormat的直接子类)
        SimpleDateFormat 是一个以与语言环境相关的方式来格式化和分析日期的具体类。它允许进行格式化(日期 -> 文本)、分析(文本 -> 日期)和规范化。
        SimpleDateFormat 使得可以选择任何用户定义的日期-时间格式的模式。但是,仍然建议通过 DateFormat 中的 getTimeInstance、getDateInstance 或 getDateTimeInstance 来新的创建日期-时间格式化程序。
4、java.util.Calendar(抽象类)
        Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
        与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化。
5、java.util.GregorianCalendar(Calendar的直接子类)
        GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家使用的标准日历系统。
        GregorianCalendar 是一种混合日历,在单一间断性的支持下同时支持儒略历和格里高利历系统,在默认情况下,它对应格里高利日历创立时的格里高利历日期(某些国家是在 1582 年 10 月 15 日创立,在其他国家要晚一些)。可由调用方通过调用 setGregorianChange() 来更改起始日期。
二、java.util.Date的使用
1、java.util.Date的API简介
类 java.util.Date 表示特定的瞬间,精确到毫秒。提供了很多的方法,但是很多已经过时,不推荐使用,下面仅仅列出没有过时的方法:
构造方法摘要
————-
Date()
          分配 Date 对象并用当前时间初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date)
          分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
方法摘要
————-
 boolean after(Date when)
          测试此日期是否在指定日期之后。
 boolean before(Date when)
          测试此日期是否在指定日期之前。
 Object clone()
          返回此对象的副本。
 int compareTo(Date anotherDate)
          比较两个日期的顺序。
 boolean equals(Object obj)
          比较两个日期的相等性。
 long getTime()
          返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
 int hashCode()
          返回此对象的哈希码值。
 void setTime(long time)
          设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点。
 String toString()
          把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中:
          dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
          mon 是月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
          dd 是一月中的某一天(01 至 31),显示为两位十进制数。
          hh 是一天中的小时(00 至 23),显示为两位十进制数。
          mm 是小时中的分钟(00 至 59),显示为两位十进制数。
          ss 是分钟中的秒数(00 至 61),显示为两位十进制数。
          zzz 是时区(并可以反映夏令时)。标准时区缩写包括方法 parse 识别的时区缩写。如果不提供时区信息,则 zzz 为空,即根本不包括任何字符。
          yyyy 是年份,显示为 4 位十进制数。
下面是一个Date类的综合实例:
import java.util.Date;

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-30
* Time: 8:45:44
* 日期测试
*/
public class TestDate {
public static void main(String args[]) {
TestDate nowDate = new TestDate();
nowDate.getSystemCurrentTime();
nowDate.getCurrentDate();
}

/**
* 获取系统当前时间
* System.currentTimeMillis()返回系统当前时间,结果为1970年1月1日0时0分0秒开始,到程序执行取得系统时间为止所经过的毫秒数
* 1秒=1000毫秒
*/
public void getSystemCurrentTime() {
System.out.println(“—-获取系统当前时间—-“);
System.out.println(“系统当前时间 = ” + System.currentTimeMillis());
}

/**
* 通过Date类获取当前日期和当前时间
* date.toString()把日期转换为dow mon dd hh:mm:ss zzz yyyy
*/
public void getCurrentDate() {
System.out.println(“—-获取系统当前日期—-“);
//创建并初始化一个日期(初始值为当前日期)
Date date = new Date();
System.out.println(“现在的日期是 = ” + date.toString());
System.out.println(“自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = ” + date.getTime());
}
}

运行结果:
—-获取系统当前时间—-
系统当前时间 = 1196413077278
—-获取系统当前日期—-
现在的日期是 = Fri Nov 30 16:57:57 CST 2007
自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = 1196413077278

Process finished with exit code 0

2、java.text.DateFormat抽象类的使用
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并分析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年,1 月 1 日 00:00:00 这一刻开始的毫秒数。
DateFormat 提供了很多类方法,以获得基于默认或给定语言环境和多种格式化风格的默认日期/时间 Formatter。格式化风格包括 FULL、LONG、MEDIUM 和 SHORT。方法描述中提供了使用这些风格的更多细节和示例。
DateFormat 可帮助进行格式化并分析任何语言环境的日期。对于月、星期,甚至日历格式(阴历和阳历),其代码可完全与语言环境的约定无关。
要格式化一个当前语言环境下的日期,可使用某个静态工厂方法:
      myString = DateFormat.getDateInstance().format(myDate);
如果格式化多个日期,那么获得该格式并多次使用它是更为高效的做法,这样系统就不必多次获取有关环境语言和国家约定的信息了。
 DateFormat df = DateFormat.getDateInstance();
 for (int i = 0; i < myDate.length; ++i) {
  output.println(df.format(myDate[i]) + “; “);
 }
要格式化不同语言环境的日期,可在 getDateInstance() 的调用中指定它。
 DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
还可使用 DateFormat 进行分析。
 myDate = df.parse(myString);
使用 getDateInstance 来获得该国家的标准日期格式。另外还提供了一些其他静态工厂方法。使用 getTimeInstance 可获得该国家的时间格式。使用 getDateTimeInstance 可获得日期和时间格式。可以将不同选项传入这些工厂方法,以控制结果的长度(从 SHORT 到 MEDIUM 到 LONG 再到 FULL)。确切的结果取决于语言环境,但是通常:
 SHORT 完全为数字,如 12.13.52 或 3:30pm
 MEDIUM 较长,如 Jan 12, 1952
 LONG 更长,如 January 12, 1952 或 3:30:32pm
 FULL 是完全指定,如 Tuesday, April 12, 1952 AD 或 3:30:42pm PST。
 如果愿意,还可以在格式上设置时区。如果想对格式化或分析施加更多的控制(或者给予用户更多的控制),可以尝试将从工厂方法所获得的 DateFormat 强制转换为 SimpleDateFormat。这适用于大多数国家;只是要记住将其放入一个 try 代码块中,以防遇到特殊的格式。
还可以使用借助 ParsePosition 和 FieldPosition 的分析和格式化方法形式来:逐步地分析字符串的各部分。 对齐任意特定的字段,或者找出字符串在屏幕上的选择位置。
DateFormat 不是同步的。建议为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须保持外部同步。
3、java.text.SimpleDateFormat(DateFormat的直接子类)的使用
SimpleDateFormat 是一个以与语言环境相关的方式来格式化和分析日期的具体类。它允许进行格式化(日期 -> 文本)、分析(文本 -> 日期)和规范化。
SimpleDateFormat 使得可以选择任何用户定义的日期-时间格式的模式。但是,仍然建议通过 DateFormat 中的 getTimeInstance、getDateInstance 或 getDateTimeInstance 来新的创建日期-时间格式化程序。每一个这样的类方法都能够返回一个以默认格式模式初始化的日期/时间格式化程序。可以根据需要使用 applyPattern 方法来修改格式模式。有关使用这些方法的更多信息,请参阅 DateFormat。
日期和时间模式
日期和时间格式由日期和时间模式 字符串指定。在日期和时间模式字符串中,未加引号的字母 ‘A’ 到 ‘Z’ 和 ‘a’ 到 ‘z’ 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (‘) 引起来,以免进行解释。””” 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在分析时与输入字符串进行匹配。
定义了以下模式字母(所有其他字符 ‘A’ 到 ‘Z’ 和 ‘a’ 到 ‘z’ 都被保留):
字母
日期或时间元素
表示
示例
G
Era 标志符
AD
y
1996; 96
M
年中的月份
July; Jul; 07
w
年中的周数
27
W
月份中的周数
2
D
年中的天数
189
d
月份中的天数
10
F
月份中的星期
2
E
星期中的天数
Tuesday; Tue
a
Am/pm 标记
PM
H
一天中的小时数(0-23)
0
k
一天中的小时数(1-24)
24
K
am/pm 中的小时数(0-11)
0
h
am/pm 中的小时数(1-12)
12
m
小时中的分钟数
30
s
分钟中的秒数
55
S
毫秒数
978
z
时区
Pacific Standard Time; PST; GMT-08:00
Z
时区
-0800
更多的参考信息可以查看JDK API文档,下面给出一个综合实例:
import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-30
* Time: 11:20:58
* To change this template use File | Settings | File Templates.
*/
public class TestSimpleDateFormat {
public static void main(String args[]) throws ParseException {
TestSimpleDateFormat test = new TestSimpleDateFormat();
test.testDateFormat();

}

public void testDateFormat() throws ParseException {
//创建日期
Date date = new Date();

//创建不同的日期格式
DateFormat df1 = DateFormat.getInstance();
DateFormat df2 = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss EE”);
DateFormat df3 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA);     //产生一个指定国家指定长度的日期格式,长度不同,显示的日期完整性也不同
DateFormat df4 = new SimpleDateFormat(“yyyy年MM月dd日 hh时mm分ss秒 EE”, Locale.CHINA);
DateFormat df5 = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss EEEEEE”, Locale.US);
DateFormat df6 = new SimpleDateFormat(“yyyy-MM-dd”);
DateFormat df7 = new SimpleDateFormat(“yyyy年MM月dd日”);

//将日期按照不同格式进行输出
System.out.println(“——-将日期按照不同格式进行输出——“);
System.out.println(“按照Java默认的日期格式,默认的区域                      : ” + df1.format(date));
System.out.println(“按照指定格式 yyyy-MM-dd hh:mm:ss EE ,系统默认区域      :” + df2.format(date));
System.out.println(“按照日期的FULL模式,区域设置为中文                      : ” + df3.format(date));
System.out.println(“按照指定格式 yyyy年MM月dd日 hh时mm分ss秒 EE ,区域为中文 : ” + df4.format(date));
System.out.println(“按照指定格式 yyyy-MM-dd hh:mm:ss EE ,区域为美国        : ” + df5.format(date));
System.out.println(“按照指定格式 yyyy-MM-dd ,系统默认区域                  : ” + df6.format(date));

//将符合该格式的字符串转换为日期,若格式不相配,则会出错
Date date1 = df1.parse(“07-11-30 下午2:32”);
Date date2 = df2.parse(“2007-11-30 02:51:07 星期五”);
Date date3 = df3.parse(“2007年11月30日 星期五”);
Date date4 = df4.parse(“2007年11月30日 02时51分18秒 星期五”);
Date date5 = df5.parse(“2007-11-30 02:51:18 Friday”);
Date date6 = df6.parse(“2007-11-30”);

System.out.println(“——-输出将字符串转换为日期的结果——“);
System.out.println(date1);
System.out.println(date2);
System.out.println(date3);
System.out.println(date4);
System.out.println(date5);
System.out.println(date6);
}
}

运行结果:
——-将日期按照不同格式进行输出——
按照Java默认的日期格式,默认的区域                      : 07-11-30 下午5:04
按照指定格式 yyyy-MM-dd hh:mm:ss EE ,系统默认区域      :2007-11-30 05:04:10 星期五
按照日期的FULL模式,区域设置为中文                      : 2007年11月30日 星期五
按照指定格式 yyyy年MM月dd日 hh时mm分ss秒 EE ,区域为中文 : 2007年11月30日 05时04分10秒 星期五
按照指定格式 yyyy-MM-dd hh:mm:ss EE ,区域为美国        : 2007-11-30 05:04:10 Friday
按照指定格式 yyyy-MM-dd ,系统默认区域                  : 2007-11-30
——-输出将字符串转换为日期的结果——
Fri Nov 30 14:32:00 CST 2007
Fri Nov 30 02:51:07 CST 2007
Fri Nov 30 00:00:00 CST 2007
Fri Nov 30 02:51:18 CST 2007
Fri Nov 30 02:51:18 CST 2007
Fri Nov 30 00:00:00 CST 2007

Process finished with exit code 0

4、java.util.Calendar(抽象类)
java.util.Calendar是个抽象类,是系统时间的抽象表示,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化。
一个Calendar的实例是系统时间的抽象表示,从Calendar的实例可以知道年月日星期月份时区等信息。Calendar类中有一个静 态方法get(int x),通过这个方法可以获取到相关实例的一些值(年月日星期月份等)信息。参数x是一个产量值,在Calendar中有定义。
Calendar中些陷阱,很容易掉下去:
1、Calendar的星期是从周日开始的,常量值为0。
2、Calendar的月份是从一月开始的,常量值为0。
3、Calendar的每个月的第一天值为1。
5、java.util.GregorianCalendar(Calendar的直接子类)
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家使用的标准日历系统。结合Calendar抽象类使用。
下面给出一个综合实例看看Calendar类的用法:
import java.util.*;
import java.text.SimpleDateFormat;

/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-30
* Time: 15:06:57
* Calendar的使用测试
*/
public class TestCalendar {
public static void main(String args[]) {
TestCalendar testCalendar = new TestCalendar();
testCalendar.testCalendar();

}

public void testCalendar() {
//创建Calendar的方式
Calendar now1 = Calendar.getInstance();
Calendar now2 = new GregorianCalendar();
Calendar now3 = new GregorianCalendar(2007, 10, 30);
Calendar now4 = new GregorianCalendar(2007, 10, 30, 15, 55);      //陷阱:Calendar的月份是0~11
Calendar now5 = new GregorianCalendar(2007, 10, 30, 15, 55, 44);
Calendar now6 = new GregorianCalendar(Locale.US);
Calendar now7 = new GregorianCalendar(TimeZone.getTimeZone(“GMT-8:00”));

//通过日期和毫秒数设置Calendar
now2.setTime(new Date());
System.out.println(now2);

now2.setTimeInMillis(new Date().getTime());
System.out.println(now2);

//定义日期的中文输出格式,并输出日期
SimpleDateFormat df = new SimpleDateFormat(“yyyy年MM月dd日 hh时mm分ss秒 E”, Locale.CHINA);
System.out.println(“获取日期中文格式化化输出:” + df.format(now5.getTime()));
System.out.println();

System.out.println(“——–通过Calendar获取日期中年月日等相关信息——–“);
System.out.println(“获取年:” + now5.get(Calendar.YEAR));
System.out.println(“获取月(月份是从0开始的):” + now5.get(Calendar.MONTH));
System.out.println(“获取日:” + now5.get(Calendar.DAY_OF_MONTH));
System.out.println(“获取时:” + now5.get(Calendar.HOUR));
System.out.println(“获取分:” + now5.get(Calendar.MINUTE));
System.out.println(“获取秒:” + now5.get(Calendar.SECOND));
System.out.println(“获取上午、下午:” + now5.get(Calendar.AM_PM));
System.out.println(“获取星期数值(星期是从周日开始的):” + now5.get(Calendar.DAY_OF_WEEK));
System.out.println();

System.out.println(“———通用星期中文化转换———“);
String dayOfWeek[] = {“”, “日”, “一”, “二”, “三”, “四”, “五”, “六”};
System.out.println(“now5对象的星期是:” + dayOfWeek[now5.get(Calendar.DAY_OF_WEEK)]);
System.out.println();

System.out.println(“———通用月份中文化转换———“);
String months[] = {“一月”, “二月”, “三月”, “四月”, “五月”, “六月”, “七月”, “八月”, “九月”, “十月”, “十一月”, “十二月”};
System.out.println(“now5对象的月份是: ” + months[now5.get(Calendar.MONTH)]);
}
}

运行结果:
java.util.GregorianCalendar[time=1196414388324,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id=”Asia/Shanghai”,offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2007,MONTH=10,WEEK_OF_YEAR=48,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=19,SECOND=48,MILLISECOND=324,ZONE_OFFSET=28800000,DST_OFFSET=0]
java.util.GregorianCalendar[time=1196414388324,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id=”Asia/Shanghai”,offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2007,MONTH=10,WEEK_OF_YEAR=48,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=334,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=19,SECOND=48,MILLISECOND=324,ZONE_OFFSET=28800000,DST_OFFSET=0]
获取日期中文格式化化输出:2007年11月30日 03时55分44秒 星期五

——–通过Calendar获取日期中年月日等相关信息——–
获取年:2007
获取月(月份是从0开始的):10
获取日:30
获取时:3
获取分:55
获取秒:44
获取上午、下午:1
获取星期数值(星期是从周日开始的):6

———通用星期中文化转换———
now5对象的星期是:五

———通用月份中文化转换———
now5对象的月份是: 十一月

Process finished with exit code 0

三、总结
Java中日期的经常有一下五个方面:
1、创建日期
2、日期格式化显示
3、日期的转换(主要是和字符串之间的相互转换)
4、日期中年、月、日、时、分、秒、星期、月份等获取。
5、日期的大小比较、日期的加减。
这也是学习java日期操作的难点和关键,掌握了这些,日期问题一般难不倒你。
关于日期的大小比较和加减在API文档中都有详尽的描述,鉴于时间关系,在此不做介绍。祝各位博友周末愉快

认识HTML5的WebSocket - 立伟 - 博客园

mikel阅读(1337)

来源: 认识HTML5的WebSocket – 立伟 – 博客园

在HTML5规范中,我最喜欢的Web技术就是正迅速变得流行的WebSocket API。WebSocket提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术。这个新的API提供了一个方法,从客户端使用简单的语法 有效地推动消息到服务器。让我们看一看HTML5的WebSocket API:它可用于客户端、服务器端。而且有一个优秀的第三方API,名为Socket.IO。

一、什么是WebSocket API?

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。 WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为 Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

Ajax技术很聪明的一点是没有设计要使用的方式。WebSocket为指定目标创建,用于双向推送消息。

二、WebSocket API的用法

只专注于客户端的API,因为每个服务器端语言有自己的API。下面的代码片段是打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。

// 创建一个Socket实例
var socket = new WebSocket(‘ws://localhost:8080’);

// 打开Socket
socket.onopen = function(event) {

// 发送一个初始化消息
socket.send(‘I am the client and I\’m listening!’);

// 监听消息
socket.onmessage = function(event) {
console.log(‘Client received a message’,event);
};

// 监听Socket的关闭
socket.onclose = function(event) {
console.log(‘Client notified socket has closed’,event);
};

// 关闭Socket….
//socket.close()
};

让我们来看看上面的初始化片段。参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。

onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。

WebSocket的语法非常简单,使用WebSockets是难以置信的容易……除非客户端不支持WebSocket。IE浏览器目前不支持WebSocket通信。如果你的客户端不支持WebSocket通信,下面有几个后备方案供你使用:

Flash技术 —— Flash可以提供一个简单的替换。 使用Flash最明显的缺点是并非所有客户端都安装了Flash,而且某些客户端,如iPhone/iPad,不支持Flash。

AJAX Long-Polling技术 —— 用AJAX的long-polling来模拟WebSocket在业界已经有一段时间了。它是一个可行的技术,但它不能优化发送的信息。也就是说,它是一个解决方案,但不是最佳的技术方案。

由于目前的IE等浏览器不支持WebSocket,要提供WebSocket的事件处理、返回传输、在服务器端使用一个统一的API,那么该怎么办呢?幸运的是,Guillermo Rauch创建了一个Socket.IO技术。

三、带Socket.IO的WebSocket

Socket.IO 是Guillermo Rauch创建的WebSocket API,Guillermo Rauch是LearnBoost公司的首席技术官以及LearnBoost实验室的首席科学家。Socket.IO使用检测功能来判断是否建立 WebSocket连接,或者是AJAX long-polling连接,或Flash等。可快速创建实时的应用程序。Socket.IO还提供了一个NodeJS API,它看起来非常像客户端API。
建立客户端Socket.IO

Socket.IO可以从GitHub下载,可以把socket.io.js文件包含到页面中:

<script src=”http://cdn.socket.io/stable/socket.io.js”></script>
[/code

此时,Socket.IO在此页面上是有效的,是时候创建Socket了:

// 创建Socket.IO实例,建立连接
var socket= new io.Socket(‘localhost’,{
port: 8080
});
socket.connect();

// 添加一个连接监听器
socket.on(‘connect’,function() {
console.log(‘Client has connected to the server!’);
});

// 添加一个连接监听器
socket.on(‘message’,function(data) {
console.log(‘Received a message from the server!’,data);
});

// 添加一个关闭连接的监听器
socket.on(‘disconnect’,function() {
console.log(‘The client has disconnected!’);
});

// 通过Socket发送一条消息到服务器
function sendMessageToServer(message) {
socket.send(message);
}

Socket.IO简化了WebSocket API,统一了返回运输的API。传输包括:
WebSocket
Flash Socket
AJAX long-polling
AJAX multipart streaming
IFrame
JSONP polling

你还可以设置任意的Socket.IO构造器的第二个选项,选项包括:

port – 待连接的端口
transports – 一个数组,包含不同的传输类型
transportOptions – 传输的参数使用的对象,带附加属性

Socket.IO还提供了由本地WebSocket API提供的普通连接、断开连接、消息事件。Socket还提供了封装每个事件类型的方法。

四、NodeJS和Socket.IO联合开发

Socket.IO提供的服务器端解决方案,允许统一的客户端和服务器端的API。使用Node,你可以创建一个典型的HTTP服务器,然后把服务器的实例传递到Socket.IO。从这里,你创建连接、断开连接、建立消息监听器,跟在客户端一样。

一个简单的服务器端脚本看起来如下:

// 需要HTTP 模块来启动服务器和Socket.IO
var http= require(‘http’), io= require(‘socket.io’);

// 在8080端口启动服务器
var server= http.createServer(function(req, res){
// 发送HTML的headers和message
res.writeHead(200,{ ‘Content-Type’: ‘text/html’ });
res.end(‘<h1>Hello Socket Lover!</h1>’);
});
server.listen(8080);

// 创建一个Socket.IO实例,把它传递给服务器
var socket= io.listen(server);

// 添加一个连接监听器
socket.on(‘connection’, function(client){

// 成功!现在开始监听接收到的消息
client.on(‘message’,function(event){
console.log(‘Received message from client!’,event);
});
client.on(‘disconnect’,function(){
clearInterval(interval);
console.log(‘Server has disconnected’);
});
});

你可以运行服务器部分,假定已安装了NodeJS,从命令行执行:

node socket-server.js

现在客户端和服务器都能来回推送消息了!在NodeJS脚本内,可以使用简单的JavaScript创建一个定期消息发送器:

// 创建一个定期(每5秒)发送消息到客户端的发送器
var interval= setInterval(function() {
client.send(‘This is a message from the server! ‘ + new Date().getTime());
},5000);

服务器端将会每5秒推送消息到客户端!

五、dojox.Socket和Socket.IO

Persevere的创建者Kris Zyp创建了dojox.Socket。dojox.Socket以Dojo库一致的方式封装了WebSocket API,用于在客户端不支持WebSocket时,使用long-polling替代。

下面是怎样在客户端使用dojox.Socket和在服务器端使用Socket.IO的例子:

var args, ws= typeof WebSocket!= ‘undefined’;
var socket= dojox.socket(args= {
url: ws? ‘/socket.io/websocket’ : ‘/socket.io/xhr-polling’,
headers:{
‘Content-Type’:’application/x-www-urlencoded’
},
transport: function(args, message){
args.content = message; // use URL-encoding to send the message instead of a raw body
dojo.xhrPost(args);
};
});
var sessionId;
socket.on(‘message’, function(){
if (!sessionId){
sessionId= message;
args.url += ‘/’ + sessionId;
}else if(message.substr(0, 3) == ‘~h~’){
// a heartbeat
}
});

dojox.socket.Reconnect还创建了在套接字失去连接时自动重连。期待包含dojox.Socket的Dojo 1.6版本早日发布。

六、实际应用和WebSocket资源

有很多WebSocke的实际应用。WebSocket对于大多数客户机-服务器的异步通信是理想的,在浏览器内聊天是最突出的应用。WebSocket由于其高效率,被大多数公司所使用。

WebSocket资源
Socket.IO站点:http://socket.io/
WebSocket的Wikipedia:http://en.wikipedia.org/wiki/WebSockets
WebSockets.org站点:http://www.websockets.org/
Dojo WebSocket站点:http://www.sitepen.com/blog/2010/10/31/dojo-websocket/

闲话iOS的MVC设计模式 - Ken_Wu - 博客园

mikel阅读(1590)

来源: 闲话iOS的MVC设计模式 – Ken_Wu – 博客园

模式是经验知识的复制应用。MVC设计模式在不同的开发平台有不同阐述和应用。目前在网路上可以搜索出java版本、c++版本、c#版本的,也有ios版本的。我这里也发布这篇关于MVC设计模式的文章,用我的缘走你的路。

写在前面的话

若然不用设计模式,难道就不能开发设计程序了吗?不然。那么设计模式给我们带来什么呢?如果你不学习别人总结出来的设计模式,就能轻松、快捷、真正 地解决问题,而且还乐意再来一次,我相信你不需要别人的设计模式了。如果🈶某些问题,让你很挠头,让你不敢再回首,不妨借助别人总结出来的设计模式,来 提高一下你的解决方案。

在设计作品中,如果为了使用设计模式而应用设计模式,我认为有点炫技的嫌疑了,大可不必。在iOS APP开发过程中,一般都会涉及到数据处理和UI呈现。如果计算数据的同时还要处理UI的话,会有很多弊端:1. 容易分散精力。2. 不利于复用。3. 重绘画面的时候容易导致重复计算。为了解决这些弊端,我们需要解耦,MVC设计模式就是对症的药。

iOS的MVC设计模式

什么是MVC(Model - View - Controller)设计模式?先看下面的图。(很抱歉,第一次提交后该图没有显示出来。更正之。)

这首先是个分类方面的认识。如,一个程序页面当中有什么东西呢?容易看出来的有数据内容,比如某些文字描述,某些图片内容,还有数据相关的呈现方 式,如文字的颜色,背景颜色,图片的大小,位置等。还有别的吗?恐怕一个静态的页面很难勾起这个答案:规则、逻辑、道。这些就是隐藏在程序页面背后的、数 据与呈现方式之间的程序逻辑、决策。

程序页面包含:

1. 数据

2. 呈现方式

3. 数据与呈现方式之间的关系。

举个例子,我有四张牌要放到桌面上。要怎样放?呈扇形排布还是呈一行排布,或者成一列排布?四张牌就是数据;扇形排布就是呈现方式;桌面就是我们的程序页面。后面这个“怎样”,就是程序页面背后隐藏的决策逻辑了。

我们可以把这三者叫三个不同的职责。这三个职责可以应用在不同的思维层面上,比如系统架构上,软件架构设计上,程序设计上。只要我们从这三个角度来 考虑,就是应用了MVC设计模式了。当然在程序代码层面,iOS的库中在类的命名和职责上都做的很清楚了,如 UITableViewController,UITableView,UITableViewCell,NSArray,NSNumber等。我们也可 以在设计自己类时,起个好名字,每个类专注且仅专注于其中一个职责。

MVC设计模式就是这样思维模式下的一个产物。包含以下内容:

1. Model <=> 数据

2. View  <=> 呈现方式

3. 控制器(Controller)<=> 数据与呈现方式之间的关系

4. 以上三者的交互关系。什么关系?

1. Model 不与 View直接通话,如图中Model和View之间的两黄实线。

2. Model 与 Controller 通话,如图中Model和Controller中两灰色虚实线、当中的绿色箭头、Model上橙色的KVO。

3. View与 controller 通话,如图中Controller和View之间两灰色虚实线、当中的Outlet绿色箭头、黄色的delegate和data source。

4. View上action 与 controller 上的target。

5. Model上KVO发送端与controller上的黄色接受端。

具体步骤:

第一轮:

设计Model时,先只关注数据所具有的属性。

设计View时,先只关注任意一个view的呈现方式,不要考虑view中呈现的数据,需要时从controller中可以获取就行了。

设计Controller时,要关注怎么从Model中获取数据,要关注怎么通知 view,以呈现新的数据。与View的通信可以通过用protocol定义消息,view通过delegate来告诉Controller其事件处理过 程中的细节,View通过Data Source来询问需要呈现的数据。

第二轮:

设计Model时,可以考虑数据来源的多种方式,数据的变化如何通知到 Controller,等等。咱主动一点,不能让Controller拉一下,才动一下。这时,可以加入KVO设计模式或者NSNotification 了。这两者都可以达到这样的效果:一旦数据(Model)变动,监听者(这里设为Controller)就收到消息,从而处理该消息。

设计View时,可以考虑如何主动将用户对数据的操作通知到Controller。如增加手势处理等。

设计Controller时,可以考虑是否忽略数据的变化对View的影响。若然要考虑变化,则其频度、缓存等策略又该怎样呢?

直到第N轮达到该设计可以发布的标准。

例子:我们可以用上面放牌的例子。通过仿照UITableViewController,把每张牌设计类似TableViewCell的类,整个桌 面设计成一个类似TableView的类,另外看不到逻辑设计成类似TableViewController的类,每张牌的内容设计成某个继承于 NSObject的类,包含有名字、图片等属性。有了这个框架,可以向桌面放任何数目的牌,任何不同的牌,可以按照任一不同的方式排列这些牌,等等。可复 用性、可扩展性、低耦合等质量属性就具备了。如要展示一下的页面:

闲话完毕。感谢各位读者,希望能给你带来灵感。

Google Protocol Buffer搭建及示例 - 推酷

mikel阅读(1255)

 

来源: Protocol Buffer搭建及示例 – 推酷

Protocol Buffer(简称Protobuf或PB)是由Google推出的一种数据交换格式,与传统的XML和JSON不同的是,它是一种二进制格式,免去了文 本格式转换的各种困扰,并且转换效率也是非常快,由于它的跨平台、跨编程语言的特点,让它越来越普及,尤其是网络数据交换方面日趋成为一种主流.

PB目前托管在GitHub,链接地址: https://github.com/google/protobuf ,源码的主要功能可以分为两部分:

  • PB基础库 :完成对象->数据的序列化、数据->对象的反序列化这两个转换过程的支持;
  • PB编译器 :源码生成器,将PB格式定义文件.proto(PB数据格式的一种定义文件)转换为对象源码(支持C++,JAVA,Python等格式).

截止目前PB的最新版本为3.0.0-beta-1(alpha-4),已经加入了对Objective-C的支持(其它之前低版本中也已经有OC扩展支持),以下的示例便以该版本为例.

1.编译源码,生成PB编译器

编译源码主要的目的就是在本地生成PB的编译器,下载好 protobuf-objectivec-3.0.0-alpha-4.tar.gz 文件,执行以下的命令进行编译:

tar -xzvf protobuf-objectivec-3.0.0-alpha-4.tar.gz
cd protobuf-3.0.0-alpha-4
./configure
make
make check
sudo make install

如果编译顺利的话,便可以使用 protoc 命令了,以后便可以用这个命令将.proto文件转换为不同语言的源代码文件.

2.为工程添加PB依赖库

需要在项目中使用ProtocolBuffer,需要将依赖库添加到项目中来,刚才下载的源码objectivec目录中已经包含一个可编译 libProtocolBuffers.a静态库的工程,直接引入工程即可,当然ProtocolBuffer项目也支持cocoapods的方式引入, 在你的Podfile中添加:

platform :ios, '7.1'
pod "Protobuf", "~> 3.0.0-alpha-4"

3.使用PB编译器编译.proto文件

为了验证我们的ProtocolBuffer环境已经搭建好了,以下就来小试牛刀,创建类似以下的test.proto文件:

message Person
{
  required string name = 1; //姓名
  required int32 sex = 2;   //性别
  required int32 age = 3;   //年龄
}

然后通过以下的命令生成生成Model的源代码文件:

protoc --objc_out=./ ./test.proto

在当前目录便可以看到Test.pbobjc.h和Test.pbobjc.m这两个文件了(需要注意的是生成的代码是MRC的,如果引入ARC工程中记得添加-fno-objc-arc的标签).

4.在项目中使用PB完成序列化&反序列化

将上个步骤中生成的源代码添加到工程之中,你就可以直接使用他们了,使用起来非常的方便,示例如下:

// 创建对象
Person *person = [Person new];
person.name = @"TanHao";
person.sex = 1;
person.age = 28;
    
// 序列化为Data
NSData *data = [person data];
    
// 反序列化为对象
Person *person2 = [Person parseFromData:data error:NULL];
NSLog(@"name:%@ sex:%d age:%d",person2.name,person2.sex,person2.age);