• SpringMVC请求分发的简单实现


    简介

        以前用了下SpringMVC感觉挺不错了,前段事件也简单了写了一些代码来实现了SpringMVC简单的请求分发功能,实现的主要思想如下:
    • 将处理请求的类在系统启动的时候加载起来,相当于SpringMVC中的Controller
    • 读取Controller中的配置并对应其处理的URL
    • 通过调度Servlet进行拦截请求,并找到相应的Controller进行处理

    主要代码

    首先得标识出来哪些类是Controller类,这里我自己定义的是ServletHandler,通过Annotation的方式进行标识,并配置每个类和方法处理的URL:
    package com.meet58.base.servlet.annotation;
    
    public @interface ServletHandler {
    	
    }
    

    这里注解主要是声明这个类是一个ServletHandler类,用于处理请求的类,系统启动的时候就会加载这些类。

    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和相对应的ServletHandler进行匹配:
    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);
    	}
    }
    

    在这个类中扫描了每个ServletHandler类中的方法,并记录他们的要处理的URL,接下来就是通过容器实例化这些ServletHandler类了:
    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);
    	}
    }
    

    在ServletHandler类处理完成,并知道他们分别处理哪些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也是用到了,不过这个只是简单的兴趣研究并没有在实际项目中运用,可能会存在线程安全的问题。

  • 相关阅读:
    VB.Net常用数学函数整理
    VB6转换C#2010步骤及工具
    通用MD5加密演示
    一行代码解决VB.NET中Dialog窗体销毁不刷新主窗体
    JavaScript中defer 和onload的执行顺序
    XML读取事例程序
    C#正则表达式使用指引
    使用Response.ContentType 来控制下载文件的类型
    重载、重写的简易解释
    西历转和历函数
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3366214.html
Copyright © 2020-2023  润新知