C(Client客户端)/S(Server服务端)架构
客户端:Socket相关API
服务端:ServerSocket Thread
数据库:XML DOM4J
Data Transfer Object 它一般用于后端->前端的交互使用
VO -> View Object 它一般用于前端视图层->后端的交互使用
POJO -> Plain Old Java Object 主要用于后期ORM框架映射使用
创建DTO作为数据交换对象
上传需要客户端再开启线程
下载需要服务器在开启线程
这两者socket、输入输出不能太早关闭
上传的文件要添加到对应小说分类和小说XML中,并更新MAP集合
我们定义了一系列的服务类,不同的服务类专门用于处理不同的请求。
LoginService
RegisterService
GetNovelClassesService
GetNovelsService
DownloadService
UploadService
在抽取一个接口Service
让服务继承
场景:根据客户的描述 产生符合要求的实例。
1.产品规范 接口
Service
2.符合规范的产品 接口的实现类
LoginService
RegisterService
GetNovelClassesService
GetNovelsService
DownloadService
UploadService
3.生产产品的工厂
ServiceFactory
4.客户
main
字节码类型 Class
3种获取方法
1.类名.class
2.对象名.getClass()
3.Class.forName(全类名)
可以获取一个类的所有构造、方法、属性、注解…
也可以对这些构造…进行使用
/**
* 服务端工厂
* 简单工厂模式:3.生产产品的工厂类
* @author 周太阳
* 2019年5月13日
*/
public class ServiceFactory {
/**存储产品的列表*/
private static HashMap<String,String> map = new HashMap<String,String>();
/**
* 初始化工厂
*/
static {
// 创建SAXReader读取XML中的DOM树
SAXReader reader = new SAXReader();
try {
Document document = reader.read(InitProperties.getPropertyValue("sunshine.server.config.service"));
// 获得根元素并遍历元素
Element rootElement = document.getRootElement();
List<Element> elements = rootElement.elements();
for (Element element : elements) {
// 将对应的元素的属性放入map集合中
map.put(element.attributeValue("id"), element.attributeValue("class"));
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* @return 用反射返回对应的服务的对象
* @throws Exception
*/
public static Service getService(String service) throws Exception {
try {
// 得到需要服务的Class地址
String className = map.get(service);
// 判断Class地址是否为空
if (className == null) {
// 自定义抛出
throw new ClassNotFoundException();
}else {
// 利用反射得到对应的对象
Class<?> clazz = Class.forName(className);
// 返回对象
return (Service)clazz.newInstance();
}
} catch (ClassNotFoundException e) {
throw new NoSuchServiceException("该功能尚未研发成功,敬请期待!");
}
}
}
借鉴于未来一些框架思想
如小说分类
<novelClasses>
<novelClass>
<classname>武侠classname>
<catalog>txtcatalog/wuxia/catalog>
<config>resource/novel/txt_wuxia.xmlconfig>
novelClass>
<novelClass>
<classname>言情classname>
<catalog>txtcatalog/yanqing/catalog>
<config>resource/novel/txt_yanqing.xmlconfig>
novelClass>
novelClasses>
小说
<novellist>
<novel>
<name>古侠今遇name>
<author>醉人岁月author>
<description>与世隔绝三百多年的“碧湖山庄”四少侠出现在大都市中,既有古人的风采,又有现代人的韵味!description>
<filename>古侠今遇.txtfilename>
novel>
<novel>
<name>寒剑孤灯name>
<author>忧郁丁香author>
<description>寒剑孤灯,传奇故事description>
<filename>寒剑孤灯.txtfilename>
novel>
novellist>
接口实现类都需要重复实现某些方法。
继承:减少重复代码 ,提升代码复用性。
package org.sunshine.server.service;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import org.sunshine.server.utility.DTO;
/**
* 服务器基本方法
* 实现Service接口
* 提取输入输出流的开启与关闭
* 和继承service方法
* @author 周太阳
* 2019年5月15日
*/
public abstract class BasicService implements Service{
/**创建接收套接字的属性*/
private Socket socket;
private ObjectInputStream ois;
private ObjectOutputStream oos;
/**创建保存传输的data对象*/
private Object data;
private int receive;
/**
* 初始化输入输出
* @param socket
*/
public void init(Socket socket, ObjectInputStream ois, ObjectOutputStream oos, Object data) throws Exception {
// 得到当前对象的socket
this.socket = socket;
this.ois = ois;
this.oos = oos;
// 返回数据传输对象DTO
this.data = data;
this.receive = receive;
}
/**
* Service方法
* @throws Exception
*/
@Override
public abstract void service() throws Exception;
/**
* 释放资源
*/
@Override
public void destroy() throws Exception {
ois.close();
oos.close();
socket.close();
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public ObjectInputStream getOis() {
return ois;
}
public void setOis(ObjectInputStream ois) {
this.ois = ois;
}
public ObjectOutputStream getOos() {
return oos;
}
public void setOos(ObjectOutputStream oos) {
this.oos = oos;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public int getReceive() {
return receive;
}
public void setReceive(int receive) {
this.receive = receive;
}
}
JAVA三层架构:表现层 业务逻辑层 数据访问层
Data Access Object 数据存取对象
常用基本取名代码:
举例:对用户的操作
User getUserById(Long id);
User getUserByUserName(String name);
List getUserListByConditions(Map
void addUser(User user);
void deleteUser(User user);
void updateUser(User user);
/**
* 通信工具类
* @author 周太阳
* 2019年5月16日
*/
public class Client2Service {
private ObjectInputStream ois;
private ObjectOutputStream oos;
private Socket socket;
public Client2Service(String host, int port) {
super();
try {
socket = new Socket(host, port);
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 释放资源
* @throws IOException
*/
public void destroy() throws IOException {
oos.close();
ois.close();
socket.close();
}
/**
* 发送请求
* @throws IOException
*/
public DTO requestService(DTO dto) throws Exception {
oos.writeObject(dto);
DTO response = (DTO)ois.readObject();
// 后期操作关流可以不写在这里
destroy();
return response;
}
}
执行某个服务的方法的时候,这个服务的返回值是一个服务,然后通过循环继续执行。
public class ClientMain {
private static Service service = ServiceFactory.getService(ServiceConstants.START_SERVICE);
public static void main(String[] args) {
while(true) {
if (service == null) {
System.out.println("程序结束!");
break;
}
try {
service = service.service();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
如user,constructs
/**
* 用户类常量
* @author 周太阳
* 2019年5月19日
*/
public class UserConstants {
/**用户登录成功*/
public static final int USER_LOGIN_SUCCESS = 1;
/**登录失败*/
public static final int USER_LOGIN_FAILED = 2;
/**登录发生错误*/
public static final int USER_LOGIN_ERROR = 3;
/**用户注册名存在*/
public static final int USER_REGISTER_ALREADY_EXISTS = 4;
/**用户注册发生错误*/
public static final int USER_REGISTER_ERROR = 5;
/**用户注册成功*/
public static final int USER_REGISTER_SUCCES = 6;
}
提升系统中的代码阅读性和维护性。
发现有很多值使用比较频繁,又不好记,可能后期要修改可以定义为常量
服务常量
反射类的服务常量
字符串常量等……
/**
* 反射类的服务常量
* @author 周太阳
* 2019年5月19日
*/
public class ServiceConstants {
/**开始*/
public static final String START_SERVICE = "start";
/**登录*/
public static final String LOGIN_SERVICE = "login";
/**注册*/
public static final String REGISTER_SERVICE = "register";
/**获得小说分类*/
public static final String GET_NOVEL_CLASS_SERVICE = "getNovelClasses";
/**获得小说*/
public static final String GET_NOVEL_SERVICE = "getNovels";
/**获得小说预览*/
public static final String GET_NOVEL_PREVIEW_SERVICE = "getPreview";
/**下载*/
public static final String DOWNLOAD_SERVICE = "downLoad";
/**上传*/
public static final String UPLOAD_SERVICE = "upLoad";
}
Properties是继承HashTable的,里面内容=左边是键,=右边是值
可以将其用到反射上,配合永动机更方便。
#服务器配置
#服务器端口号
sunshine.socket.server.port=8080
#获取服务器配置文件
sunshine.server.config.service=resource/Service.xml
#用户配置文件地址
sunshine.server.config.user=resource/user/UserInfo.xml
#小说分类配置文件地址
sunshine.server.config.novel.class=resource/novel/NovelClass.xml
将配置信息存放在配置文件中(XML不推荐,因为XML需要DOM解析,使用比较复杂)