这周的代码出了点问题,目前还没有调试完全,就先不把所有代码都粘贴上来了。下周默默的去调代码了!
在上周我们搭建的服务器,仅仅具有一个传输一个界面的功能。在实际应用中,对于同一份资源的访问,往往可以通过多条路径进行访问,而且可以处理多个不同的请求。这些需求,就导致了我们在搭建服务器的时候,需要逐步去完善服务器的功能。
一、对于多请求的处理——多态
我们模拟一个具有注册和登录功能的网页,首先我们定义一个抽象类Servlet,为后面的具体功能实现提供一个规范的编写方法,然后再创建具体的注册和登录类,实现网页多请求处理,也就是多态。
第一步:创建抽象类Servlet
package com.peng.server.demo03;
/**
* 抽象为一个父类
*/
public abstract class Servlet {
public void service(Response rep,Request req) throws Exception{
this.doGet(rep,req);
this.doPost(rep,req);
}
public abstract void doGet(Response rep,Request req) throws Exception;
public abstract void doPost(Response rep,Request req) throws Exception;
}
第二步:创建登录类
package com.peng.server.demo03;
/**
* 登录界面
*/
public class LoginServlet extends Servlet {
@Override
public void doGet(Response rep, Request req) throws Exception {
String name = req.getParameter("uname");
String pwd = req.getParameter("pwd");
if(login(name,pwd)) {
rep.println("登陆成功");
}else {
rep.println("登录失败");
}
}
public boolean login(String name,String pwd) {
return name.equals("peng") && pwd.equals("123456");
}
@Override
public void doPost(Response rep, Request req) throws Exception {
// TODO Auto-generated method stub
}
}
第三步:创建注册类
package com.peng.server.demo03;
public class RegisterServlet extends Servlet{
@Override
public void doGet(Response rep, Request req) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void doPost(Response rep, Request req) throws Exception {
rep.println("<html><head><title>返回注册</title>");
rep.println("</head><body>");
rep.println("你的用户名为:"+req.getParameter("uname"));
rep.println("</body></html>");
}
}
二、多条路径访问同一份资源
在实际情况下,对于同一份资源或者说文件的访问,往往具有多条路径可以指向目的地,所以我们需要解决多条路径访问同一份资源的问题。我们可以设想一下相关的解决方案,为了满足多条路径访问同一份资源,简单的思路就是多对一的解决方案。所以我们可以使用容器来解决这个问题,在容器的选择上,我们使用键值对map来存储目标资源和相关的所有路径,只要在键值对存在着相关路径,那么我们就可以在键值对map上进行搜索,然后将键值对中对应的键取出来,与其相对应的值进行比对。具体实现如下:
第一步:建立一个上下文,由于我们所有的网页信息均在Servlet中进行实现,所以,我们首先使用一个map为每个多态类别起一个别名,在后面的存储的时候,方便直接存储字符串,而不是存储整个类。然后再使用一个map,用来存储不同路径与Servlet类别的映射关系。根据每一条不同的路径,直接进行映射到同一份资源处。
package com.peng.server.demo03;
import java.util.HashMap;
import java.util.Map;
/**
* 上下文
*/
public class ServletContext {
/**
* 使用两个map的主要作用在于:可以对于同一份资源LoginServlet,首先为其取一个别名,
* 然后可以使用多个路径访(/log --->login || /login --->login)问同一份资源LoginServlet
*/
//为每一个servlet取一个别名
//login ---> com.peng.server.LoginServlet
private Map<String,String> servlet;
// mapping作为一个映射,对于多个访问路径,指向同一份资源
// url --->login
// /log --->login
// /login --->login
private Map<String,String> mapping;
public ServletContext() {
servlet = new HashMap<String,String>();
mapping = new HashMap<String,String>();
}
//set和get方法
public Map<String, String> getServlet() {
return servlet;
}
public void setServlet(Map<String, String> servlet) {
this.servlet = servlet;
}
public Map<String, String> getMapping() {
return mapping;
}
public void setMapping(Map<String, String> mapping) {
this.mapping = mapping;
}
}
第二步:将关于登录和注册的路径存储在相关的map中,比如对于路径 : /login ,将访问: /login;对于路径 : /log ,将访问: /login;对于路径: /reg ,将访问: /register。
package com.peng.server.demo03;
import java.util.Map;
public class WebApp {
private static ServletContext context;
static {
context = new ServletContext();
Map<String,String> mapping = context.getMapping();
/**
* 传送路径:
* 对于路径 : /login ,将访问: /login
* 对于路径 : /log ,将访问: /login
* 对于路径: /reg ,将访问: /register
*/
mapping.put("/login", "/login");
mapping.put("/log", "/login");
mapping.put("/reg", "/register");
Map<String,String> servlet = context.getServlet();
servlet.put("login","com.peng.server.LoginServlet");
servlet.put("register","com.peng.server.RegisterServlet");
}
//使用了反射的动态属性
public static Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
if(null == url || (url=url.trim()).equals("")) {
return null;
}
//根据字符串(完整路径)创建对象
String name = context.getServlet().get(context.getMapping().get(url));
return (Servlet)Class.forName(name).newInstance();//在动态使用的时候创建这个对象 确保空构造存在
}
}
三、分发器的封装
由于整个网页的请求服务属于多线程处理,所以我们需要建立一个分发器,来进行对各个用户的请求与应答进行相关的处理。
package com.peng.server.demo03;
import java.io.IOException;
import java.net.Socket;
import com.peng.server.util.CloseUtil;
public class Dispatcher implements Runnable{
private Socket client;
private Response rep;
private Request req;
private int code =200;
public Dispatcher(Socket client) {
this.client = client;
try {
req = new Request(client.getInputStream());
rep = new Response(client.getOutputStream());
} catch (IOException e) {
code = 500;//报错后,将代码状态推送1个500出去,表示代码有问题
return ;
}
}
@Override
public void run() {
try {
Servlet serv = WebApp.getServlet(req.getUrl());//动态的通过访问地址获取访问相关资源
if(null == serv) {
this.code = 404; //找不到处理
}else {
serv.service(rep, req);
}
rep.pushToClient(code);
} catch (Exception e) {
//若此处报错,则继续尝试推送一个500出去
this.code = 500;
}
try {
rep.pushToClient(500);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
CloseUtil.closeAll(client);
}
}
四、关于反射的一点概念:
在我们将多条路径和目的地资源写入到map中时,我们使用了一点反射的概念。下面简单介绍一些关于反射的东西。
1、动态语言:在运行期间随意的改变对象的类型 +结构 例如:ruby js
java不是动态语言,但具有动态属性(反射)
2、反射 镜子
1)在编译器创建对象 new 完整类名 反射---->通过对象 找出类名
2)在运行期动态的创建对象 分析对象(属性、方法)
3)jvm在创建对象时自动生成与之对应Class对象,同一个类的多个对象在jvm只有一个与之对应的class对象
3、获取Class类别的几种常用方法
1)、可以看成类的元数据。每一个对象在创建的时候,jvm会自动生成一个与之对应的class。同类型的对象对应一个class
2)、获取class对象的三种方式
a、Object中的getClass方法 : 对象.getClass()
b、类.class形式 : 如student.class.
c、最常用,Class类的静态方法 :Class.forName("完整类名")