간단한 servlet 프레임워크

6558 단어
본인은 servlet 프레임워크의 원리를 연구하고 싶었지만 연구하기가 유난히 어려웠습니다. 왜냐하면 모든 웹 프레임워크가 매우 복잡하기 때문입니다.무심결에 인터넷에서 간단한 것을 찾았는데 마치 단순 웹 프레임워크의 작가가 쓴 것 같아서 괜찮은 것 같아서 조금 고쳤는데 아주 간단한 servlet이 형성되었다.
그리고 servlet 프레임워크를 말하기 전에 현재의 주류 MVC 프레임워크는 대부분이 HttpServlet을 계승한 다음에 실현하는 서비스 방법이다.직접적으로 실현되는 것은 서비스 방법이기 때문에 어떤 DoGet이나doPost 방법이든 상관없다.
우리가 평소에 사용하는doGet이나doPost 방법은 모두 서비스를 통해 호출되기 때문에 서비스야말로Http서비스에서 가장 중요한 방법으로 전체 국면을 통제하는 것이다.
이외에 나도 일부 프레임워크가 Filter 인터페이스를 직접 실현하여 WEB 프레임워크를 실현하는 것을 보았다. 이렇게 해도 된다.HttpServlet에서 할 수 있는 모든 작업은 Filter의 실현에서 할 수 있고 Filter가 더욱 강력하기 때문이다.그러나 실현되기도 더욱 복잡하다.
다음에 연구한 이 간단한 servlet 프레임워크는 주로 두 가지 유형으로 구성되는데 하나는 가장 중요한 제어 전송 유형이다.
4
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		
		String uri = request.getRequestURI();
		//       .do   
		uri = uri.substring(request.getContextPath().length(), uri.length() - 3);
		
		@SuppressWarnings("unchecked")
		Map<String,Object> map = (Map<String,Object>) this.getServletContext().getAttribute("mapPath");
		
		if (map.containsKey(uri)) {
			//   http  uri      Action  
			Object obj = map.get(uri);
			//   http      
			String methodName = request.getParameter("method");
			//        null,     Action    index  
			if (methodName == null) {
				methodName = "index";
			}
			Method method = null;
			try {
				//               
				method = obj.getClass().getMethod(methodName,
						HttpServletRequest.class, HttpServletResponse.class);
			} catch (Exception e) {
				throw new RuntimeException(" " + obj.getClass().getName()
						+ "     " + methodName + "      !!!");
			}
			try {
				//   Controller      
				method.invoke(obj, request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
이 유형의 사고방식은 요청 경로를 통해 컨트롤러에 대응하는 실례를 찾은 다음에 실례 안의 구체적인 방법을 호출하는 것이다.이 실례의 생성은 탐지기에서 이루어진 것이다. 탐지기로 실례를 생성하면 좋은 점이 있다. 탐지기는 웹 서버를 시작할 때 자동으로 불러온다. 그러면 서버가 시작할 때 모든 컨트롤러를 실례화한 다음에 servlet Context에 존재한다.물론 공사가 너무 크거나 컨트롤러가 너무 많으면 이런 서비스는
기기의 메모리는 적지 않게 소모될 것이다. 그러나 간단한 servlet이라면 그렇게 많은 것을 고려할 필요가 없다.
4
public class LoadServletListener implements ServletContextListener{
	// Map  key    URI,value    URI    Action  (Action           )
	private static Map<String, Object> map = new HashMap<String, Object>();
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		if(map!=null)
			map=null;
	}
	@Override
	public void contextInitialized(ServletContextEvent event) {
		ServletContext context = event.getServletContext();
		String servletPackage = context.getInitParameter("servletPackage");
		String classPath = context.getRealPath(
				"/WEB-INF/classes/"+servletPackage.replace('.',File.separatorChar));
		scanClassPath(new File(classPath));
		context.setAttribute("mapPath", map);
		System.out.println(map);
	}
	
	/*
	 *           ,       Control  ,     value(URI)  Map   key,
	 *           Map   value
	 */
	private void scanClassPath(File file) {
		try {
			if (file.isFile()) {
				if (file.getName().endsWith(".class")) {
					String path = file.getPath();
					MyClassLoader myClassLoader = new MyClassLoader(
							this.getClass().getClassLoader());
					Class<?> clazz = myClassLoader.load(path);
					Controller controller = (Controller) clazz.getAnnotation(Controller.class);
					if (controller != null) {
						String uri = controller.value();
						Object action = clazz.newInstance();
						map.put(uri, action);
					}
				}
			} else {
				File[] files = file.listFiles();
				for (File child : files) {
					scanClassPath(child);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	//          ,                         
	class MyClassLoader extends ClassLoader {
		public MyClassLoader(ClassLoader parent) {
			super(parent);
		}
		public Class<?> load(String path) {
			FileInputStream fis = null;
			try {
				fis = new FileInputStream(path);
				byte[] buf = new byte[fis.available()];
				int len = 0;
				int total = 0;
				int fileLength = buf.length;
				while (total < fileLength) {
					len = fis.read(buf, total, fileLength - total);
					total = total + len;
				}
				return super.defineClass(null, buf, 0, fileLength);
			} catch (Exception e) {
				throw new RuntimeException(e);
			} finally {
				if (fis != null) {
					try {
						fis.close();
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
					fis = null;
				}
			}
		}
	}
}
이것이 바로 그 실례화 컨트롤러 클래스의 탐지기이다. 실례화 클래스는 이 과정이 좀 복잡하다. 먼저 경로를 통해 해당하는class 파일을 찾은 다음에ClassLoader를 통해 이class 파일의 내용을 불러와서Class 클래스를 만들고 이Class 클래스를 통해 해당되는 실례를 만들어야 한다.
그리고 controller 안의 URL을 이 실례와 대응해서 하나의 맵에 저장하고 이 맵을 servlet Context에 넣습니다.
이 Controller 메모는 매우 간단합니다.
4
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
	String value();
}
사실 개인적으로 이 Controller는 필요하지 않다고 생각한다. 더 좋은 것만 추가할 수 있다. 다음에 기본적인 웹을 살펴보자.xml 프로필 붙여넣기:
        <context-param>
		<param-name>servletPackage</param-name>
		<param-value>com.controller</param-value>
	</context-param>
	<listener>
		<listener-class>com.zit.LoadServletListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>DispatcherServlet</servlet-name>
		<servlet-class>com.zit.DispatcherServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>DispatcherServlet</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

servletPackage 이 매개 변수는 컨트롤러에 대응하는 패키지를 지정하는 것을 의미하며,listener가 파일을 스캔하는 효율이 더욱 높다는 것을 의미합니다.
이런 물건은 물건을 모두 장착한 후에 실제 컨트롤러를 쓸 수 있다.
4
@Controller("/hello")
public class HelloController {

	public void index(HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		request.getRequestDispatcher("/pages/hello.jsp")
				.forward(request, response);
	}
}
이런 간단한 servlet 프레임워크도 형성되었다. 적어도 직접 servlet을 쓰는 것보다 조금 간단하고 앞으로 다른 servlet 프레임워크를 연구하는 데 좋은 참조가 되었다.

좋은 웹페이지 즐겨찾기