接上文:实现tomcat对于静态资源的功能;
本文增加动态功能以及对比servletapi,项目目录大概就是这样*(当然本文只是用最简单的类加载器来实现动态的功能,相比tomcat本身来说,本案例根本没用到连接器和容器,欲想了解更多,请移步笔者tomcat专栏!)*
html主要是静态的上篇文章说的
package cn.wcy.mytomcat2;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import cn.wcy.mytomcat2.Servlet;
public class Server2 {
//定义一个变量,存放服务端WebContent目录的绝对路径
public static String WEB_ROOT=System.getProperty("user.dir")+"/"+"WebContent";
//定义静态变量,用于存放本次请求的静态页面名称
private static String url = "";
//定义一个静态类型map,存储服务端conf.properties中的配置信息
private static Map<String,String> map = new HashMap<String,String>();
static {
Properties prop = new Properties();
try {
prop.load(new FileInputStream(WEB_ROOT+"\\conf.properties"));
Set set = prop.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
String key = (String)iterator.next();
String value = prop.getProperty(key);
map.put(key,value);
}
}catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//创建ServerSocket,监听本机的80端口,等待来自客户端的请求
ServerSocket ss = null;
Socket socket = null;
OutputStream os = null;
InputStream is = null;
try {
ss = new ServerSocket(8080);
while(true) {
//获取到客户端对应的socket
socket = ss.accept();
//获取到输入流对象
is = socket.getInputStream();
//获取到输出流对象
os = socket.getOutputStream();
//获取HTTP协议的请求部分 ,截取客户端访问的资源名称,将这个资源名称赋值给url
parse(is);
//判断一下获取到的url是静态资源还是动态资源
if(null!=url) {
//如果有.就是有后缀就是说知道了静态资源名
if(url.indexOf(".")!=-1) {
//发送静态资源
sendStaticResource(os);
}else {
sendDynamicResource(is,os);
}
}
}
}catch(Exception e) {
e.printStackTrace();
}finally {
if(null!=is) {
is.close();
is = null;
}
if(null!=os) {
os.close();
os = null;
}
if(null!=socket) {
socket.close();
socket = null;
}
}
}
//获取HTTP协议的请求部分 ,截取客户端访问的资源名称,将这个资源名称赋值给url
private static void parse(InputStream is) throws IOException {
//定义一个变量,存放http协议请求部分数据
StringBuffer content = new StringBuffer(2048);
//定义一个数组,存放http协议请求部分数据
byte[] buffer = new byte[2048];
int i=-1;
i = is.read(buffer);
for(int j=0;j<i;j++) {
content.append((char)buffer[j]);
}
System.out.println(content);
parseUrl(content.toString());
}
private static void parseUrl(String content) {
//定义两个变量存放请求行的两个控制的位置
int index1,index2;
//获取http请求部分第1个空格的位置
index1 = content.indexOf(" ");
if(index1!=-1) {
index2=content.indexOf(" ",index1+1);
//获取http请求部分第二个空格的位置
if(index2>index1) {
//获取字符串穿获取到本次请求资源的名称
url = content.substring(index1+2,index2);
}
}
}
private static void sendDynamicResource(InputStream is,OutputStream os) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
//将http协议的响应行和响应头发送到客户端
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
// os.write("Server:Apache\n".getBytes());
os.write("Content-type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
//判断map中的是否存在key是和本次请求的资源路径一直
if(map.containsKey(url)) {
//如果包含指定的key
String value = map.get(url);
//通过反射将对应的java程序加载
Class clazz = Class.forName(value);
Servlet servlet = (Servlet)clazz.newInstance();
//执行init方法
servlet.init();
//执行service方法
servlet.Service(is,os);
}
}
//发送静态资源
private static void sendStaticResource(OutputStream os) throws IOException {
//定义一个字节数组,用于存放本次请求的静态资源demo01.html的内容
byte[] bytes = new byte[2048];
//定义一个文件输入流,用户获取静态资源demo01.html的内容
FileInputStream is = null;
try {
//创建文件对象File,代表本次要请求的资源demo01.html
File file = new File(WEB_ROOT,url);
if(file.exists()) {
//向客户端输出http协议的响应头和响应行
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
is = new FileInputStream(file);
int ch = is.read(bytes);
while(ch!=-1) {
os.write(bytes,0,ch);
ch = is.read(bytes);
}
}else {
os.write("HTTP/1.1 404 not found\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
String errorMessage = "404 not found";
os.write(errorMessage.getBytes());
}
}catch(Exception e) {
e.printStackTrace();
}finally {
if(null!=is) {
is.close();
is = null;
}
}
}
}
这里的server类相比上篇文章的没有增加多少功能,可以对比一下也就是发送动态的功能吧;接下来是一个servlet接口:
package cn.wcy.mytomcat2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
//所有服务端的java小程序要实现的内容
public interface Servlet {
//初始化
public void init();
//里边可以动态写入内容
public void Service(InputStream is,OutputStream os) throws IOException;
//销毁方法
public void destroy();
}
然后有两个实现类:实现类的方法就是重写了接口的方法而已主要是在server()方法中做的动作;
package cn.wcy.mytomcat2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import cn.wcy.mytomcat2.Servlet;
public class FirstServlet implements Servlet{
@Override
public void init() {
// TODO Auto-generated method stub
System.out.println("FirstServlet....init");
}
@Override
public void Service(InputStream is, OutputStream os) throws IOException {
// TODO Auto-generated method stub
os.write("I am form FirstServlet".getBytes());
os.flush();
}
@Override
public void destroy() {
// jdk的servlet接口也是有这个方法的,并且实现类注释已经写明了说什么也不做
}
}
secondServlet所做的事情跟first类似不再赘述;然后是conf.properties
aa=cn.wcy.mytomcat2.FirstServlet
bb=cn.wcy.mytomcat2.SecondServlet
就是用来提供全限定类名的;
整个逻辑也就是通过ServerSocket拿到访问的客户端的Socket,然后在拿到OutputStream流之后手动写入响应头和响应体,剩下的就交给浏览器自己解析就可以了当然我们也可以用web.xml来配置映射关系,但是需要用到xml解析技术我们这里重点不在这不赘述了;
接下来我们来给apache-tomcat做一下对比用我们这次所定义的类:
- servlet是java中定义的一个接口,我们写jsp的controller实现的是HttpServlet类,这个类里边有doget和dopost方法,里边也包含了对inputstream的解析,不过是以request的形式,重写的那两个方法是protect类型的;
- HttpServlet类是继承了静态类GenericServlet,这个类实现了Servlet和ServletConfig两个接口当然还有这个java.io.Serializable,不过这个接口里边方法也是空方法,也就是起了一个标志性的作用(标志可序列化)
Java API中java.io.Serializable接口源码: public interface Serializable {
}
- 类通过实现java.io.Serializable接口可以启用其序列化功能。未实现次接口的类无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
- ServletConfig中的方法:
- getInitParameter(): 获取指定名称的初始化参数值。
- getInitParameterNames():获取当前 Servlet 所有的初始化参数名称。其返回值为枚举类型 Enumeration。
- getServletName():获取当前 Servlet 的中指定的 Servlet名称。
- getServletContext():获取到当前 Servlet 的上下文对象 ServletContext。
– ServletConfig接口的特点
每一个servlet都对应一个ServletConfig用于封装各自的配置信息,即有几个servlet就会产生几个ServletConfig对象。
上一篇文章:实现tomcat的静态资源功能;