一、动态资源的获取
因为TimeServlet无法直接调用,所以获得相应的路径。
package processor;import connector.ConnectorUtils;import connector.Request;import connector.Response;import javax.servlet.Servlet;import javax.servlet.ServletException;import java.io.File;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.net.MalformedURLException;import java.net.URL;import java.net.URLClassLoader;public class ServletProcessor {//准备好URLClassLoaderURLClassLoader getServletLoader() throws MalformedURLException {File webRoot = new File(ConnectorUtils.WEB_ROOT);URL webRootUrl = webRoot.toURI().toURL();return new URLClassLoader(new URL[]{webRootUrl});}Servlet getServlet(URLClassLoader loader, Request request) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {/*/servlet/TimeServlet*/String uri = request.getRequestUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);Class servletClass = loader.loadClass(servletName);Servlet servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();return servlet;}public void process(Request request, Response response) throws IOException {try {URLClassLoader loader = getServletLoader();Servlet servlet = getServlet(loader, request);servlet.service(request, response);} catch (MalformedURLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}}
二、测试ServletProcessor
package processor;import connector.Request;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.Test;import test.util.TestUtils;import javax.servlet.Servlet;import java.lang.reflect.InvocationTargetException;import java.net.MalformedURLException;import java.net.URLClassLoader;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 15:32*/public class ServletProcessorTest {private static final String SERVLET_REQUEST="GET /servlet/TimeServlet HTTP/1.1";@Testpublic void givenServletRequest_thenLoadServlet() throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Request request= TestUtils.createRequest(SERVLET_REQUEST);ServletProcessor processor=new ServletProcessor();URLClassLoader loader=processor.getServletLoader();Servlet servlet=processor.getServlet(loader,request);Assertions.assertEquals("webroot.TimeServlet",servlet.getClass().getName());}}
三、使用facade模式
示例中这个代码明显出现问题,既涉及到服务器的开发也涉及到Servlet的开发,现实中这两个开发应属于不同群体。所以我们要保证服务器的开发不会被Servlet的人员过分利用,例如如下情况:
这种情况很危险,Servlet的人员可以肆意调用函数。
为了防止这种问题发生,我们需要应用Facade模式。
首先需要写如下两个函数
ResponseFacade
把ServletResponse放置在ResponseFacade中就可以有效防止这种非法转型。
package connector;import javax.servlet.ServletOutputStream;import javax.servlet.ServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.Locale;public class ResponseFacade implements ServletResponse {private ServletResponse response = null;public ResponseFacade(Response response) {this.response = response;}@Overridepublic String getCharacterEncoding() {return response.getCharacterEncoding();}@Overridepublic String getContentType() {return response.getContentType();}@Overridepublic ServletOutputStream getOutputStream() throws IOException {return response.getOutputStream();}@Overridepublic PrintWriter getWriter() throws IOException {return response.getWriter();}@Overridepublic void setCharacterEncoding(String s) {response.setCharacterEncoding(s);}@Overridepublic void setContentLength(int i) {response.setContentLength(i);}@Overridepublic void setContentLengthLong(long l) {response.setContentLengthLong(l);}@Overridepublic void setContentType(String s) {response.setContentType(s);}@Overridepublic void setBufferSize(int i) {response.setBufferSize(i);}@Overridepublic int getBufferSize() {return response.getBufferSize();}@Overridepublic void flushBuffer() throws IOException {response.flushBuffer();}@Overridepublic void resetBuffer() {response.resetBuffer();}@Overridepublic boolean isCommitted() {return response.isCommitted();}@Overridepublic void reset() {response.reset();}@Overridepublic void setLocale(Locale locale) {response.setLocale(locale);}@Overridepublic Locale getLocale() {return response.getLocale();}}
RequestFacade
同理
package connector;import javax.servlet.*;import java.io.BufferedReader;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Enumeration;import java.util.Locale;import java.util.Map;public class RequestFacade implements ServletRequest {private ServletRequest request = null;public RequestFacade(Request request) {this.request = request;}@Overridepublic Object getAttribute(String s) {return request.getAttribute(s);}@Overridepublic Enumeration<String> getAttributeNames() {return request.getAttributeNames();}@Overridepublic String getCharacterEncoding() {return request.getCharacterEncoding();}@Overridepublic void setCharacterEncoding(String s) throws UnsupportedEncodingException {request.setCharacterEncoding(s);}@Overridepublic int getContentLength() {return request.getContentLength();}@Overridepublic long getContentLengthLong() {return request.getContentLengthLong();}@Overridepublic String getContentType() {return request.getContentType();}@Overridepublic ServletInputStream getInputStream() throws IOException {return request.getInputStream();}@Overridepublic String getParameter(String s) {return request.getParameter(s);}@Overridepublic Enumeration<String> getParameterNames() {return request.getParameterNames();}@Overridepublic String[] getParameterValues(String s) {return request.getParameterValues(s);}@Overridepublic Map<String, String[]> getParameterMap() {return request.getParameterMap();}@Overridepublic String getProtocol() {return request.getProtocol();}@Overridepublic String getScheme() {return request.getScheme();}@Overridepublic String getServerName() {return request.getServerName();}@Overridepublic int getServerPort() {return request.getServerPort();}@Overridepublic BufferedReader getReader() throws IOException {return request.getReader();}@Overridepublic String getRemoteAddr() {return request.getRemoteAddr();}@Overridepublic String getRemoteHost() {return request.getRemoteHost();}@Overridepublic void setAttribute(String s, Object o) {request.setAttribute(s,o);}@Overridepublic void removeAttribute(String s) {request.removeAttribute(s);}@Overridepublic Locale getLocale() {return request.getLocale();}@Overridepublic Enumeration<Locale> getLocales() {return request.getLocales();}@Overridepublic boolean isSecure() {return request.isSecure();}@Overridepublic RequestDispatcher getRequestDispatcher(String s) {return request.getRequestDispatcher(s);}@Overridepublic String getRealPath(String s) {return request.getRealPath(s);}@Overridepublic int getRemotePort() {return request.getRemotePort();}@Overridepublic String getLocalName() {return request.getLocalName();}@Overridepublic String getLocalAddr() {return request.getLocalAddr();}@Overridepublic int getLocalPort() {return request.getLocalPort();}@Overridepublic ServletContext getServletContext() {return request.getServletContext();}@Overridepublic AsyncContext startAsync() throws IllegalStateException {return request.startAsync();}@Overridepublic AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {return request.startAsync();}@Overridepublic boolean isAsyncStarted() {return request.isAsyncStarted();}@Overridepublic boolean isAsyncSupported() {return request.isAsyncSupported();}@Overridepublic AsyncContext getAsyncContext() {return request.getAsyncContext();}@Overridepublic DispatcherType getDispatcherType() {return request.getDispatcherType();}}
应用
修改Processor代码
public void process(Request request, Response response) throws IOException {URLClassLoader loader = getServletLoader();try {Servlet servlet = getServlet(loader, request);RequestFacade requestFacade=new RequestFacade(request);ResponseFacade responseFacade=new ResponseFacade(response);servlet.service(requestFacade, responseFacade);} catch (MalformedURLException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}
四、动态资源支持的实现
首先修改Connector的代码:
public void run() {try {server=new ServerSocket(port);System.out.println("服务器已启动,监听端口【"+port+"】");while (true){Socket socket=server.accept();InputStream input=socket.getInputStream();OutputStream output=socket.getOutputStream();Request request=new Request(input);request.parse();Response response=new Response(output);response.setRequest(request);if (request.getRequestUri().startsWith("/servlet/")){ServletProcessor processor=new ServletProcessor();processor.process(request,response);}else {StaticProcessor processor = new StaticProcessor();processor.process(request, response);}shutDown(socket);}} catch (IOException e) {e.printStackTrace();}}
更改TestClient类
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 14:33*/public class TestClient {public static void main(String[] args) {try {Socket socket=new Socket("localhost",9999);OutputStream outputStream=socket.getOutputStream();outputStream.write("GET /servlet/TimeServlet HTTP/1.1".getBytes());socket.shutdownOutput();InputStream inputStream=socket.getInputStream();byte[] buffer=new byte[2048];int length=inputStream.read(buffer);StringBuilder response=new StringBuilder();for (int j=0;j<length;j++){char a=(char)buffer[j];response.append(a);}System.out.println(response.toString());socket.shutdownInput();socket.close();} catch (IOException i) {i.printStackTrace();}}}
结果显示如下:
五、使用NIO模型重写
package connector;import processor.ServletProcessor;import processor.StaticProcessor;import java.io.Closeable;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Set;public class NioConnector implements Runnable {private static final int DEFAULT_PORT = 9999;private ServerSocketChannel server;private Selector selector;private int port;private void shutDown(Closeable... closeables) {try {for (Closeable shut : closeables) {if (shut != null) {shut.close();}}} catch (IOException e) {e.printStackTrace();}}public NioConnector(int port) {this.port = port;}public NioConnector() {this(DEFAULT_PORT);}public void start() {try {server = ServerSocketChannel.open();server.configureBlocking(false);server.socket().bind(new InetSocketAddress(port));selector=Selector.open();server.register(selector, SelectionKey.OP_ACCEPT);System.out.println("启动服务器,监听端口["+port+"]");while (true){selector.select();Set<SelectionKey> selectionKeys=selector.selectedKeys();for (SelectionKey key:selectionKeys){handles(key);}selectionKeys.clear();}} catch (IOException e) {e.printStackTrace();}finally {shutDown(selector,server);}}private void handles(SelectionKey key) throws IOException {//ACCEPTif (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);}else if (key.isReadable()){SocketChannel client= (SocketChannel) key.channel();key.cancel();client.configureBlocking(true);Socket clientSocket=client.socket();InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream();Request request=new Request(inputStream);request.parse();Response response=new Response(outputStream);response.setRequest(request);if (request.getRequestUri().startsWith("/servlet/")){ServletProcessor processor=new ServletProcessor();processor.process(request,response);}else {StaticProcessor processor=new StaticProcessor();processor.process(request,response);}}}@Overridepublic void run() {new Thread(this).start();}}
六、测试重写

