當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
手写实现Spring(IOC、DI),SpringMVC基础功能
生活随笔
收集整理的這篇文章主要介紹了
手写实现Spring(IOC、DI),SpringMVC基础功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
手寫實現Spring(IOC、DI),SpringMVC功能
spring和springMVC的用法相信大家都不陌生,我簡單講下我實現的思路
| 配置 | 配置web.xml: init-param和Servlet |
| 初始化啟動 | 讀取web.xml,加載spring配置文件init() |
| 獲取配置文件里的掃包路徑,掃描得到相關的class集合 | |
| 實例化相關的實體類,注入到ioc容器中 | |
| 依賴注入(byType or byName) | |
| 獲取handlerMaping url和method的映射關系 | |
| 執行轉發 | 獲取url請求地址 |
| 根據url匹配對應的controller的method | |
| 通過反射調用對應的method,獲取controller的返回結果,解析 |
代碼
package com.chenpp.spring.web.servlet;public class CPDispatcherServlet extends HttpServlet{//存儲對應的配置文件信息(config.properties)private static ConcurrentHashMap<String, String> initParams = new ConcurrentHashMap<String, String>();//ioc容器:實例化classprivate static ConcurrentHashMap<String, Object> iocMap = new ConcurrentHashMap<String, Object>();//存儲url和對應的方法的Mapping關系private static ConcurrentHashMap<Pattern, MethodMapping> urlMethodMap = new ConcurrentHashMap<Pattern, MethodMapping>();//存儲掃描到的包路徑下class類private static List<Class<?>> classes = new ArrayList<>();@Overridepublic void init(ServletConfig servletConfig) throws ServletException {//1.根據web.xml配置的init-param獲取配置文件路徑,讀取掃包路徑parseConfig(servletConfig);//2.根據掃包路徑,遞歸獲取到所有需要掃描的class[]doScanner(initParams.get(Constants.PACKAGE_SCANNING));//3.初始化@CPService和@CPController的beans,放入到IOC容器中initBeans(classes);//4.對使用了@CPAutowire注解的屬性值進行依賴注入(反射機制)doDI();//5.遍歷所有的@CPController的類和其上的方法,對url和method進行映射handlerMapping();System.out.println("spring 啟動加載完成...........");}private void parseConfig(ServletConfig servletConfig) {String location = servletConfig.getInitParameter(Constants.CONTEXT_CONFIG_LOCATION);InputStream in = this.getClass().getClassLoader().getResourceAsStream(location);Properties properties = new Properties();try {properties.load(in);} catch (IOException e) {e.printStackTrace();}finally{try {if(in != null){in.close();}} catch (IOException e) {e.printStackTrace();}}//遍歷properties,保存到initParamMap里for(Object key:properties.keySet()){Object value = properties.get(key);initParams.put(key.toString(), value.toString());}}private void doScanner(String packageName) {//根據path遍歷所有class文件URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.","/"));File dir = new File(url.getFile());try{for(File file:dir.listFiles()){if(file.isDirectory()){//file是目錄,遞歸doScanner(packageName + "." + file.getName());}else{//獲取class文件路徑和文件名String className = packageName + "." + file.getName().replace(".class", "").trim();Class<?> classFile = Class.forName(className);classes.add(classFile);}}}catch (Exception e){}}private void initBeans(List<Class<?>> classes){try{//遍歷所有的class文件,判斷其上是否有CPController和CPService注解for(Class<?> clazz :classes){//對于CPController只需要存儲beanName和對應bean的關系即可(一般不用于注入)if(clazz.isAnnotationPresent(CPController.class)){CPController controller = clazz.getAnnotation(CPController.class);String beanName = controller.value();//如果有定義屬性值,以其為beanName,否則默認類名首字母小寫if(StringUtils.isEmpty(beanName)){beanName = StringUtils.toFirstLowChar(clazz.getSimpleName());}iocMap.put(beanName, clazz.newInstance());continue;}else if(clazz.isAnnotationPresent(CPService.class)){//CPService注入有根據beanName和類型兩種(接口類型)CPService service = clazz.getAnnotation(CPService.class);String beanName = service.value();//如果有定義屬性值,以其為beanName,否則默認類名首字母小寫if(StringUtils.isEmpty(beanName)){beanName = StringUtils.toFirstLowChar(clazz.getSimpleName());iocMap.put(beanName, clazz.newInstance());}//按照類型存儲一個實例關系(為了方便按照類型注入)iocMap.put(clazz.getName(), clazz.newInstance());//按照接口的類型再存儲一個實例關系(注入的時候方便按照接口類型來注入)Class<?>[] interfaces = clazz.getInterfaces();for (Class<?> i : interfaces) {iocMap.put(i.getName(), clazz.newInstance());}}//TODO ...其他關于Component的注解就先不考慮}}catch (Exception e){}}//執行依賴注入private void doDI() {try{//遍歷所有iocMap里的實例集合,判斷其屬性字段上是否有@CPAutowire注解for(Map.Entry<String,Object> entry:iocMap.entrySet()){Class<?> clazz = entry.getValue().getClass();Field[] fields = clazz.getDeclaredFields();for(Field field:fields){//CPAutowire默認使用byType的方式裝配CPAutowire autoAnnotation = field.getAnnotation(CPAutowire.class);field.setAccessible(true);if(autoAnnotation != null ){CPQualifier qualifier = field.getAnnotation(CPQualifier.class);if(qualifier != null && !StringUtils.isEmpty(qualifier.value())){//按照名字注入field.set(entry.getValue(), iocMap.get(qualifier.value()));continue;}//否則按照類型注入field.set(entry.getValue(), iocMap.get(field.getType().getName()));}}}}catch (Exception e){}}private void handlerMapping() {for(Map.Entry<String,Object> entry:iocMap.entrySet()){Class<?> clazz = entry.getValue().getClass();//判斷Controller類上是否有CPRequestMapping注解if(!clazz.isAnnotationPresent(CPRequestMapping.class)) continue;String baseUrl = clazz.getAnnotation(CPRequestMapping.class).value();Method[] methods = clazz.getDeclaredMethods();//遍歷CPController上的Method 獲取url與MethodMapping的映射關系for(Method method:methods){String methodUrl ="";if(method.isAnnotationPresent(CPRequestMapping.class)){methodUrl = method.getAnnotation(CPRequestMapping.class).value();}String regex = ("/"+baseUrl+methodUrl).replaceAll("/+", "/");Pattern pattern = Pattern.compile(regex);MethodMapping model = new MethodMapping(entry.getValue(),method);urlMethodMap.put(pattern, model);}}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {try{this.doDispatcher(req, resp);}catch (Exception e){resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));}}private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {//獲取實際的url請求String url = req.getRequestURI();String contextPath = req.getContextPath();url = url.replace(contextPath, "").replaceAll("/+", "/");//根據url請求去獲取響應的MethodBean對象MethodMapping methodMapping = null;for(Pattern pattern: urlMethodMap.keySet()){if(pattern.matcher(url).matches()){methodMapping = urlMethodMap.get(pattern);}}//如果找不到匹配的url,則直接返回404if(methodMapping == null){resp.getWriter().println("404 not found");resp.flushBuffer();return;}//獲取方法的參數類型 列表Class<?> [] paramTypes = methodMapping.getMethod().getParameterTypes();//用于存儲實際的參數列表Object [] paramValues = new Object[paramTypes.length];//獲取請求的參數列表(Request請求里的參數都是字符串類型的,如果一個參數出現多次,那么它的value就是String數組)Map<String,String[]> params = req.getParameterMap();for (Map.Entry<String, String[]> param : params.entrySet()) {//將數組參數轉化為stringString value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");//如果找到匹配的參數名,則開始填充參數數組paramValuesif(!methodMapping.getParamIndexMapping().containsKey(param.getKey())){continue;}int index = methodMapping.getParamIndexMapping().get(param.getKey());paramValues[index] = convert(paramTypes[index],value);}//設置方法中的request和response對象if(methodMapping.getParamIndexMapping().containsKey(HttpServletRequest.class.getName())){int reqIndex = methodMapping.getParamIndexMapping().get(HttpServletRequest.class.getName());paramValues[reqIndex] = req;}if(methodMapping.getParamIndexMapping().containsKey(HttpServletResponse.class.getName())) {int respIndex = methodMapping.getParamIndexMapping().get(HttpServletResponse.class.getName());paramValues[respIndex] = resp;}//執行方法獲得返回值Object returnValue = "";try {returnValue = methodMapping.getMethod().invoke(methodMapping.getController(),paramValues);//如果方法有加CPResponseBody注解,則直接返回結果 TODO Controller上也可加,這里就沒考慮這種情形if(methodMapping.getMethod().isAnnotationPresent(CPResponseBody.class)){resp.getWriter().println(returnValue);return;}//否則根據配置文件里配置的視圖進行轉發req.getRequestDispatcher(initParams.get(Constants.PAGE_PREFIX)+returnValue+initParams.get(Constants.PAGE_SUFFIX)).forward(req, resp);} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}/*** 轉化參數類型,將String轉化為實際的參數類型* */private Object convert(Class<?> paramType, String value) {if( int.class == paramType || Integer.class == paramType){return Integer.valueOf(value);}if( double.class == paramType || Double.class == paramType){return Double.valueOf(value);}//TODO 這里只是列舉了幾種常用的,可以繼續完善...return value;}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {this.doPost(req,resp);} } public class MethodMapping {private Method method;private Object controller;protected Map<String,Integer> paramIndexMapping; //參數順序public Method getMethod() {return method;}public void setMethod(Method method) {this.method = method;}public Object getController() {return controller;}public void setController(Object controller) {this.controller = controller;}public Map<String, Integer> getParamIndexMapping() {return paramIndexMapping;}public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {this.paramIndexMapping = paramIndexMapping;}public MethodMapping(Object controller, Method method){this.controller = controller;this.method = method;paramIndexMapping = new HashMap<String,Integer>();putParamIndexMapping(method);}/*** 根據方法獲取對應參數和下標的Mapping** */private void putParamIndexMapping(Method method) {//遍歷Method中的所有參數,獲取其對應的參數名和下標Parameter[] params = method.getParameters();for(int i = 0 ; i < params.length ; i++){Class<?> type = params[i].getType();if(type == HttpServletRequest.class || type == HttpServletResponse.class){paramIndexMapping.put(type.getName(),i);continue;}Annotation[] annotations = params[i].getAnnotations();String paramName = getAnnotationParamName(annotations);if(StringUtils.isEmpty(paramName)){//想要通過反射獲取參數名而不是arg0,需要在編譯時指定“-parameters”選項paramName = params[i].getName();}paramIndexMapping.put(paramName,i);}}private String getAnnotationParamName(Annotation[] annotations){for(Annotation a : annotations) {if (a instanceof CPRequestParam) {return ((CPRequestParam) a).value();}}return "";}}執行效果:
源碼地址:
https://github.com/dearfulan/cp-springmvc/tree/master/
總結
以上是生活随笔為你收集整理的手写实现Spring(IOC、DI),SpringMVC基础功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Gradle构建中:No cached
- 下一篇: JAVA通过反射获取方法真实参数名