先來看咱們的程序入口DispatcherServlethtml
主要核心處理流程以下:java
1 掃描基礎包下的帶有controller 以及 service註解的class,並保存到list中web
2 對第一步掃描到的class進行實例化操做json
3 對第一步掃描到的class中帶有AutoWired註解的屬性值進入自動注入數組
4 創建url和方法,方法對應的controller的映射關係(使用map)服務器
當以上準備工做完成之後,app
用戶提交請求,服務器獲取url 並在第4步創建的映射關係中查找ide
找到了,就執行相關方法,找不到 就報404錯誤測試
如下是核心代碼ui
幾個自定義註解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";
boolean required() default true;
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
================================================================================
幫助類
須要自行引入
Pom位置
<dependency> <groupId>org.objectweb.asm</groupId> <artifactId>org.objectweb.asm</artifactId> <version>3.3</version> </dependency>
public class AsmUtil { /** * 獲取指定類指定方法的參數名 * * @param clazz * 要獲取參數名的class * @param method * 要獲取參數名的方法 * @return 按參數順序排列的參數名列表,若是沒有參數,則返回null */ public static String[] getMethodParameterNamesByAsm4(final Class clazz, final Method method) { final String methodName = method.getName(); final Class<?>[] methodParameterTypes = method.getParameterTypes(); final int methodParameterCount = methodParameterTypes.length; String className = method.getDeclaringClass().getName(); final boolean isStatic = Modifier.isStatic(method.getModifiers()); final String[] methodParametersNames = new String[methodParameterCount]; int lastDotIndex = className.lastIndexOf("."); className = className.substring(lastDotIndex + 1) + ".class"; InputStream is = clazz.getResourceAsStream(className); try { ClassReader cr = new ClassReader(is); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cr.accept(new ClassAdapter(cw) { public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); final Type[] argTypes = Type.getArgumentTypes(desc); // 參數類型不一致 if (!methodName.equals(name) || !matchTypes(argTypes, methodParameterTypes)) { return mv; } return new MethodAdapter(mv) { public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { // 若是是靜態方法,第一個參數就是方法參數,非靜態方法,則第一個參數是 this ,而後纔是方法的參數 int methodParameterIndex = isStatic ? index : index - 1; if (0 <= methodParameterIndex && methodParameterIndex < methodParameterCount) { methodParametersNames[methodParameterIndex] = name; } super.visitLocalVariable(name, desc, signature, start, end, index); } }; } }, 0); } catch (Exception e) { e.printStackTrace(); } return methodParametersNames; } /** * 比較參數是否一致 */ private static boolean matchTypes(Type[] types, Class<?>[] parameterTypes) { if (types.length != parameterTypes.length) { return false; } for (int i = 0; i < types.length; i++) { if (!Type.getType(parameterTypes[i]).equals(types[i])) { return false; } } return true; } }
核心控制器
public class DispatcherServlet extends HttpServlet { /** * */ private static final long serialVersionUID = -8630585768835454263L; // 保存含有controller或者service註解的class名字 private List<String> classNames = new ArrayList<>(); // 保存含有實例化對象 private Map<String, Object> instancesMap = new HashMap<>(); // 保存url和url對應的方法以及controller的映射關係 private Map<String, UrlMethodMappingModel> handlerMapping = new HashMap<>(); public void init(ServletConfig config) throws ServletException { // 1 掃描基礎包下的全部controller 以及 service scanBasePackage(config.getInitParameter("basePackage")); try { // 2 實例化list中的對象 而且保存到map中 instanceClass(); // 3 注入使用了autowired的註解的屬性 doAutoWired(); // 4創建 url和方法的映射 urlAndMethodMapping(); } catch (Exception e) { e.printStackTrace(); } } private void urlAndMethodMapping() { for (Map.Entry<String, Object> entry : instancesMap.entrySet()) { Class<?> clazz = entry.getValue().getClass(); // 只處理Controller的,只有Controller有RequestMapping if (!clazz.isAnnotationPresent(Controller.class)) { continue; } // 定義url StringBuffer urlBuffer = new StringBuffer(); urlBuffer.append("/"); if (clazz.isAnnotationPresent(RequestMapping.class)) { RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class); urlBuffer.append(requestMapping.value()); } // 獲取方法上的RequestMapping Method[] methods = clazz.getMethods(); // 只處理帶RequestMapping的方法 for (Method method : methods) { if (!method.isAnnotationPresent(RequestMapping.class)) { break; } RequestMapping methodMapping = method.getAnnotation(RequestMapping.class); // requestMapping.value()便是在requestMapping上註解的請求地址,無論用戶寫不寫"/",咱們都給他補上 String realUrl = urlBuffer.append("/" + methodMapping.value()).toString(); // 替換掉多餘的"/",由於有的用戶在RequestMapping上寫"/xxx/xx",有的不寫,因此咱們處理掉多餘的"/" realUrl = realUrl.replaceAll("/+", "/"); // -------獲取訪問url完整路徑end // ---獲取方法的請求參數開始 // 獲取全部的參數的註解,有幾個參數就有幾個annotation[],空的話也會有一個數組 // 是數組的緣由是,一個參數能夠有多個註解…… Annotation[][] annotations = method.getParameterAnnotations(); // 因爲後面的Method的invoke時,須要傳入全部參數的值的數組,因此須要保存各參數的位置 Map<String/* 參數名 如 name */, Integer/* 參數下標位置 */> paramMap = new HashMap<>(); // 獲取方法裏的全部參數的參數名(注意:此處使用了ASM.jar // 如TestController 的test(String name, HttpServletResponse // reponse),將獲得以下數組["name","reponse"] String[] paramNames = AsmUtil.getMethodParameterNamesByAsm4(clazz, method); // 獲取全部參數的類型 Class<?>[] paramTypes = method.getParameterTypes(); for (int i = 0; i < annotations.length; i++) { Annotation[] annotationArray = annotations[i]; if (annotationArray.length == 0) // 就是當前參數沒有使用註解 (String // name ) { Class<?> type = paramTypes[i]; if (type == HttpServletRequest.class || type == HttpServletResponse.class) { paramMap.put(type.getName(), i); } else { // 普通屬性 paramMap.put(paramNames[i], i); } } // 有註解,就遍歷每一個參數上的全部註解 for (Annotation annotation : annotationArray) { if (annotation.annotationType() == RequestParam.class) { String paramValue = ((RequestParam) annotation).value(); if (!"".equals(paramValue)) { paramMap.put(paramValue, i); break; } } } } UrlMethodMappingModel model = new UrlMethodMappingModel(method, entry.getValue(), paramMap); handlerMapping.put(realUrl, model); } } } private void doAutoWired() { // 遍歷instancesMap中被託管的對象 for (Map.Entry<String, Object> entry : instancesMap.entrySet()) { // 查找全部被Autowired註解的屬性 Field[] fieldArray = entry.getValue().getClass().getDeclaredFields(); for (Field field : fieldArray) { // 沒有使用Autowired註解 if (!field.isAnnotationPresent(Autowired.class)) { continue; } Autowired autowired = field.getAnnotation(Autowired.class); if ("".equals(autowired.value())) { field.setAccessible(true); String beanName = lowerFirstChar(field.getType().getSimpleName()); if (instancesMap.get(beanName) != null) { try { field.set(entry.getValue(), instancesMap.get(beanName)); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } } private void instanceClass() throws Exception { if (classNames.size() == 0) { return; } for (String className : classNames) { try { Class<?> clazz = Class.forName(className); // 若是是controller 或者 service 直接使用類名 if (clazz.isAnnotationPresent(Controller.class)) { instancesMap.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance()); } else if (clazz.isAnnotationPresent(Service.class)) { // 獲取註解上的名字 若是有的話 // 獲取註解上的值 Service service = clazz.getAnnotation(Service.class); String servicename = service.value(); if (!"".equals(servicename)) { instancesMap.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance()); } else { Class<?>[] interfaces = clazz.getInterfaces(); // 這裏簡單處理 假定ServiceImpl只實現了一個接口 for (Class<?> class1 : interfaces) { instancesMap.put(lowerFirstChar(class1.getSimpleName()), clazz.newInstance()); break; } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } private void scanBasePackage(String basePackage) { URL baseUrl = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/")); File baseFile = new File(baseUrl.getFile()); File[] files = baseFile.listFiles(); for (File file : files) { if (file.isDirectory()) { // 是目錄 scanBasePackage(basePackage + "." + file.getName()); } else { // 是文件 if (!file.getName().endsWith(".class")) { continue; } String className = basePackage + "." + file.getName().replace(".class", ""); // 獲取該名字對應的的字節碼對象 判斷是否是含有Controller註解以及Service註解 try { Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)) { classNames.add(className); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 匹配用戶請求 boolean isPattern = patternUrlRequest(request, response); if (!isPattern) { out(response, "404 not found"); } } catch (Exception e) { e.printStackTrace(); } } private boolean patternUrlRequest(HttpServletRequest request, HttpServletResponse response) { String url = request.getRequestURI(); String projectUrl = request.getContextPath(); url = url.replace(projectUrl, "").replaceAll("/+", "/"); UrlMethodMappingModel mappingModel = handlerMapping.get(url); if (mappingModel != null) // 匹配成功 { Map<String, Integer> paramsIndex = mappingModel.getParamMap(); Object[] values = new Object[paramsIndex.size()]; // 獲取該方法的參數類型 Class<?>[] methodTypes = mappingModel.getMethod().getParameterTypes(); for (Map.Entry<String, Integer> params : paramsIndex.entrySet()) { String key = params.getKey(); // 變量名 if (key.equals(HttpServletRequest.class.getName())) { values[params.getValue()] = request; } else if (key.equals(HttpServletResponse.class.getName())) { values[params.getValue()] = response; } else { String userPostValue = request.getParameter(key); Class<?> type = methodTypes[params.getValue()]; // 轉換用戶提交上來的數據 Object convertValue = convertValue(userPostValue, type); values[params.getValue()] = convertValue; } } // 激活該方法 try { mappingModel.getMethod().invoke(mappingModel.getController(), values); } catch (Exception e) { e.printStackTrace(); } return true; } else { return false; } } private Object convertValue(String userPostValue, Class<?> type) { if (type == String.class) { return userPostValue; } else if (type == Integer.class || type == int.class) { return Integer.parseInt(userPostValue); } else if (type == Long.class || type == long.class) { return Long.valueOf(userPostValue); } else { try { throw new Exception("不支持的數據類型"); } catch (Exception e) { e.printStackTrace(); } } return null; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } // 首字母小寫 private String lowerFirstChar(String className) { char[] chars = className.toCharArray(); chars[0] += 32; return String.valueOf(chars); } private void out(HttpServletResponse response, String str) { try { response.setContentType("application/json;charset=utf-8"); response.getWriter().print(str); } catch (IOException e) { e.printStackTrace(); } } private class UrlMethodMappingModel { private Method method; private Object controller; private Map<String/* 參數名 */, Integer/* 下標位置 */> paramMap; public Method getMethod() { return method; } public Object getController() { return controller; } public Map<String, Integer> getParamMap() { return paramMap; } public UrlMethodMappingModel(Method method, Object controller, Map<String, Integer> paramMap) { this.method = method; this.controller = controller; this.paramMap = paramMap; } } }
測試Controller
@Controller public class TestController { @Autowired ITestService testServ; @RequestMapping(value = "/testAdd") public void testAdd(@RequestParam("name") String name, HttpServletResponse reponse) { String result = testServ.add(name); out(reponse, result); } @RequestMapping(value = "/test") public void test(String name, HttpServletResponse reponse) { String result = testServ.add(name+" *** "); out(reponse, result); } private void out(HttpServletResponse response, String str) { try { response.setContentType("application/json;charset=utf-8"); response.getWriter().print(str); } catch (IOException e) { e.printStackTrace(); } } }
服務接口定義
public interface ITestService { String add(String name); }
服務接口實現
@Service public class TestServImp implements ITestService { @Override public String add(String name) { return "add " + name + " "; } }
運行結果以下
總結:大致功能基本已經完成,若是想寫出更多功能,建議閱讀