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("该功能尚未研发成功,敬请期待!"); } } }借鉴于未来一些框架思想
如小说分类
<?xml version="1.0" encoding="UTF-8"?> <!-- 服务器小说分类 --> <novelClasses> <novelClass> <classname>武侠</classname> <catalog>txtcatalog/wuxia/</catalog> <config>resource/novel/txt_wuxia.xml</config> </novelClass> <novelClass> <classname>言情</classname> <catalog>txtcatalog/yanqing/</catalog> <config>resource/novel/txt_yanqing.xml</config> </novelClass> </novelClasses>小说
<?xml version="1.0" encoding="UTF-8"?> <!-- 武侠小说 --> <novellist> <novel> <name>古侠今遇</name> <author>醉人岁月</author> <description>与世隔绝三百多年的“碧湖山庄”四少侠出现在大都市中,既有古人的风采,又有现代人的韵味!</description> <filename>古侠今遇.txt</filename> </novel> <novel> <name>寒剑孤灯</name> <author>忧郁丁香</author> <description>寒剑孤灯,传奇故事</description> <filename>寒剑孤灯.txt</filename> </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<String,Object> conditions); 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解析,使用比较复杂)