面向接口编程,分不多,但是绝对干货
如何运用面向接口编程的方式设计一个 SocketUtil? SocketUtil可以用于普通的长连接以及HTTP请求;主要是设计HTTP的接口或者抽象类,但是一定要给普通长连接留下开发余地,尽量满足开闭原则。
我已经做了一个,但是总感觉差点什么,主要是想不到哪些地方是将来可能会变的,或者如何以更方便扩展的方式去设计。
下面贴出我设计的 SocketUtil :
Java code
// ----------------- 接口 ----------------------
package com.liuwei.util.socket;
public interface SocketUtil {
/**
* 连接远端
*/
public void connect( String remote, int port ) throws Exception;
/**
* 断开
*/
public void disconnect();
/**
* 发送
*/
public Object send( Object data )
throws Exception;
}
// ----------------- 抽象 ----------------------
package com.liuwei.util.socket.impl;
import
import
import
import java.nio.channels.SocketChannel;
import com.liuwei.util.socket.SocketUtil;
public abstract class AbstractSocketUtil implements SocketUtil {
protected SocketAddress remote;
protected SocketChannel channel;
protected String hostAddress;
protected String uri;
public void connect(String remote, int port) throws Exception {
remote = this.format(remote);
this.remote = new InetSocketAddress(this.hostAddress, port);
this.channel = SocketChannel.open();
this.channel.configureBlocking(false);
this.channel.connect(this.remote);
for (System.out.println("\n>> 开始连接: " + remote); true;) {
System.out.println(">> .....");
if (!this.channel.finishConnect()) {
Thread.sleep(10);
}
else {
System.out.println(">> 连接成功!");
break;
}
}
}
public void disconnect() {
try {
if (channel.isOpen()) {
channel.close();
}
} catch (IOException exception) {
}
}
abstract protected String format(String address);
}
// ----------------- 具体实现:可以不看,没什么用 ----------------------
package com.liuwei.util.socket.impl;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import com.liuwei.util.socket.entity.HttpRequestData;
public class HttpSocketUtil extends AbstractSocketUtil {
private String charset = "UTF-8";
public HttpSocketUtil() {
}
public HttpSocketUtil(String charset) {
this.charset = charset.isEmpty() ? "UTF-8" : charset;
}
public Object send(Object data) throws Exception {
byte[] requestData = null;
ByteBuffer buffer = null;
requestData = this.buildRequestData(data).getBytes(this.charset);
buffer = ByteBuffer.allocate(1024);
buffer.put(requestData);
buffer.flip();
this.channel.write(buffer);
return this.receive(this.channel, buffer);
}
protected String format(String address) {
address = address.trim().replaceFirst("http://", "");
address = address.trim().replaceFirst("https://", "");
int index = address.indexOf("/");
if (index == -1) {
index = address.length();
this.uri = "/";
} else {
this.uri = address.substring(index);
}
this.hostAddress = address.substring(0, index);
return address;
}
private String receive(SocketChannel channel, ByteBuffer buffer)
throws Exception {
StringBuilder builder = new StringBuilder();
long timeout = 5000;
long timeoutStart = 0;
int byteSize = 0;
for (boolean startFlag = false; true;) {
buffer.clear();
int byteNum = this.channel.read(buffer);
if (byteNum != 0) {
startFlag = true;
timeoutStart = 0;
if (byteNum == -1) {
break;
}
byteSize += byteNum;
buffer.flip();
builder.append(new String(buffer.array(), this.charset));
} else if ((byteNum == 0) && (startFlag)) {
timeoutStart = (timeoutStart == 0) ? System.currentTimeMillis()
: timeoutStart;
if ((System.currentTimeMillis() - timeoutStart) > timeout) {
break;
}
Thread.sleep(500);
}
}
System.out.println(">> 本次读取到 " + byteSize / 1024 + "K 字节");
return builder.toString();
}
private String buildRequestData(Object data) {
if (data instanceof HttpRequestData) {
return this.buildHttpRequestHead((HttpRequestData) data);
}
return null;
}
private String buildHttpRequestHead(HttpRequestData data) {
String sessionIdKeyAndValue = data.getSessionIdKey() + "="
+ data.getSessionId();
StringBuffer httpHead = new StringBuffer();
httpHead.append("GET " + this.uri + " HTTP/1.1\r\n");
httpHead.append("Host: " + this.hostAddress + "\r\n");
httpHead.append("User-Agent:"
+ "Mozilla/5.0 (Windows NT 6.1; rv:22.0)\r\n");
httpHead.append("Cookie: " + sessionIdKeyAndValue + "\r\n");
httpHead.append("Connection: Keep-Alive\r\n");
httpHead.append("\r\n");
return httpHead.toString();
}
}
// ----------------- 下面是今天心血来潮刚改的 ----------------//
// ----------------- 接口 ----------------------
package com.liuwei.util.socket;
public interface SocketUtil {
/**
* 连接远端
*/
public void connect( String remote, int port ) throws Exception;
/**
* 断开
*/
public void disconnect();
/**
* 发送
*/
public Object send() throws Exception;
}
// ----------------- 还是接口 ----------------------
package com.liuwei.util.socket;
import java.util.Map;
public interface HttpUtil extends SocketUtil {
/**
* 使用默认端口连接远端(80)。
* @param remote 这是URL,后面不要跟URI、端口。如果需要更改端口,可以使用:
* connect( String remote, int port ) throws Exception
* 例如:http://www.baidu.com
* @throws Exception 连接失败抛出异常。。。。
*/
public void connect( String remote ) throws Exception;
/**
* 顾名思义,就是指定访问的服务器路径。
* @param URI Uniform Resource Identifier
*/
public void setURI( String URI );
/**
* 指定HTTP请求头。
* @param header
*/
public void setHeader( Map<String, String> header );
/**
* 指定向服务器发送的内容。
* @param content
*/
public void setBody( String content );
}
注意看这里:
别那么吝啬,如果每个都能说出些干货,相互取经,说不定一个困惑多年的、怎么都想不明白的理论一下子就明白了,受益匪浅的。
很多东西,就算看人家先进的源码,也不一定明白。我写这个东西也是因为看Spring的源码,尤其是BeanFactory,好像有点感觉,但是模模糊糊的,理解不完全更讲不出来,很希望得到高人指点。