前言
之前我们实现了MyHttpRequest与MyHttpResponse的部分方法。接下来我们要实现自己的MyServlet部分方法。创建出自己的Servlet并将其通过xml文件配置到我们的服务器中。
实现
我们需要实现MyServlet的三个常用方法
- init():初始化方法
- void service(MyHttpRequest request, MyHttpResponse response):区分请求方式并处理请求
- destroy():销毁方法
添加MyServlet接口方法
public interface MyServlet {
void init();
void service(MyHttpRequest request, MyHttpResponse response);
void destroy();
}
实现MyHttpServlet抽象接口类
public abstract class MyHttpServlet implements MyServlet {
@Override
public void init() {
System.out.println(getClass().getName()+"执行了初始化方法");
}
@Override
public void service(MyHttpRequest request, MyHttpResponse response) {
String method = request.getMethod();
if (method.equals("GET")){
doGet(request,response);
}else {
doPost(request,response);
}
}
protected abstract void doPost(MyHttpRequest request, MyHttpResponse response);
protected abstract void doGet(MyHttpRequest request, MyHttpResponse response);
@Override
public void destroy() {
System.out.println(getClass().getName()+"执行了销毁方法");
}
}
编写Servlet去继承MyHttpServlet实现doGet与doPost方法
public class MyFirstServlet extends MyHttpServlet {
@Override
protected void doPost(MyHttpRequest request, MyHttpResponse response) {
this.doGet(request,response);
}
@Override
protected void doGet(MyHttpRequest request, MyHttpResponse response) {
response.write("这是我第一个Servlet");
}
}
public class MySecondServlet extends MyHttpServlet {
@Override
protected void doPost(MyHttpRequest request, MyHttpResponse response) {
this.doGet(request,response);
}
@Override
protected void doGet(MyHttpRequest request, MyHttpResponse response) {
response.write("这是我第二个Servlet");
}
}
至此,我们实现了我们的Servlet。但是,并不能通过浏览器访问我们的Serlvet。我们需要写一个xml配置文件来配置浏览器的访问路径与Servlet的映射关系
xml文件映射
<?xml version="1.0" encoding="UTF-8"?>
<myXml>
<myServlet>
<myServlet-name>first</myServlet-name>
<myServlet-class>com.zmt.servlet.MyFirstServlet</myServlet-class>
</myServlet>
<myServlet>
<myServlet-name>second</myServlet-name>
<myServlet-class>com.zmt.servlet.MySecondServlet</myServlet-class>
</myServlet>
<myServlet-mapping>
<myServlet-name>first</myServlet-name>
<myUrl-pattern>/first</myUrl-pattern>
</myServlet-mapping>
<myServlet-mapping>
<myServlet-name>second</myServlet-name>
<myUrl-pattern>/second</myUrl-pattern>
</myServlet-mapping>
</myXml>
xml文件中,描述了ServletName与ServletClass映射与ServletName与Uri的映射关系。接下来编写一个Handler来实现读取xml文件并保存映射关系。这里我使用的是dom4j的jar包中的SAXReader类来解析xml文件。因此需要添加lib文件夹。
大家可以网上搜索一下这两个jar包放入lib目录下
编写Handler代码
public class MyServletHandler {
//这个map用于存放名字和类的全路径的对应关系
public static final ConcurrentHashMap<String, String> NAME_CLASS = new ConcurrentHashMap<>();
//这个map用于存放对外访问路径和名字的关系
public static final ConcurrentHashMap<String, String> PATTERN_NAME = new ConcurrentHashMap<>();
//这个map用于存放对外访问路径和MyServlet的关系
public static final ConcurrentHashMap<String, MyServlet> PATTERN_CLASS = new ConcurrentHashMap<>();
//初始化方法读取xml文件
public static void init() {
SAXReader saxReader = new SAXReader();
try {
String path = URLDecoder.decode(Objects.requireNonNull(MyServletHandler.class.getResource("/").getPath()), "utf-8");
Document document = saxReader.read(new File(path + "/webapp/WEB-INF/web.xml"));
Element element = document.getRootElement();
List<Element> myServlets = element.elements("myServlet");
List<Element> myServletMappers = element.elements("myServlet-mapping");
for (Element myServlet : myServlets) {
String name = myServlet.element("myServlet-name").getText();
String classPath = myServlet.element("myServlet-class").getText();
NAME_CLASS.put(name, classPath);
}
for (Element myServletMapper : myServletMappers) {
String name = myServletMapper.element("myServlet-name").getText();
String pattern = myServletMapper.element("myUrl-pattern").getText();
PATTERN_NAME.put(pattern, name);
}
} catch (IOException | DocumentException e) {
e.printStackTrace();
}
}
}
修改线程池代码
public void execute(Socket socket) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName()+"开始处理请求");
try {
InputStream inputStream = socket.getInputStream();
MyHttpRequest myHttpRequest = new MyHttpRequest(inputStream);
//读取完毕后,进行一个简单响应
OutputStream outputStream = socket.getOutputStream();
MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);
String uri = myHttpRequest.getUri();
System.out.println(uri);
//如果能够根据uri拿到对应的myServlet类
if (MyServletHandler.PATTERN_CLASS.containsKey(uri)) {
MyServlet myServlet = MyServletHandler.PATTERN_CLASS.get(uri);
myServlet.service(myHttpRequest,myHttpResponse);
}else if (MyServletHandler.PATTERN_NAME.containsKey(uri)){//如果还没有初始化MyServlet
String name = MyServletHandler.PATTERN_NAME.get(uri);
System.out.println(name);
String classPath = MyServletHandler.NAME_CLASS.get(name);
MyServlet myServlet = (MyServlet) Class.forName(classPath).newInstance();
MyServletHandler.PATTERN_CLASS.put(uri,myServlet);
myServlet.init();
myServlet.service(myHttpRequest,myHttpResponse);
}else {
myHttpResponse.setStatus(404);
myHttpResponse.write("没有找到该路径");
}
socket.close();
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
});
}
修改启动类代码
public class MyTomCat {
public static void main(String[] args) {
TomCatThreadPool threadPool = new TomCatThreadPool(2);
try {
//监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
MyServletHandler.init();
System.out.println("我的服务器启动成功...");
//socket没接收到关闭事件之前不退出循环
while (!serverSocket.isClosed()) {
//等待连接,该事件会阻塞线程
Socket socket = serverSocket.accept();
threadPool.execute(socket);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败");
}
}
}
启动测试
浏览器访问localhost:8080/first
至此。我们实现了自定义Servlet的应用。