一、动态资源的获取

因为TimeServlet无法直接调用,所以获得相应的路径。

  1. package processor;
  2. import connector.ConnectorUtils;
  3. import connector.Request;
  4. import connector.Response;
  5. import javax.servlet.Servlet;
  6. import javax.servlet.ServletException;
  7. import java.io.File;
  8. import java.io.IOException;
  9. import java.lang.reflect.InvocationTargetException;
  10. import java.net.MalformedURLException;
  11. import java.net.URL;
  12. import java.net.URLClassLoader;
  13. public class ServletProcessor {
  14. //准备好URLClassLoader
  15. URLClassLoader getServletLoader() throws MalformedURLException {
  16. File webRoot = new File(ConnectorUtils.WEB_ROOT);
  17. URL webRootUrl = webRoot.toURI().toURL();
  18. return new URLClassLoader(new URL[]{webRootUrl});
  19. }
  20. Servlet getServlet(URLClassLoader loader, Request request) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  21. /*
  22. /servlet/TimeServlet
  23. */
  24. String uri = request.getRequestUri();
  25. String servletName = uri.substring(uri.lastIndexOf("/") + 1);
  26. Class servletClass = loader.loadClass(servletName);
  27. Servlet servlet = (Servlet) servletClass.getDeclaredConstructor().newInstance();
  28. return servlet;
  29. }
  30. public void process(Request request, Response response) throws IOException {
  31. try {
  32. URLClassLoader loader = getServletLoader();
  33. Servlet servlet = getServlet(loader, request);
  34. servlet.service(request, response);
  35. } catch (MalformedURLException e) {
  36. e.printStackTrace();
  37. } catch (InstantiationException e) {
  38. e.printStackTrace();
  39. } catch (InvocationTargetException e) {
  40. e.printStackTrace();
  41. } catch (NoSuchMethodException e) {
  42. e.printStackTrace();
  43. } catch (IllegalAccessException e) {
  44. e.printStackTrace();
  45. } catch (ClassNotFoundException e) {
  46. e.printStackTrace();
  47. } catch (ServletException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }

二、测试ServletProcessor

  1. package processor;
  2. import connector.Request;
  3. import org.junit.jupiter.api.Assertions;
  4. import org.junit.jupiter.api.Test;
  5. import test.util.TestUtils;
  6. import javax.servlet.Servlet;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.net.MalformedURLException;
  9. import java.net.URLClassLoader;
  10. /**
  11. * @ClassName:
  12. * @Description:
  13. * @author: hszjj
  14. * @date: 2019/11/26 15:32
  15. */
  16. public class ServletProcessorTest {
  17. private static final String SERVLET_REQUEST="GET /servlet/TimeServlet HTTP/1.1";
  18. @Test
  19. public void givenServletRequest_thenLoadServlet() throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
  20. Request request= TestUtils.createRequest(SERVLET_REQUEST);
  21. ServletProcessor processor=new ServletProcessor();
  22. URLClassLoader loader=processor.getServletLoader();
  23. Servlet servlet=processor.getServlet(loader,request);
  24. Assertions.assertEquals("webroot.TimeServlet",servlet.getClass().getName());
  25. }
  26. }

三、使用facade模式

示例中这个代码明显出现问题,既涉及到服务器的开发也涉及到Servlet的开发,现实中这两个开发应属于不同群体。所以我们要保证服务器的开发不会被Servlet的人员过分利用,例如如下情况:
image.png
这种情况很危险,Servlet的人员可以肆意调用函数。
为了防止这种问题发生,我们需要应用Facade模式。
首先需要写如下两个函数

ResponseFacade

把ServletResponse放置在ResponseFacade中就可以有效防止这种非法转型。

  1. package connector;
  2. import javax.servlet.ServletOutputStream;
  3. import javax.servlet.ServletResponse;
  4. import java.io.IOException;
  5. import java.io.PrintWriter;
  6. import java.util.Locale;
  7. public class ResponseFacade implements ServletResponse {
  8. private ServletResponse response = null;
  9. public ResponseFacade(Response response) {
  10. this.response = response;
  11. }
  12. @Override
  13. public String getCharacterEncoding() {
  14. return response.getCharacterEncoding();
  15. }
  16. @Override
  17. public String getContentType() {
  18. return response.getContentType();
  19. }
  20. @Override
  21. public ServletOutputStream getOutputStream() throws IOException {
  22. return response.getOutputStream();
  23. }
  24. @Override
  25. public PrintWriter getWriter() throws IOException {
  26. return response.getWriter();
  27. }
  28. @Override
  29. public void setCharacterEncoding(String s) {
  30. response.setCharacterEncoding(s);
  31. }
  32. @Override
  33. public void setContentLength(int i) {
  34. response.setContentLength(i);
  35. }
  36. @Override
  37. public void setContentLengthLong(long l) {
  38. response.setContentLengthLong(l);
  39. }
  40. @Override
  41. public void setContentType(String s) {
  42. response.setContentType(s);
  43. }
  44. @Override
  45. public void setBufferSize(int i) {
  46. response.setBufferSize(i);
  47. }
  48. @Override
  49. public int getBufferSize() {
  50. return response.getBufferSize();
  51. }
  52. @Override
  53. public void flushBuffer() throws IOException {
  54. response.flushBuffer();
  55. }
  56. @Override
  57. public void resetBuffer() {
  58. response.resetBuffer();
  59. }
  60. @Override
  61. public boolean isCommitted() {
  62. return response.isCommitted();
  63. }
  64. @Override
  65. public void reset() {
  66. response.reset();
  67. }
  68. @Override
  69. public void setLocale(Locale locale) {
  70. response.setLocale(locale);
  71. }
  72. @Override
  73. public Locale getLocale() {
  74. return response.getLocale();
  75. }
  76. }

RequestFacade

同理

  1. package connector;
  2. import javax.servlet.*;
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.UnsupportedEncodingException;
  6. import java.util.Enumeration;
  7. import java.util.Locale;
  8. import java.util.Map;
  9. public class RequestFacade implements ServletRequest {
  10. private ServletRequest request = null;
  11. public RequestFacade(Request request) {
  12. this.request = request;
  13. }
  14. @Override
  15. public Object getAttribute(String s) {
  16. return request.getAttribute(s);
  17. }
  18. @Override
  19. public Enumeration<String> getAttributeNames() {
  20. return request.getAttributeNames();
  21. }
  22. @Override
  23. public String getCharacterEncoding() {
  24. return request.getCharacterEncoding();
  25. }
  26. @Override
  27. public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
  28. request.setCharacterEncoding(s);
  29. }
  30. @Override
  31. public int getContentLength() {
  32. return request.getContentLength();
  33. }
  34. @Override
  35. public long getContentLengthLong() {
  36. return request.getContentLengthLong();
  37. }
  38. @Override
  39. public String getContentType() {
  40. return request.getContentType();
  41. }
  42. @Override
  43. public ServletInputStream getInputStream() throws IOException {
  44. return request.getInputStream();
  45. }
  46. @Override
  47. public String getParameter(String s) {
  48. return request.getParameter(s);
  49. }
  50. @Override
  51. public Enumeration<String> getParameterNames() {
  52. return request.getParameterNames();
  53. }
  54. @Override
  55. public String[] getParameterValues(String s) {
  56. return request.getParameterValues(s);
  57. }
  58. @Override
  59. public Map<String, String[]> getParameterMap() {
  60. return request.getParameterMap();
  61. }
  62. @Override
  63. public String getProtocol() {
  64. return request.getProtocol();
  65. }
  66. @Override
  67. public String getScheme() {
  68. return request.getScheme();
  69. }
  70. @Override
  71. public String getServerName() {
  72. return request.getServerName();
  73. }
  74. @Override
  75. public int getServerPort() {
  76. return request.getServerPort();
  77. }
  78. @Override
  79. public BufferedReader getReader() throws IOException {
  80. return request.getReader();
  81. }
  82. @Override
  83. public String getRemoteAddr() {
  84. return request.getRemoteAddr();
  85. }
  86. @Override
  87. public String getRemoteHost() {
  88. return request.getRemoteHost();
  89. }
  90. @Override
  91. public void setAttribute(String s, Object o) {
  92. request.setAttribute(s,o);
  93. }
  94. @Override
  95. public void removeAttribute(String s) {
  96. request.removeAttribute(s);
  97. }
  98. @Override
  99. public Locale getLocale() {
  100. return request.getLocale();
  101. }
  102. @Override
  103. public Enumeration<Locale> getLocales() {
  104. return request.getLocales();
  105. }
  106. @Override
  107. public boolean isSecure() {
  108. return request.isSecure();
  109. }
  110. @Override
  111. public RequestDispatcher getRequestDispatcher(String s) {
  112. return request.getRequestDispatcher(s);
  113. }
  114. @Override
  115. public String getRealPath(String s) {
  116. return request.getRealPath(s);
  117. }
  118. @Override
  119. public int getRemotePort() {
  120. return request.getRemotePort();
  121. }
  122. @Override
  123. public String getLocalName() {
  124. return request.getLocalName();
  125. }
  126. @Override
  127. public String getLocalAddr() {
  128. return request.getLocalAddr();
  129. }
  130. @Override
  131. public int getLocalPort() {
  132. return request.getLocalPort();
  133. }
  134. @Override
  135. public ServletContext getServletContext() {
  136. return request.getServletContext();
  137. }
  138. @Override
  139. public AsyncContext startAsync() throws IllegalStateException {
  140. return request.startAsync();
  141. }
  142. @Override
  143. public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
  144. return request.startAsync();
  145. }
  146. @Override
  147. public boolean isAsyncStarted() {
  148. return request.isAsyncStarted();
  149. }
  150. @Override
  151. public boolean isAsyncSupported() {
  152. return request.isAsyncSupported();
  153. }
  154. @Override
  155. public AsyncContext getAsyncContext() {
  156. return request.getAsyncContext();
  157. }
  158. @Override
  159. public DispatcherType getDispatcherType() {
  160. return request.getDispatcherType();
  161. }
  162. }

应用

修改Processor代码

  1. public void process(Request request, Response response) throws IOException {
  2. URLClassLoader loader = getServletLoader();
  3. try {
  4. Servlet servlet = getServlet(loader, request);
  5. RequestFacade requestFacade=new RequestFacade(request);
  6. ResponseFacade responseFacade=new ResponseFacade(response);
  7. servlet.service(requestFacade, responseFacade);
  8. } catch (MalformedURLException e) {
  9. e.printStackTrace();
  10. } catch (InstantiationException e) {
  11. e.printStackTrace();
  12. } catch (InvocationTargetException e) {
  13. e.printStackTrace();
  14. } catch (NoSuchMethodException e) {
  15. e.printStackTrace();
  16. } catch (IllegalAccessException e) {
  17. e.printStackTrace();
  18. } catch (ClassNotFoundException e) {
  19. e.printStackTrace();
  20. } catch (ServletException e) {
  21. e.printStackTrace();
  22. }
  23. }

四、动态资源支持的实现

首先修改Connector的代码:

  1. public void run() {
  2. try {
  3. server=new ServerSocket(port);
  4. System.out.println("服务器已启动,监听端口【"+port+"】");
  5. while (true){
  6. Socket socket=server.accept();
  7. InputStream input=socket.getInputStream();
  8. OutputStream output=socket.getOutputStream();
  9. Request request=new Request(input);
  10. request.parse();
  11. Response response=new Response(output);
  12. response.setRequest(request);
  13. if (request.getRequestUri().startsWith("/servlet/")){
  14. ServletProcessor processor=new ServletProcessor();
  15. processor.process(request,response);
  16. }else {
  17. StaticProcessor processor = new StaticProcessor();
  18. processor.process(request, response);
  19. }
  20. shutDown(socket);
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }

更改TestClient类

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.net.Socket;
  5. /**
  6. * @ClassName:
  7. * @Description:
  8. * @author: hszjj
  9. * @date: 2019/11/26 14:33
  10. */
  11. public class TestClient {
  12. public static void main(String[] args) {
  13. try {
  14. Socket socket=new Socket("localhost",9999);
  15. OutputStream outputStream=socket.getOutputStream();
  16. outputStream.write("GET /servlet/TimeServlet HTTP/1.1".getBytes());
  17. socket.shutdownOutput();
  18. InputStream inputStream=socket.getInputStream();
  19. byte[] buffer=new byte[2048];
  20. int length=inputStream.read(buffer);
  21. StringBuilder response=new StringBuilder();
  22. for (int j=0;j<length;j++){
  23. char a=(char)buffer[j];
  24. response.append(a);
  25. }
  26. System.out.println(response.toString());
  27. socket.shutdownInput();
  28. socket.close();
  29. } catch (IOException i) {
  30. i.printStackTrace();
  31. }
  32. }
  33. }

结果显示如下:

image.png

五、使用NIO模型重写

  1. package connector;
  2. import processor.ServletProcessor;
  3. import processor.StaticProcessor;
  4. import java.io.Closeable;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.OutputStream;
  8. import java.net.InetSocketAddress;
  9. import java.net.ServerSocket;
  10. import java.net.Socket;
  11. import java.nio.channels.SelectionKey;
  12. import java.nio.channels.Selector;
  13. import java.nio.channels.ServerSocketChannel;
  14. import java.nio.channels.SocketChannel;
  15. import java.util.Set;
  16. public class NioConnector implements Runnable {
  17. private static final int DEFAULT_PORT = 9999;
  18. private ServerSocketChannel server;
  19. private Selector selector;
  20. private int port;
  21. private void shutDown(Closeable... closeables) {
  22. try {
  23. for (Closeable shut : closeables) {
  24. if (shut != null) {
  25. shut.close();
  26. }
  27. }
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. public NioConnector(int port) {
  33. this.port = port;
  34. }
  35. public NioConnector() {
  36. this(DEFAULT_PORT);
  37. }
  38. public void start() {
  39. try {
  40. server = ServerSocketChannel.open();
  41. server.configureBlocking(false);
  42. server.socket().bind(new InetSocketAddress(port));
  43. selector=Selector.open();
  44. server.register(selector, SelectionKey.OP_ACCEPT);
  45. System.out.println("启动服务器,监听端口["+port+"]");
  46. while (true){
  47. selector.select();
  48. Set<SelectionKey> selectionKeys=selector.selectedKeys();
  49. for (SelectionKey key:selectionKeys){
  50. handles(key);
  51. }
  52. selectionKeys.clear();
  53. }
  54. } catch (IOException e) {
  55. e.printStackTrace();
  56. }finally {
  57. shutDown(selector,server);
  58. }
  59. }
  60. private void handles(SelectionKey key) throws IOException {
  61. //ACCEPT
  62. if (key.isAcceptable()) {
  63. ServerSocketChannel server = (ServerSocketChannel) key.channel();
  64. SocketChannel client = server.accept();
  65. client.configureBlocking(false);
  66. client.register(selector, SelectionKey.OP_READ);
  67. }else if (key.isReadable()){
  68. SocketChannel client= (SocketChannel) key.channel();
  69. key.cancel();
  70. client.configureBlocking(true);
  71. Socket clientSocket=client.socket();
  72. InputStream inputStream=clientSocket.getInputStream();
  73. OutputStream outputStream=clientSocket.getOutputStream();
  74. Request request=new Request(inputStream);
  75. request.parse();
  76. Response response=new Response(outputStream);
  77. response.setRequest(request);
  78. if (request.getRequestUri().startsWith("/servlet/")){
  79. ServletProcessor processor=new ServletProcessor();
  80. processor.process(request,response);
  81. }else {
  82. StaticProcessor processor=new StaticProcessor();
  83. processor.process(request,response);
  84. }
  85. }
  86. }
  87. @Override
  88. public void run() {
  89. new Thread(this).start();
  90. }
  91. }

六、测试重写

image.png