SpringMVCリクエスト配信のシンプルな実装

11718 ワード

概要
以前SpringMVCを使ったのはとても悪くなくて、前段の事件も簡単にいくつかのコードを書いてSpringMVCの簡単な要求の配布機能を実現して、実現する主な思想は以下の通りです:
  • 処理要求のクラスをシステム起動時にロードします.SpringMVCのController
  • に相当します.
  • Controllerにおける構成を読み取る、その処理に対応するURL
  • .
  • サーブレットをスケジューリングすることによってブロック要求を行い、対応するControllerを見つけて処理
  • を行う.
    プライマリコード
    まず、どのクラスがControllerクラスであるかを識別しなければなりません.ここで私が定義したのはサーブレットHandlerで、Annotation方式で識別し、各クラスとメソッド処理のURLを構成します.
    package com.meet58.base.servlet.annotation;
    
    
    
    public @interface ServletHandler {
    
    	
    
    }
    
    

    ここで注記は、主にこのクラスがサーブレットHandlerクラスであることを宣言し、要求を処理するクラスであり、システムが起動するとこれらのクラスがロードされます.
    package com.meet58.base.servlet.annotation;
    
    
    
    import java.lang.annotation.Documented;
    
    import java.lang.annotation.ElementType;
    
    import java.lang.annotation.Retention;
    
    import java.lang.annotation.RetentionPolicy;
    
    import java.lang.annotation.Target;
    
    
    
    import com.meet58.base.servlet.types.RequestMethod;
    
    import com.meet58.base.servlet.types.ResponseType;
    
    
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    
    @Retention(RetentionPolicy.RUNTIME)
    
    @Documented
    
    public @interface HandlerMapping {
    
    	String value();	
    
    	
    
    }
    
    

    この注釈は、処理要求を構成する注釈であり、処理するパスを定義します.
    注記を定義すると、システムが起動したときにクラスをスキャンしてロードします.次に、スキャンするコードを示します.
    package com.meet58.base.servlet.mapping;
    
    
    
    import java.io.IOException;
    
    
    
    import org.springframework.core.io.Resource;
    
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import org.springframework.core.io.support.ResourcePatternResolver;
    
    import org.springframework.core.io.support.ResourcePatternUtils;
    
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    
    import org.springframework.core.type.classreading.MetadataReader;
    
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    
    import org.springframework.core.type.filter.TypeFilter;
    
    import org.springframework.util.ClassUtils;
    
    
    
    import com.meet58.base.servlet.annotation.ServletHandler;
    
    
    
    public class ServletHandlerMappingResolver {
    
    	
    
    	private static final String RESOURCE_PATTERN = "/**/*.class";
    
    	
    
    	private String[] packagesToScan;
    
    
    
    	private ResourcePatternResolver resourcePatternResolver; 
    
    	
    
    	
    
    	private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {
    
    		new AnnotationTypeFilter(ServletHandler.class, false)};
    
    
    
    	
    
    	public ServletHandlerMappingResolver(){
    
    		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(new PathMatchingResourcePatternResolver());
    
    	}
    
    	
    
    	public ServletHandlerMappingResolver scanPackages(String[] packagesToScan){
    
    		try {
    
    			for (String pkg : packagesToScan) {
    
    				String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    
    						ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
    
    				Resource[] resources;
    
    				
    
    					resources = this.resourcePatternResolver.getResources(pattern);
    
    				
    
    				MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
    
    				for (Resource resource : resources) {
    
    					if (resource.isReadable()) {
    
    						MetadataReader reader = readerFactory.getMetadataReader(resource);
    
    						String className = reader.getClassMetadata().getClassName();
    
    						if (matchesFilter(reader, readerFactory)) {
    
    							ServletHandlerMappingFactory.addClassMapping(this.resourcePatternResolver.getClassLoader().loadClass(className));
    
    						}
    
    					}
    
    				}
    
    			}
    
    		} catch (IOException e) {
    
    			// TODO Auto-generated catch block
    
    			e.printStackTrace();
    
    		} catch (ClassNotFoundException e) {
    
    			// TODO Auto-generated catch block
    
    			e.printStackTrace();
    
    		}
    
    		return this;
    
    	}
    
    	
    
    	public String[] getPackagesToScan() {
    
    		return packagesToScan;
    
    	}
    
    
    
    	public void setPackagesToScan(String[] packagesToScan) {
    
    		this.packagesToScan = packagesToScan;
    
    		this.scanPackages(packagesToScan);
    
    	}
    
    
    
    	private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
    
    		for (TypeFilter filter : ENTITY_TYPE_FILTERS) {
    
    			if (filter.match(reader, readerFactory)) {
    
    				return true;
    
    			}
    
    		}
    
    		return false;
    
    	}
    
    
    
    }
    
    

    このコードはSpringでHibernate永続化オブジェクトをどのようにスキャンするかのコードで、参考にしてみました.次に処理するURLと対応するサーブレットHandlerをマッチングします.
    package com.meet58.base.servlet.mapping;
    
    
    
    import java.lang.reflect.Method;
    
    import java.util.HashMap;
    
    import java.util.Map;
    
    
    
    import org.apache.log4j.Logger;
    
    
    
    import com.meet58.base.servlet.annotation.HandlerMapping;
    
    import com.meet58.base.servlet.context.ServletHandlerFactory;
    
    
    
    public class ServletHandlerMappingFactory {
    
    	
    
    	private static Logger logger = Logger.getLogger(ServletHandlerMappingFactory.class);
    
    
    
    	private static Map<String, Method> servletHandlerMapping = new HashMap<String, Method>();
    
    
    
    
    
    	public static void addClassMapping(Class<?> clazz) {
    
    		String url = null;
    
    		HandlerMapping handlerMapping = clazz.getAnnotation(HandlerMapping.class);
    
    		if (handlerMapping != null) {
    
    			url = handlerMapping.value();
    
    		} else {
    
    			String classSimpleName = clazz.getSimpleName().toLowerCase();
    
    			url = "/" + classSimpleName.substring(0,
    
    							classSimpleName.indexOf("servlet"));
    
    		}
    
    		if (url != null) {
    
    			if(url.endsWith("/")){
    
    				url = url.substring(url.length() - 1);
    
    			}
    
    			ServletHandlerFactory.put(clazz);
    
    			logger.info(" Load servlet handler class:" + clazz.getName() + " url:" + url);
    
    			scanHandlerMethod(clazz,url);
    
    		}
    
    	}
    
    
    
    	public static void scanHandlerMethod(Class<?> clazz,String classMapping) {
    
    		Method[] methods = clazz.getDeclaredMethods();
    
    		for (Method method : methods) {
    
    			HandlerMapping handlerMapping = method.getAnnotation(HandlerMapping.class);
    
    			if (handlerMapping != null && handlerMapping.value() != null) {
    
    				String mapping = handlerMapping.value();
    
    				if(!mapping.startsWith("/")){
    
    					mapping = "/" + mapping;
    
    				}
    
    				mapping = classMapping + mapping;
    
    				addMethodMapping( mapping,method);
    
    			}
    
    		}
    
    	}
    
    
    
    	public static void addMethodMapping(String url,Method method) {
    
    		logger.info(" Load servlet handler mapping, method:" + method.getName() + " for url:" + url);
    
    		Method handlerMethod = servletHandlerMapping.get(url);
    
    		if(handlerMethod != null){
    
    			throw new IllegalArgumentException(" url :" + url + " is already mapped by :" + handlerMethod);
    
    		}else{
    
    			servletHandlerMapping.put(url, method);
    
    		}
    
    	}
    
    
    
    	public static Method getMethodMapping(String url) {
    
    		return servletHandlerMapping.get(url);
    
    	}
    
    }
    
    

    このクラスでは、各サーブレットHandlerクラスのメソッドをスキャンし、処理するURLを記録します.次に、これらのサーブレットHandlerクラスをコンテナでインスタンス化します.
    package com.meet58.base.servlet.context;
    
    
    
    import java.util.HashMap;
    
    import java.util.Map;
    
    
    
    import org.apache.log4j.Logger;
    
    
    
    public class ServletHandlerFactory {
    
    	
    
    	private static Logger logger = Logger.getLogger(ServletHandlerFactory.class);
    
    
    
    	private static Map<String,Object> classes = new HashMap<String,Object>();
    
    	
    
    	public static void put(Class<?> clazz){
    
    		try {
    
    			logger.info("   ServletHandler :"+ clazz.getName());
    
    			Object servlet = clazz.newInstance();
    
    			classes.put(clazz.getName(), servlet);
    
    		} catch (InstantiationException e) {
    
    			logger.error("   Servlet :" + clazz.getName() + "  :" + e.getMessage());
    
    		} catch (IllegalAccessException e) {
    
    			logger.error("   Servlet :" + clazz.getName() + "  :" + e.getMessage());
    
    		}
    
    	}
    
    	
    
    	@SuppressWarnings("unchecked")
    
    	public static <T> T get(String className){
    
    		return (T)classes.get(className);
    
    	}
    
    }
    
    

    サーブレットHandlerクラスの処理が完了し、それぞれどのURLを処理しているかを知ると、対応するURLの要求の配布をスケジューラで行うことができます.
    package com.meet58.base.servlet;
    
    
    
    import java.io.IOException;
    
    import java.lang.reflect.InvocationTargetException;
    
    import java.lang.reflect.Method;
    
    import java.util.ArrayList;
    
    import java.util.List;
    
    
    
    import javax.servlet.ServletException;
    
    import javax.servlet.annotation.WebServlet;
    
    import javax.servlet.http.HttpServlet;
    
    import javax.servlet.http.HttpServletRequest;
    
    import javax.servlet.http.HttpServletResponse;
    
    
    
    import org.apache.log4j.Logger;
    
    
    
    import com.meet58.base.context.WebHttpRequestContext;
    
    import com.meet58.base.servlet.context.ServletHandlerFactory;
    
    import com.meet58.base.servlet.mapping.ServletHandlerMappingFactory;
    
    import com.meet58.util.WebUtils;
    
    
    
    @WebServlet(urlPatterns = { "*.do" })
    
    public class WebHttpDispatchServlet extends HttpServlet {
    
    
    
    	private static final long serialVersionUID = 1L;
    
    
    
    	private Logger logger = Logger.getLogger(this.getClass());
    
    
    
    	private List<String> excludeUrls = new ArrayList<String>();
    
    
    
    	@Override
    
    	public void init() throws ServletException {
    
    		//   websocket  
    
    		excludeUrls.add("/meet.do");
    
    		super.init();
    
    	}
    
    
    
    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    
    			throws ServletException, IOException {
    
    		this.doPost(request, response);
    
    	}
    
    
    
    	public void doPost(HttpServletRequest request, HttpServletResponse response)
    
    			throws ServletException, IOException {
    
    		try {
    
    			String url = request.getRequestURI().replace(
    
    					request.getContextPath(), "");
    
    			if (excludeUrls.contains(url)) {
    
    				return;
    
    			}
    
    			Method handlerMethod = ServletHandlerMappingFactory.getMethodMapping(url);
    
    			if (handlerMethod == null) {
    
    				response.sendError(404, "No handler found for " + url);
    
    				logger.error("No handler found for " + url);
    
    				return;
    
    			}
    
    
    
    			Object servlet = ServletHandlerFactory.get(handlerMethod
    
    					.getDeclaringClass().getName());
    
    
    
    			if (servlet == null) {
    
    				response.sendError(404, "No handler class found for " + url);
    
    				logger.error("No handler class found for " + url);
    
    				return;
    
    			}
    
    
    
    			Object result = invokeHandlerMethod(servlet, handlerMethod);
    
    			handleInvokeResult(result);
    
    
    
    			// this.doService();
    
    		} catch (Throwable e) {
    
    			handlerException(e);
    
    		}
    
    	}
    
    
    
    	public void handleInvokeResult(Object result) {
    
    		String location = "";
    
    		if (result instanceof String) {
    
    			if (((String) result).startsWith("redirect:")) {
    
    				location = ((String) result).substring("redirect:".length(),
    
    						((String) result).length());
    
    				WebUtils.redirect(location);
    
    			} else if (((String) result).startsWith("forward:")) {
    
    				location = ((String) result).substring("forward:".length(),
    
    						((String) result).length());
    
    				WebUtils.forward(location);
    
    			}
    
    		}
    
    	}
    
    
    
    	public Object invokeHandlerMethod(Object object, Method method)
    
    			throws Throwable {
    
    		Object result = null;
    
    		if (method != null) {
    
    			try {
    
    				result = method.invoke(object);
    
    			} catch (InvocationTargetException e) {
    
    				throw e.getTargetException();
    
    			}
    
    		}
    
    		return result;
    
    	}
    
    
    
    	public void handlerException(Throwable e) {
    
    		String message = e.getMessage() != null ? e.getMessage() : e.toString();
    
    		e.printStackTrace();
    
    		if (WebHttpRequestContext.isAsyncRequest()) {
    
    			WebUtils.writeFailure(message);
    
    		} else {
    
    			try {
    
    				WebHttpRequestContext.getResponse().sendError(500, message);
    
    			} catch (IOException e1) {
    
    				e1.printStackTrace();
    
    			}
    
    		}
    
    	}
    
    
    
    	public String getMappingClass(String url) {
    
    		return null;
    
    	}
    
    
    
    }
    
    

    このコードではURLで対応する処理方法を見つけて処理し,異常をキャプチャする.
    この方法はStrutsも用いられているが,これは単純な興味研究で実際のプロジェクトでは用いられておらず,スレッドセキュリティの問題がある可能性がある.