手写SpringMVC-实战篇

       有了手写SpringMVC的思路,不知道大家是否已经开始实现呢?现在就为大家开始以上一篇文章《手写SpringMVC-思路篇》的思路来一步步的实现SpringMVC的手写,让大家也能在SpringMVC的实现上有个初步的认识。此篇文章可能有些长,希望大家可以一边看,一边动手按照提供的代码(此篇文章代码有些多)来实现。

一、先还是给大家看看代码的结构

手写SpringMVC-实战篇_第1张图片

上述的就是目前的服务端段代码,在项目的src->main->webapp->WEB-INF下创建一个web.xml文件,在项目启动的时候,加载web.xml文件,注入DispatcherServlet,实现后续的实例注入和初始化,并实现web的URL访问服务端,实现数据的响应。

 

二、考虑定义注解类

   在SpringMVC的框架实现中,少不了一些基本的注入类,例如:@Controller、@RequestMapping、@RequestParam等等,在上一篇文章中,根据需要实现的代码块,需要自定义一些注解类:

@CustomController   

@CustomRequestMapping

@CustomQualifier

@CustomRequestParam

@CustomService

这些分别对应着Spring+SpringMVC调用的实现注解,我们现在手动来写入这些注解。此处不过多的讲述,大家可以看文章手写SpringMVC-思路篇》中的第一部分,与SpringMVC的注解来对应理解。

注解--@CustomController   

package com.ygsoft.custom.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomController {
	String value() default "";
}

注解--@CustomRequestMapping

package com.ygsoft.custom.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomRequestMapping {
	String value() default "";
}

注解--@CustomQualifier

package com.ygsoft.custom.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomQualifier {
	String value() default "";
}

注解--@CustomRequestParam

package com.ygsoft.custom.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomRequestParam {
	String value() default "";
}

注解--@CustomService

package com.ygsoft.custom.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomService {
	String value() default "";
}

如果大家对注解定义不熟悉,最好去百度下,理解下如何自定义注解。

三、创建Controller和Service层

此部分和SpringMVC的实现代码一样,只是这部分的注解都是上一步定义的注解类来实现。

Controller层:

package com.ygsoft.custom.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomController;
import com.ygsoft.custom.annotation.CustomQualifier;
import com.ygsoft.custom.annotation.CustomRequestMapping;
import com.ygsoft.custom.annotation.CustomRequestParam;
import com.ygsoft.custom.service.MyService;

@CustomController
@CustomRequestMapping("/custom")
public class MyController {
	
	@CustomQualifier("MyServiceImpl")
	private MyService myService;
	
	@CustomRequestMapping("/query")
	public void query(HttpServletRequest request, HttpServletResponse response,
			@CustomRequestParam("name")String name, @CustomRequestParam("age")String age) {
		PrintWriter pw;
		try {
			PrintWriter writer = response.getWriter();
			String result = myService.query(name, age);
			writer.write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	} 
	
}

Service层:

package com.ygsoft.custom.service;

public interface MyService {
	public String query(String name, String age);
}

Service的impl层:

package com.ygsoft.custom.service.impl;

import com.ygsoft.custom.annotation.CustomService;
import com.ygsoft.custom.service.MyService;

@CustomService("MyServiceImpl")
public class MyServiceImpl implements MyService{

	@Override
	public String query(String name, String age) {
		return "name:" + name + ";age:" + age;
	}

}

四、创建DispatcherServlet类,并通过web.xml加载

web.xml:加载我们自己写的MyDispatcherServlet和读取配置文件。



  maven_handmvc
  
  	DispatcherServlet
  	com.ygsoft.custom.servlet.DispatcherServlet
  
  
  
  	DispatcherServlet
  	/
  

DispatcherServlet:本质上,我们自定义的DispatcherServlet是继承了HttpServlet,主要是实现init()、doGet()、doPost()方法,init()方法,就是初始化基本的beans,并实例化controller层中的定义的service的变量,同时实现映射URL请求的Path和方法;doGet()、doPost()目前给的是doGet()调用doPost(),所以doPost()主要是实现参数的解析,并通过反射的机制实现方法的调用。

package com.ygsoft.custom.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomController;
import com.ygsoft.custom.annotation.CustomQualifier;
import com.ygsoft.custom.annotation.CustomRequestMapping;
import com.ygsoft.custom.annotation.CustomService;
import com.ygsoft.custom.controller.MyController;
import com.ygsoft.custom.handlerAdapter.HandlerAdapterService;

public class DispatcherServlet extends HttpServlet {

	// 存在当前加载的所有的类
	List classNames = new ArrayList();
	
	// 存储IOC容器的MAP
	Map beans = new HashMap<>();
	
	// 存储路径和方法的映射关系
	Map handlerMap = new HashMap();
	
	public DispatcherServlet() {
		System.out.println("DispatchServlet()........");
	}
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("init()............");
		
		// 1.扫描需要的实例化的类
		doScanPackage("com.ygsoft.custom");
		System.out.println("当前文件下所有的class类.......");
		for(String name: classNames) {
			System.out.println(name);
		}
		
		// 2.实例化
		doInstance();
		System.out.println("当前实例化的对象信息.........");
		for(Map.Entry map: beans.entrySet()) {
			System.out.println("key:" + map.getKey() + "; value:" + map.getValue());
		}
		
		// 3.将IOC容器中的service对象设置给controller层定义的field上
		doIoc();
		
		// 4.建立path与method的映射关系
		handlerMapping();
		System.out.println("Controller层的path和方法映射.........");
		for(Map.Entry map: handlerMap.entrySet()) {
			System.out.println("key:" + map.getKey() + "; value:" + map.getValue());
		}
	}

	private void doScanPackage(String basePackage) {
		URL resource = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
		String fileStr = resource.getFile();
//		System.out.println(fileStr);
		File file = new File(fileStr);
		String[] listFiles = file.list();
		for(String path: listFiles) {
			File filePath = new File(fileStr + path);
			// 如果当前是目录,则递归
			if(filePath.isDirectory()) {
				doScanPackage(basePackage + "." + path);
			// 如果是文件,则直接添加到classNames
			} else {
				classNames.add(basePackage + "." + filePath.getName());
			}
		}
	}
	
	// 通过存储的classnames的类字符串来反射实例化对象,并存储与beans的Map中
	// com.ygsoft.custom.annotation.CustomController.class =>com.ygsoft.custom.annotation.CustomController
	private void doInstance() {
		if(classNames.isEmpty()) {
			System.out.println("doScanner Fail....");
		}
		
		// 开始实例化对象,通过反射来实现
		for(String className: classNames) {
			String cn = className.replaceAll(".class", "");
			try {
				Class clazz = Class.forName(cn);
				// 判断当前类是否有注解CustomController类,获取设置的CustomeRequestMapping的值
				if(clazz.isAnnotationPresent(CustomController.class)) {
					// 通过CustomeRequestMapping获取值,作为beans的key
					CustomRequestMapping requestMapping = clazz.getAnnotation(CustomRequestMapping.class);
					String key = requestMapping.value();
					// beans的vaue为实例化对象
					Object value = clazz.newInstance();
					beans.put(key, value);
				// 判断当前的类是否有注解CustomService(考虑Service层),获取值
				} else if(clazz.isAnnotationPresent(CustomService.class)) {
					// 通过CustomService获取值,作为beans的key
					CustomService service = clazz.getAnnotation(CustomService.class);
					String key = service.value();
					
					// beans的vaue为实例化对象
					Object value = clazz.newInstance();
					beans.put(key, value);
				} else {
					continue;
				}
				
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void doIoc() {
		if(beans.isEmpty()) {
			System.out.println("no class is instance......");
			return;
		}
		for(Map.Entry map: beans.entrySet()) {
			// 获取实例
			Object instance = map.getValue();
			
			// 获取类
			Class clazz = instance.getClass();
			// 如果当前是CustomController类,则获取类中定义的field来设置其对象
			if(clazz.isAnnotationPresent(CustomController.class)) {
				// 获取全部的成员变量
				Field[] fields = clazz.getDeclaredFields();
				for(Field field: fields) {
					// 如果当前的成员变量使用注解CustomRequestMapping进行处理
					if(field.isAnnotationPresent(CustomQualifier.class)) {
						// 获取当前成员变量的注解值
						CustomQualifier qualifier = field.getAnnotation(CustomQualifier.class);
						String value = qualifier.value();
						
						// 由于此类成员变量设置为private,需要强行设置
						field.setAccessible(true);
						
						// 将beans的实例化对象赋值给当前的变量
						try {
							field.set(instance, beans.get(value));
						} catch (IllegalArgumentException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						}
					} else {
						continue;
					}
				}
			}
		}
	}
	
	private void handlerMapping() {
		if(beans.isEmpty()) {
			System.out.println("no class is instance......");
			return;
		}
		for(Map.Entry map: beans.entrySet()) {
			// 获取当前的对象
			Object instance = map.getValue();
			
			// 获取当前的类
			Class clazz = instance.getClass();
			// 获取注解当前为Controller的类
			if(clazz.isAnnotationPresent(CustomController.class)) {
				// 获取类上的路径
				CustomRequestMapping clazzRm = clazz.getAnnotation(CustomRequestMapping.class);
				String clazzPath = clazzRm.value();
				
				// 处理方法
				Method[] methods = clazz.getMethods();
				for(Method method: methods) {
					// 判断注解为RequestMapping
					if(method.isAnnotationPresent(CustomRequestMapping.class)) {
						// 获取方法上的路径
						CustomRequestMapping methodRm = method.getAnnotation(CustomRequestMapping.class);
						String methodPath = methodRm.value();
						
						// 将类上的路径+方法上的路径 设置为key,方法设置为value
						handlerMap.put(clazzPath + methodPath, method);
					} else {
						continue;
					}
				}
			} else {
				continue;
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("doGet()............");
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("doPost()............");
		
		// 通过req获取请求的uri /maven_handmvc/custom/query
		String uri = req.getRequestURI();
		
		// /maven_handmvc
		String context = req.getContextPath();
		String path = uri.replaceAll(context, "");
		
		// 通过当前的path获取handlerMap的方法名
		Method method = (Method) handlerMap.get(path);
		// 获取beans容器中的bean
		MyController instance = (MyController) beans.get("/" + path.split("/")[1]);
		
		// 处理参数
		HandlerAdapterService ha = (HandlerAdapterService) beans.get("customHandlerAdapter"); 
		Object[] args = ha.handle(req, resp, method, beans);
		
		// 通过反射来实现方法的调用
		try {
			method.invoke(instance, args);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void destroy() {
		System.out.println("destroy()............");
	}

	
}

DispatcherServlet类的实现中涉及到下面几个步骤:

  • 在init方法中:

// 1.扫描需要的实例化的类
 doScanPackage("com.ygsoft.custom");
// 2.实例化
doInstance();
// 3.将IOC容器中的service对象设置给controller层定义的field上
doIoc();
 // 4.建立path与method的映射关系
handlerMapping();

  • 在doGet()和doPost()中:

主要是处理方法的参数,其中使用了策略模式来实现参数的处理,具体的可以看后续的代码。

 // 处理参数
        HandlerAdapterService ha = (HandlerAdapterService) beans.get("customHandlerAdapter"); 
        Object[] args = ha.handle(req, resp, method, beans);
        
 // 通过反射来实现方法的调用
        try {
            method.invoke(instance, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

五、参数处理涉及的类

其实此部分的实现,可以不用这么复杂,由于个人学习,想将设计模式-策略模式使用到此部分,所以就有了下面的代码,其实这部分大家认为理解比较困难,或者不好理解,大家可以使用简单的方式来实现。

使用反射调用实现的代码为:method.invoke(instance, args); 其中涉及的参数变量为args,目前的实现中请求的参数为HttpServletRequest request, HttpServletResponse response,
            @CustomRequestParam("name")String name, @CustomRequestParam("age")String age

可以通过Annotation[][] annotations = method.getParameterAnnotations();获取所有的参数的注解 与Class[] paramTypes = method.getParameterTypes();获取所有参数的类型进行遍历将args赋值,然后再通过上面的反射进行调用。

下面的代码是使用了策略模式来实现,大家可以借鉴,可以研究下。

HandlerAdapterService:定义一个处理参数接口

package com.ygsoft.custom.handlerAdapter;

import java.lang.reflect.Method;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface HandlerAdapterService {
	public Object[] handle(HttpServletRequest req, HttpServletResponse resp,
			Method method, Map beans);
}

CustomHandlerAdapter:处理参数的实现类

package com.ygsoft.custom.handlerAdapter;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomService;
import com.ygsoft.custom.argumentResolver.ArgumentResolver;

@CustomService("customHandlerAdapter")
public class CustomHandlerAdapter implements HandlerAdapterService {

	@Override
	public Object[] handle(HttpServletRequest req, HttpServletResponse resp,
			Method method, Map beans) {
		//获取方法中含义的参数
		Class[] paramClazzs = method.getParameterTypes();
		System.out.println("======当前需要解析的参数对应的类=========");
		for(Class clazz: paramClazzs) {
			System.out.println(clazz);
		}
		
		// 定义一个返回参数的结果集
		Object[] args = new Object[paramClazzs.length];
//		Object[] args = {req, resp, "chenyanwu", "20"};
		
		// 定义一个ArgumentResolver实现类的Map
		Map argumentResolvers = getBeansOfType(beans, ArgumentResolver.class);
		
		System.out.println("======当前需要解析的参数对应的类实例化=========");
		for(Map.Entry map: argumentResolvers.entrySet()) {
			System.out.println("key:" + map.getKey() + "; value:" + map.getValue());
		}
		
		//定义参数索引
		int paramIndex = 0;
		//定义数组下标索引
		int i = 0; 
		// 开始处理参数
		for(Class paramClazz: paramClazzs) {
			//哪个参数对应了哪个参数解析类,用策略模式来找
            for (Map.Entry entry : argumentResolvers.entrySet()) {
                ArgumentResolver ar = (ArgumentResolver)entry.getValue();
                
                if (ar.support(paramClazz, paramIndex, method)) {
                    args[i++] = ar.argumentResolver(req,
                            resp,
                            paramClazz,
                            paramIndex,
                            method);
                }
            }
            paramIndex++;
		}
		
		return args;
	}

	/**
	 * @param beans IOC容器中全部的bean
	 * @param intfType 定义的ArgumentResolver类
	 * @return
	 */
	private Map getBeansOfType(Map beans,
			Class intfType) {
		Map resultBeans = new HashMap<>();
		for(Map.Entry map: beans.entrySet()) {
			// 获取满足ArgumentResolver接口的bean
			Class[] intfs = map.getValue().getClass().getInterfaces();
			
			if(intfs != null && intfs.length >0) {
				for(Class intf: intfs) {
					// 将满足的bean存储在resultBeans中
					if(intf.isAssignableFrom(intfType)) {
						resultBeans.put(map.getKey(), map.getValue());
					}
				}
			}
		}
		return resultBeans;
	}

}

参数类:

ArgumentResolver:

package com.ygsoft.custom.argumentResolver;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface ArgumentResolver {
	/**
	 * 判断当前的类是继承于ArgumentResolver
	 * @param type 当前参数注解的类对象
	 * @param paramIndex 参数下标
	 * @param method 当前的方法
	 * @return
	 */
	public boolean support(Class type, int paramIndex, Method method);
	
	/**
	 * 
	 * @param request
	 * @param response
	 * @param type
	 * @param paramIndex
	 * @param method
	 * @return
	 */
	public Object argumentResolver(HttpServletRequest request,
	            HttpServletResponse response, Class type, 
	            int paramIndex,
	            Method method);
}

HttpServletRequestArgumentResolver:处理Request请求参数

package com.ygsoft.custom.argumentResolver;

import java.lang.reflect.Method;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomService;

@CustomService("httpServletRequestArgumentResolver")
public class HttpServletRequestArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class type, int paramIndex, Method method) {
		return ServletRequest.class.isAssignableFrom(type);
	}

	@Override
	public Object argumentResolver(HttpServletRequest request,
			HttpServletResponse response, Class type, int paramIndex,
			Method method) {
		return request;
	}

}

HttpServletResponseArgumentResolver:处理Response参数

package com.ygsoft.custom.argumentResolver;

import java.lang.reflect.Method;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomService;

@CustomService("httpServletResponseArgumentResolver")
public class HttpServletResponseArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class type, int paramIndex, Method method) {
		return ServletResponse.class.isAssignableFrom(type);
	}

	@Override
	public Object argumentResolver(HttpServletRequest request,
			HttpServletResponse response, Class type, int paramIndex,
			Method method) {
		return response;
	}

}

RequestParamArgumentResolver:处理自定义的参数

package com.ygsoft.custom.argumentResolver;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ygsoft.custom.annotation.CustomRequestParam;
import com.ygsoft.custom.annotation.CustomService;

@CustomService("requestParamArgumentResolver")
public class RequestParamArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class type, int paramIndex, Method method) {
		// type = class java.lang.String
		// @CustomRequestParam("name")String name
		//获取当前方法的参数
		Annotation[][] an = method.getParameterAnnotations();
		Annotation[] paramAns = an[paramIndex];
		
		for (Annotation paramAn : paramAns) {
        	//判断传进的paramAn.getClass()是不是 CustomRequestParam 类型
        	if (CustomRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                return true;
            }
        }
		
		return false;
	}

	@Override
	public Object argumentResolver(HttpServletRequest request,
			HttpServletResponse response, Class type, int paramIndex,
			Method method) {
		
		//获取当前方法的参数
		Annotation[][] an = method.getParameterAnnotations();
		Annotation[] paramAns = an[paramIndex];
		
		for (Annotation paramAn : paramAns) {
        	//判断传进的paramAn.getClass()是不是 CustomRequestParam 类型
        	if (CustomRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                CustomRequestParam cr = (CustomRequestParam) paramAn;
                String value = cr.value();
                
                return request.getParameter(value);
            }
        }
		return null;
	}

}

6.启动服务,在URL输入请求地址:

http://localhost:8080/maven_handmvc/custom/query?name=chenyanwu&&age=21

返回的结果为:

手写SpringMVC-实战篇_第2张图片

手写SpringMVC-实战篇_第3张图片

扫描关注:全栈工程师成长记
一个可以交流的平台,目的是为了做一个影响最有影响力的人的平台。

你可能感兴趣的:(SpringMVC)