学习内容》:
//========学习内容=========
请求与响应(4)
请求与响应(5)
《代码内容》:
主要思路—-> 封装response在controller操作完后把responese装满然后响应回浏览器
浏览器解析HTML标签,按照需求格式解析在浏览器上
并把port单独做一个文件的读写,用户用的时候就可以在配置文件中修改服务器端口
//===================================================================================//
服务器的封装与更新优化
ServerHandler
package server;
import controller.IndexController;
import java.io.*;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.HashMap;
import java.util.Properties;
public class ServerHandler extends Thread{
private Socket socket = null;
public ServerHandler(Socket socket){
this.socket = socket;
}
//重写线程的run方法 也就是我们的Handler需要做的事情
public void run(){
/**
* 分析:我们需要启动服务器后,通过socket获取流读取请求过来的资源名
* 那么--->我们就设计一个方法 来读这个请求资源名
* **/
this.ReadRequestContentAndParams(socket);
}
//设计一个方法 来读取浏览器发送过来的请求
private void ReadRequestContentAndParams(Socket socket) {
try {
//因为 浏览器 发送过来的是一行的信息--->资源名 用高级流来读取一行
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//读取到的资源名和参数
String contentAndParams = reader.readLine();
//现在我拿到了资源名和参数,我需要把他们做一个解析。
this.parseContentAndParams(contentAndParams);
} catch (IOException e) {
e.printStackTrace();
}
}
//设计一个方法 用来解析 资源名 和 参数
private void parseContentAndParams(String contentAndParams) {
//index?uname=武伟&upassword=123123
String content = null;
HashMap paramsMap = null;
if (contentAndParams.contains("?")){
content = contentAndParams.substring(0, contentAndParams.indexOf("?"));
String[] params = contentAndParams.substring(content.length() + 1).split("&");
for (String keyAndValue : params){
String[] KV = keyAndValue.split("=");
paramsMap = new HashMap<String,String>();
paramsMap.put(KV[0],KV[1]);
}
}else{
content = contentAndParams;
}
//解析完后 把解析好的资源名 和 参数 包装成一个request对象
HttpServletRequest request = new HttpServletRequest(content,paramsMap);
HttpServletResponse response = new HttpServletResponse();
//解析好了之后 我们可以通过拿到的 资源名 去找 Controller
ServletController.findController(request,response);
//响应回去
this.responseToBrower(response);
}
public void responseToBrower(HttpServletResponse response){
try {
System.out.println(response.getResponse());
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println(response.getResponse());
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ReadServerFilePorperties
package server;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
public class ReadServerProperties {
private static HashMap<String,String> serverFileMap= new HashMap<>();
static {
Properties properties = new Properties();
try {
properties.load(new FileReader("src/server.properties"));
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()){
String key = (String)enumeration.nextElement();
String value = properties.getProperty(key);
serverFileMap.put(key,value);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Integer getServerConfig(String key){
return Integer.parseInt(serverFileMap.get(key));
}
}
HttpServletResponse
package server;
public class HttpServletResponse {
private StringBuilder builder = new StringBuilder();
public void write(String str){
this.builder.append(str);
}
public String getResponse(){
return this.builder.toString();
}
}
ServletController
package server;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
public class ServletController {
/**
* 这个类的目的是为了管理 findController方法
* 1.方法与之前服务器Handler做的事情不一致 抽离出来
* 2.每一次找寻Controller类的时候都需要参考一下web.properties
* 读取文件性能会比较低 增加一个缓存机制
* 3.每一个Controller类都是由findController方法来找寻
* 找到了Controller类的目的是为了执行里面的方法
* 让类中的方法有一个统一的规则----便于查找和使用
* 4.发现Controller类与之前的Service和Dao相似 只有方法执行 没有属性
* 让Controller类的对象变成单例模式
* **/
//定义一个Map集合 用来做缓存,在类加载的时候就把配置文件读取到集合中
private static HashMap<String,String> controllerNameMap = new HashMap<>();
//定义一个Map集合 用来做缓存,用到这个对象的时候,就把这个对象new出来,存到集合中
private static HashMap<String,HttpServlet> controllerObjectMap = new HashMap<>();
//静态块,只用一次所以静态吗,来执行读取配置文件和存入集合中
static {
Properties properties = new Properties();
try {
properties.load(new FileReader("src//web.properties"));
//获取配置文件的所有key
Enumeration<?> enumeration = properties.propertyNames();
//遍历所有的key,通过每个key找值
while (enumeration.hasMoreElements()){
String content = (String)enumeration.nextElement();
String controllerClassName = properties.getProperty(content);
controllerNameMap.put(content,controllerClassName);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//通过资源名拿到配置文件中对应的控制层的类全名,进行反射类全名,执行控制层方法
public static void findController(HttpServletRequest request,HttpServletResponse response) {
Properties properties = new Properties();
String content = request.getContent();
try {
//通过配置文件的key找到对应的控制层的名字
String realControllerName = controllerNameMap.get(content);
//我拿到 类全名之后 进行反射然后执行对应的方法
Class clazz = Class.forName(realControllerName);
if (controllerObjectMap.get(content) == null){
/**如果对象集合中,当前请求的这个对象不存在,就new一个,然后放入集合中**/
HttpServlet httpServlet = (HttpServlet)clazz.newInstance();
controllerObjectMap.put(content,httpServlet);
System.out.println("在对象集合中缓存了一次对象");
}
HttpServlet httpServlet = controllerObjectMap.get(content);
Method controllerMethod = clazz.getMethod("service", HttpServletRequest.class, HttpServletResponse.class);
controllerMethod.invoke(httpServlet,request,response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
IndexController
package controller;
import server.HttpServlet;
import server.HttpServletRequest;
import server.HttpServletResponse;
public class IndexController extends HttpServlet {
public void service(HttpServletRequest request,HttpServletResponse response){
response.write("银行系统<br>");
response.write("账号<br>");
response.write("密码<br>");
}
}
//===================================================================================//
浏览器的更新—>解析响应信息
package brower;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Brower {
private Socket socket;
private Scanner scanner = new Scanner(System.in);
//设计一个方法 来模拟输入浏览器中的URL
public void openBrower(){
System.out.println("请输入url地址");
String url = scanner.nextLine();
//将输入的url地址进行解析 拆分开 ip prot 用来创建socket对象
//资源名用来发出请求资源
this.parseUrl(url);
}
//设计一个方法 用来解析拆分Url
//localhost:9999/index.jsp
public void parseUrl(String url){
String ip = url.substring(0,url.indexOf(":"));
String port = url.substring(ip.length()+1,url.indexOf("/"));
String contentAndParam = url.substring(ip.length()+1 + port.length()+1);
//用当前截取到的信息创建socket并发出请求
this.createSocketAndRequest(ip,port,contentAndParam);
}
//设计一个方法 用来创建Socket并发出请求
public void createSocketAndRequest(String ip,String port,String contentAndParam){
try {
socket = new Socket(ip, Integer.parseInt(port));
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println(contentAndParam);
writer.flush();
//等待响应信息
this.readAndParseServerResponse();
} catch (IOException e) {
e.printStackTrace();
}
}
//接收服务器的响应信息
public void readAndParseServerResponse(){
try {
BufferedReader builder = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String responseStr = builder.readLine();
String brResponse = responseStr.replace("<br>", "\r\n");
int lessThanIndex = responseStr.indexOf("<");
int grentThanIndex = responseStr.indexOf(">");
if (lessThanIndex != -1 && grentThanIndex != -1 && grentThanIndex > lessThanIndex){
}
System.out.println(brResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Brower brower = new Brower();
brower.openBrower();
}
}
学习总结:
学懂的:
熟练掌握了如果做缓存机制,更加透彻的理解static关键字的用法
理解了Servlet如果管理Controller机制
请求响应—图:
有问题的地方:
无