SpringMVC请求分发的简单实现

来源:互联网 发布:2016年网络购物规模 编辑:程序博客网 时间:2024/05/14 09:32

简介


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

主要代码


首先得标识出来哪些类是Controller类,这里我自己定义的是ServletHandler,通过Annotation的方式进行标识,并配置每个类和方法处理的URL:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet.annotation;  
  2.   
  3. public @interface ServletHandler {  
  4.       
  5. }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet.annotation;  
  2.   
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.ElementType;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. import java.lang.annotation.Target;  
  8.   
  9. import com.meet58.base.servlet.types.RequestMethod;  
  10. import com.meet58.base.servlet.types.ResponseType;  
  11.   
  12. @Target({ElementType.TYPE, ElementType.METHOD})  
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. @Documented  
  15. public @interface HandlerMapping {  
  16.     String value();   
  17.       
  18. }  

这个注解是配置处理请求的注解,定义了要处理的路径。

定义了注解之后就是要在系统启动的时候扫描并加载这些类,下面是如何进行扫描的代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet.mapping;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.springframework.core.io.Resource;  
  6. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;  
  7. import org.springframework.core.io.support.ResourcePatternResolver;  
  8. import org.springframework.core.io.support.ResourcePatternUtils;  
  9. import org.springframework.core.type.classreading.CachingMetadataReaderFactory;  
  10. import org.springframework.core.type.classreading.MetadataReader;  
  11. import org.springframework.core.type.classreading.MetadataReaderFactory;  
  12. import org.springframework.core.type.filter.AnnotationTypeFilter;  
  13. import org.springframework.core.type.filter.TypeFilter;  
  14. import org.springframework.util.ClassUtils;  
  15.   
  16. import com.meet58.base.servlet.annotation.ServletHandler;  
  17.   
  18. public class ServletHandlerMappingResolver {  
  19.       
  20.     private static final String RESOURCE_PATTERN = "/**/*.class";  
  21.       
  22.     private String[] packagesToScan;  
  23.   
  24.     private ResourcePatternResolver resourcePatternResolver;   
  25.       
  26.       
  27.     private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {  
  28.         new AnnotationTypeFilter(ServletHandler.classfalse)};  
  29.   
  30.       
  31.     public ServletHandlerMappingResolver(){  
  32.         this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(new PathMatchingResourcePatternResolver());  
  33.     }  
  34.       
  35.     public ServletHandlerMappingResolver scanPackages(String[] packagesToScan){  
  36.         try {  
  37.             for (String pkg : packagesToScan) {  
  38.                 String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
  39.                         ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;  
  40.                 Resource[] resources;  
  41.                   
  42.                     resources = this.resourcePatternResolver.getResources(pattern);  
  43.                   
  44.                 MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);  
  45.                 for (Resource resource : resources) {  
  46.                     if (resource.isReadable()) {  
  47.                         MetadataReader reader = readerFactory.getMetadataReader(resource);  
  48.                         String className = reader.getClassMetadata().getClassName();  
  49.                         if (matchesFilter(reader, readerFactory)) {  
  50.                             ServletHandlerMappingFactory.addClassMapping(this.resourcePatternResolver.getClassLoader().loadClass(className));  
  51.                         }  
  52.                     }  
  53.                 }  
  54.             }  
  55.         } catch (IOException e) {  
  56.             // TODO Auto-generated catch block  
  57.             e.printStackTrace();  
  58.         } catch (ClassNotFoundException e) {  
  59.             // TODO Auto-generated catch block  
  60.             e.printStackTrace();  
  61.         }  
  62.         return this;  
  63.     }  
  64.       
  65.     public String[] getPackagesToScan() {  
  66.         return packagesToScan;  
  67.     }  
  68.   
  69.     public void setPackagesToScan(String[] packagesToScan) {  
  70.         this.packagesToScan = packagesToScan;  
  71.         this.scanPackages(packagesToScan);  
  72.     }  
  73.   
  74.     private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {  
  75.         for (TypeFilter filter : ENTITY_TYPE_FILTERS) {  
  76.             if (filter.match(reader, readerFactory)) {  
  77.                 return true;  
  78.             }  
  79.         }  
  80.         return false;  
  81.     }  
  82.   
  83. }  

这段代码是Spring中如何扫描Hibernate持久化对象的代码,拿过来借鉴了一下,下面要处理的就是把要处理的URL和相对应的ServletHandler进行匹配:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet.mapping;  
  2.   
  3. import java.lang.reflect.Method;  
  4. import java.util.HashMap;  
  5. import java.util.Map;  
  6.   
  7. import org.apache.log4j.Logger;  
  8.   
  9. import com.meet58.base.servlet.annotation.HandlerMapping;  
  10. import com.meet58.base.servlet.context.ServletHandlerFactory;  
  11.   
  12. public class ServletHandlerMappingFactory {  
  13.       
  14.     private static Logger logger = Logger.getLogger(ServletHandlerMappingFactory.class);  
  15.   
  16.     private static Map<String, Method> servletHandlerMapping = new HashMap<String, Method>();  
  17.   
  18.   
  19.     public static void addClassMapping(Class<?> clazz) {  
  20.         String url = null;  
  21.         HandlerMapping handlerMapping = clazz.getAnnotation(HandlerMapping.class);  
  22.         if (handlerMapping != null) {  
  23.             url = handlerMapping.value();  
  24.         } else {  
  25.             String classSimpleName = clazz.getSimpleName().toLowerCase();  
  26.             url = "/" + classSimpleName.substring(0,  
  27.                             classSimpleName.indexOf("servlet"));  
  28.         }  
  29.         if (url != null) {  
  30.             if(url.endsWith("/")){  
  31.                 url = url.substring(url.length() - 1);  
  32.             }  
  33.             ServletHandlerFactory.put(clazz);  
  34.             logger.info(" Load servlet handler class:" + clazz.getName() + " url:" + url);  
  35.             scanHandlerMethod(clazz,url);  
  36.         }  
  37.     }  
  38.   
  39.     public static void scanHandlerMethod(Class<?> clazz,String classMapping) {  
  40.         Method[] methods = clazz.getDeclaredMethods();  
  41.         for (Method method : methods) {  
  42.             HandlerMapping handlerMapping = method.getAnnotation(HandlerMapping.class);  
  43.             if (handlerMapping != null && handlerMapping.value() != null) {  
  44.                 String mapping = handlerMapping.value();  
  45.                 if(!mapping.startsWith("/")){  
  46.                     mapping = "/" + mapping;  
  47.                 }  
  48.                 mapping = classMapping + mapping;  
  49.                 addMethodMapping( mapping,method);  
  50.             }  
  51.         }  
  52.     }  
  53.   
  54.     public static void addMethodMapping(String url,Method method) {  
  55.         logger.info(" Load servlet handler mapping, method:" + method.getName() + " for url:" + url);  
  56.         Method handlerMethod = servletHandlerMapping.get(url);  
  57.         if(handlerMethod != null){  
  58.             throw new IllegalArgumentException(" url :" + url + " is already mapped by :" + handlerMethod);  
  59.         }else{  
  60.             servletHandlerMapping.put(url, method);  
  61.         }  
  62.     }  
  63.   
  64.     public static Method getMethodMapping(String url) {  
  65.         return servletHandlerMapping.get(url);  
  66.     }  
  67. }  

在这个类中扫描了每个ServletHandler类中的方法,并记录他们的要处理的URL,接下来就是通过容器实例化这些ServletHandler类了:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet.context;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. import org.apache.log4j.Logger;  
  7.   
  8. public class ServletHandlerFactory {  
  9.       
  10.     private static Logger logger = Logger.getLogger(ServletHandlerFactory.class);  
  11.   
  12.     private static Map<String,Object> classes = new HashMap<String,Object>();  
  13.       
  14.     public static void put(Class<?> clazz){  
  15.         try {  
  16.             logger.info("初始化ServletHandler类:"+ clazz.getName());  
  17.             Object servlet = clazz.newInstance();  
  18.             classes.put(clazz.getName(), servlet);  
  19.         } catch (InstantiationException e) {  
  20.             logger.error("初始化Servlet类:" + clazz.getName() + "失败:" + e.getMessage());  
  21.         } catch (IllegalAccessException e) {  
  22.             logger.error("初始化Servlet类:" + clazz.getName() + "失败:" + e.getMessage());  
  23.         }  
  24.     }  
  25.       
  26.     @SuppressWarnings("unchecked")  
  27.     public static <T> T get(String className){  
  28.         return (T)classes.get(className);  
  29.     }  
  30. }  

在ServletHandler类处理完成,并知道他们分别处理哪些URL之后,就可以通过一个调度器进行对对应的URL进行请求的分发了:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.meet58.base.servlet;  
  2.   
  3. import java.io.IOException;  
  4. import java.lang.reflect.InvocationTargetException;  
  5. import java.lang.reflect.Method;  
  6. import java.util.ArrayList;  
  7. import java.util.List;  
  8.   
  9. import javax.servlet.ServletException;  
  10. import javax.servlet.annotation.WebServlet;  
  11. import javax.servlet.http.HttpServlet;  
  12. import javax.servlet.http.HttpServletRequest;  
  13. import javax.servlet.http.HttpServletResponse;  
  14.   
  15. import org.apache.log4j.Logger;  
  16.   
  17. import com.meet58.base.context.WebHttpRequestContext;  
  18. import com.meet58.base.servlet.context.ServletHandlerFactory;  
  19. import com.meet58.base.servlet.mapping.ServletHandlerMappingFactory;  
  20. import com.meet58.util.WebUtils;  
  21.   
  22. @WebServlet(urlPatterns = { "*.do" })  
  23. public class WebHttpDispatchServlet extends HttpServlet {  
  24.   
  25.     private static final long serialVersionUID = 1L;  
  26.   
  27.     private Logger logger = Logger.getLogger(this.getClass());  
  28.   
  29.     private List<String> excludeUrls = new ArrayList<String>();  
  30.   
  31.     @Override  
  32.     public void init() throws ServletException {  
  33.         // 屏蔽websocket地址  
  34.         excludeUrls.add("/meet.do");  
  35.         super.init();  
  36.     }  
  37.   
  38.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  39.             throws ServletException, IOException {  
  40.         this.doPost(request, response);  
  41.     }  
  42.   
  43.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  44.             throws ServletException, IOException {  
  45.         try {  
  46.             String url = request.getRequestURI().replace(  
  47.                     request.getContextPath(), "");  
  48.             if (excludeUrls.contains(url)) {  
  49.                 return;  
  50.             }  
  51.             Method handlerMethod = ServletHandlerMappingFactory.getMethodMapping(url);  
  52.             if (handlerMethod == null) {  
  53.                 response.sendError(404"No handler found for " + url);  
  54.                 logger.error("No handler found for " + url);  
  55.                 return;  
  56.             }  
  57.   
  58.             Object servlet = ServletHandlerFactory.get(handlerMethod  
  59.                     .getDeclaringClass().getName());  
  60.   
  61.             if (servlet == null) {  
  62.                 response.sendError(404"No handler class found for " + url);  
  63.                 logger.error("No handler class found for " + url);  
  64.                 return;  
  65.             }  
  66.   
  67.             Object result = invokeHandlerMethod(servlet, handlerMethod);  
  68.             handleInvokeResult(result);  
  69.   
  70.             // this.doService();  
  71.         } catch (Throwable e) {  
  72.             handlerException(e);  
  73.         }  
  74.     }  
  75.   
  76.     public void handleInvokeResult(Object result) {  
  77.         String location = "";  
  78.         if (result instanceof String) {  
  79.             if (((String) result).startsWith("redirect:")) {  
  80.                 location = ((String) result).substring("redirect:".length(),  
  81.                         ((String) result).length());  
  82.                 WebUtils.redirect(location);  
  83.             } else if (((String) result).startsWith("forward:")) {  
  84.                 location = ((String) result).substring("forward:".length(),  
  85.                         ((String) result).length());  
  86.                 WebUtils.forward(location);  
  87.             }  
  88.         }  
  89.     }  
  90.   
  91.     public Object invokeHandlerMethod(Object object, Method method)  
  92.             throws Throwable {  
  93.         Object result = null;  
  94.         if (method != null) {  
  95.             try {  
  96.                 result = method.invoke(object);  
  97.             } catch (InvocationTargetException e) {  
  98.                 throw e.getTargetException();  
  99.             }  
  100.         }  
  101.         return result;  
  102.     }  
  103.   
  104.     public void handlerException(Throwable e) {  
  105.         String message = e.getMessage() != null ? e.getMessage() : e.toString();  
  106.         e.printStackTrace();  
  107.         if (WebHttpRequestContext.isAsyncRequest()) {  
  108.             WebUtils.writeFailure(message);  
  109.         } else {  
  110.             try {  
  111.                 WebHttpRequestContext.getResponse().sendError(500, message);  
  112.             } catch (IOException e1) {  
  113.                 e1.printStackTrace();  
  114.             }  
  115.         }  
  116.     }  
  117.   
  118.     public String getMappingClass(String url) {  
  119.         return null;  
  120.     }  
  121.   
  122. }  

这段代码中就是通过URL找到对应的处理方法来进行处理,并且捕获异常。

这种方法Struts也是用到了,不过这个只是简单的兴趣研究并没有在实际项目中运用,可能会存在线程安全的问题。
0 0
原创粉丝点击