手写实现Spring(IOC、DI),SpringMVC基础功能

手写实现Spring(IOC、DI),SpringMVC功能

spring和springMVC的用法相信大家都不陌生,我简单讲下我实现的思路

spring阶段 事项
配置 配置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容器:实例化class
	private 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<>();

    @Override
	public 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);
			}
		}

	}
	@Override
	protected 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,则直接返回404
		if(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()) {
			//将数组参数转化为string
			String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
			//如果找到匹配的参数名,则开始填充参数数组paramValues
			if(!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;
	}

	@Override
	protected 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/

发布了47 篇原创文章 · 获赞 12 · 访问量 5069

猜你喜欢

转载自blog.csdn.net/qq_35448165/article/details/83275603
今日推荐