zoukankan      html  css  js  c++  java
  • 实现一个简易版的SpringMvc框架

    先来看我们的程序入口DispatcherServlet

    主要核心处理流程如下:

    1 扫描基础包下的带有controller 以及 service注解的class,并保存到list

    2 对第一步扫描到的class进行实例化操作

    3 对第一步扫描到的class中带有AutoWired注解的属性值进入自动注入

    4 建立url和方法,方法对应的controller的映射关系(使用map

     

     

    当以上准备工作完成以后,

     

    用户提交请求,服务器获取url 并在第4步建立的映射关系中查找

     

    找到了,就执行相关方法,找不到 就报404错误

     

    以下是核心代码

     

    几个自定义注解

     

     

    @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 + " ";
    	}
    
    }
    

      运行结果如下

     总结:大体功能基本已经完成,如果想写出更多功能,建议阅读

  • 相关阅读:
    Qt 定时器事件startTimer
    认识网络、几种常用的网络拓扑图
    拓扑结构图,什么是拓扑结构
    Qt 利用QTime类来控制时间,QTime的成员函数的用法
    Qt QTime类的使用
    Qt 打开文件的默认路径 QFileDialog::getOpenFileName()
    Qt QWidget颜色设置的三种方法
    Qt 多个QDockWidget 切换显示
    Qt QString 格式化 arg 前面自动补0
    Qt 使用QMediaPlayer报错 defaultServiceProvider::requestService(): no service found for
  • 原文地址:https://www.cnblogs.com/javabigdata/p/7827849.html
Copyright © 2011-2022 走看看