手写SpringMVC-DispatcherServlet

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <display-name>Web Application</display-name>
    <servlet>
        <servlet-name>gpmvc</servlet-name>
        <servlet-class>com.sunreal.mvcframework.v3.servlet.GPDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>gpmvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

配置需要扫描的包:application.properties

scanPackage=com.sunreal.demo

手写注解

package com.sunreal.mvcframework.annotation;

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

/**
 * @author lkz
 * @description @Target 注解的作用目标 ElementType.TYPE 作用于接口、类、枚举上
 * 				@Retention 注解的保留位置 RetentionPolicy.RUNTIME对应的策略:注解会在class中存在,并且在运行时可以反射获取
 * @date 2021/1/26 14:00
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GPAutowired {

	String value() default "";
}

package com.sunreal.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 14:41
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GpController {

	String value() default "";
}

package com.sunreal.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 14:43
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {

	String value() default "";
}

package com.sunreal.mvcframework.annotation;

import java.lang.annotation.*;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 14:48
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {

	String value() default "";
}

package com.sunreal.mvcframework.annotation;

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

/**
 * @author lkz
 * @description
 * @date 2021/1/26 13:39
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface GPService {
	String value() default "";
}

控制器,路由

package com.sunreal.demo.mvc.action;

import com.sunreal.demo.service.IDemoService;
import com.sunreal.mvcframework.annotation.GPAutowired;
import com.sunreal.mvcframework.annotation.GPRequestMapping;
import com.sunreal.mvcframework.annotation.GPRequestParam;
import com.sunreal.mvcframework.annotation.GpController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 15:09
 */
@GpController
@GPRequestMapping("/demo")
public class DemoAction {

	@GPAutowired
	private IDemoService demoService;

	@GPRequestMapping("/query")
	public void query(HttpServletRequest req, HttpServletResponse resp, @GPRequestParam("name") String name) {
		String result = demoService.get(name);
		try {
			resp.getWriter().write(result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@GPRequestMapping("/add")
	public void add(HttpServletRequest req, HttpServletResponse resp, @GPRequestParam("a") Integer a, @GPRequestParam("b") Integer b) {
		try {
			resp.getWriter().write(a + "+" + b + "=" + (a + b));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@GPRequestMapping("/remove")
	public void remove(HttpServletRequest req, HttpServletResponse resp, @GPRequestParam("id") Integer id) {
	}

}

package com.sunreal.demo.service;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 14:53
 */
public interface IDemoService {
	String get(String name);
}

package com.sunreal.demo.service.impl;

import com.sunreal.demo.service.IDemoService;
import com.sunreal.mvcframework.annotation.GPService;

/**
 * @author lkz
 * @description
 * @date 2021/1/26 15:08
 */
@GPService
public class DemoService implements IDemoService {

	@Override
	public String get(String name) {
		return "My name is " + name;
	}
}

DispatcherServlet1.0

package com.sunreal.mvcframework.v1.servlet;

import com.sunreal.mvcframework.annotation.GPAutowired;
import com.sunreal.mvcframework.annotation.GPRequestMapping;
import com.sunreal.mvcframework.annotation.GPService;
import com.sunreal.mvcframework.annotation.GpController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @author lkz
 * @description DispatcherServlet 1.0
 * 测试 http://localhost:8099/demo/query?name=666
 * @date 2021/1/26 15:24
 */
public class GPDispatcherServlet extends HttpServlet {

	private Map<String, Object> mapping = new HashMap<>();

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

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		if (!this.mapping.containsKey(url)) {
			resp.getWriter().write("404 Not Found!");
		}
		Method method = (Method) this.mapping.get(url);
		Map<String, String[]> params = req.getParameterMap();
		method.invoke(this.mapping.get(method.getDeclaringClass().getName()), new Object[]{req, resp, params.get("name")[0]});
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		Properties configContext = new Properties();
		InputStream is = null;
		try {
			is = config.getServletContext().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
			configContext.load(is);
			String scanPackage = configContext.getProperty("scanPackage");
			doScanner(scanPackage);
			final List<String> currentMapping = new ArrayList<>(mapping.keySet());
			for (String className : currentMapping) {
				if (!className.contains(".")) {
					continue;
				}
				Class<?> clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(GpController.class)) {
					mapping.put(className, clazz.newInstance());
					String baseUrl = "";
					if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
						GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
						baseUrl = requestMapping.value();
					}
					Method[] methods = clazz.getMethods();
					for (Method method : methods) {
						if (!method.isAnnotationPresent(GPRequestMapping.class)) {
							continue;
						}
						GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
						String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
						mapping.put(url, method);
						System.out.println("Mapped " + url + "," + method);
					}
				} else if (clazz.isAnnotationPresent(GPService.class)) {
					GPService gpService = clazz.getAnnotation(GPService.class);
					String beanName = gpService.value();
					if ("".equals(beanName)) {
						beanName = clazz.getName();
					}
					Object instance = clazz.newInstance();
					mapping.put(beanName, instance);
					for (Class<?> i : clazz.getInterfaces()) {
						mapping.put(i.getName(), instance);
					}
				} else {
					continue;
				}
			}
			for (Object object : mapping.values()) {
				if (object == null) {
					continue;
				}
				Class<?> clazz = object.getClass();
				if (clazz.isAnnotationPresent(GpController.class)) {
					Field[] fields = clazz.getDeclaredFields();
					for (Field field : fields) {
						if (!field.isAnnotationPresent(GPAutowired.class)) {
							continue;
						}
						GPAutowired gpAutowired = field.getAnnotation(GPAutowired.class);
						String beanName = gpAutowired.value();
						if ("".equals(beanName)) {
							beanName = field.getType().getName();
							field.setAccessible(true);
							field.set(mapping.get(clazz.getName()), mapping.get(beanName));
						}
					}
				}
			}
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}

	}

	private void doScanner(String scanPackage) {
		// . 需要进行转义 \\.
		URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
		File classDir = new File(url.getFile());
		for (File file : classDir.listFiles()) {
			if (file.isDirectory()) {
				doScanner(scanPackage + "." + file.getName());
			} else {
				String clazzName = (scanPackage + "." + file.getName()).replace(".class", "");
				mapping.put(clazzName, null);
			}
		}
	}
}

DispatcherServlet2.0

package com.sunreal.mvcframework.v2.servlet;

import com.sunreal.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @author lkz
 * @description DispatcherServlet 2.0
 * 测试 http://localhost:8099/demo/query?name=666
 * @date 2021/1/27 9:36
 */
public class GPDispatcherServlet extends HttpServlet {

	/**
	 * 保存application.properties配置文件中的内容
	 */
	private Properties contextConfig = new Properties();
	/**
	 * 保存扫描的所有的类名
	 */
	private List<String> classNames = new ArrayList<>();
	/**
	 * 传说中的IoC容器
	 * 为了简化程序,暂时不考虑ConcurrentHashMap
	 * 主要还是关注设计思想和原理
	 */
	private Map<String, Object> ioc = new HashMap<>();
	/**
	 * 保存url和method的对应关系
	 * 真实Spring源码中,是一个List而非Map
	 */
	private Map<String, Method> handlerMapping = new HashMap<>();

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

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			e.printStackTrace();
			resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
		}
	}

	/**
	 * 委派模式,分发策略
	 *
	 * @param req
	 * @param resp
	 */
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
		resp.setContentType("text/html;charset=UTF-8");
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
		if (!this.handlerMapping.containsKey(url)) {
			resp.getWriter().write("404 Not Found!");
			return;
		}
		Method method = this.handlerMapping.get(url);
		// 第一个参数:方法所在的实例
		// 第二个参数:调用时所需要的实参
		Map<String, String[]> params = req.getParameterMap();
		// 获取方法的形参列表
		Class<?>[] parameterTypes = method.getParameterTypes();
		// 保存请求的url参数列表
		Map<String, String[]> parameterMap = req.getParameterMap();
		// 保存赋值参数的位置
		Object[] paramValues = new Object[parameterTypes.length];
		// 根据参数位置动态赋值
		for (int i = 0; i < parameterTypes.length; i++) {
			Class parameterType = parameterTypes[i];
			if (parameterType == HttpServletRequest.class) {
				paramValues[i] = req;
			} else if (parameterType == HttpServletResponse.class) {
				paramValues[i] = resp;
			} else if (parameterType == String.class) {
				// 扫描方法参数上的注解
				Annotation[][] pa = method.getParameterAnnotations();
				outer:
				for (Annotation[] annotations : pa) {
					inner:
					for (Annotation annotation : annotations) {
						if (annotation instanceof GPRequestParam) {
							String paramName = ((GPRequestParam) annotation).value();
							if (!"".equals(paramName.trim())) {
								String value = Arrays.toString(parameterMap.get(paramName))
										.replaceAll("\\[|\\]", "")
										.replaceAll("\\s", "");
								paramValues[i] = value;
							}
						}
					}
				}
			}
		}
		// 通过反射获取Method所在的Class,获得Class之后还要获得Class的名称
		// 再次调用toLowerFirstCase获取beanName
		// 反射调用url方法,实现路由
		String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
		method.invoke(ioc.get(beanName), paramValues);
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		// 1.加载配置文件
		doLoadConfig(config, config.getInitParameter("contextConfigLocation"));
		// 2.扫描相关的类
		doScanner(contextConfig.getProperty("scanPackage"));
		// 3.初始化扫描到的类,并将它们放入IoC容器中
		doInstance();
		// 4.完成依赖注入
		doAutowired();
		// 5.初始化HandlerMapping
		initHandlerMapping();

		System.out.println("GP Spring framework is init.");
	}

	/**
	 * 初始化url和Method的一对一关系
	 */
	private void initHandlerMapping() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			String baseUrl = "";
			if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
				GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
				baseUrl = requestMapping.value();
			}
			for (Method method : clazz.getMethods()) {
				if (!method.isAnnotationPresent(GPRequestMapping.class)) {
					continue;
				}
				GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
				String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
				handlerMapping.put(url, method);
				System.out.println("Mapped :" + url + "," + method);
			}
		}
	}

	/**
	 * 自动进行依赖注入
	 */
	private void doAutowired() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if (!field.isAnnotationPresent(GPAutowired.class)) {
					continue;
				}
				GPAutowired autowired = field.getAnnotation(GPAutowired.class);
				String beanName = autowired.value().trim();
				// 注意:这里的自动装配,是通过className获取对象
				// 如果是自定义参数,那么用首字母小写的参数获取对象
				if ("".equals(beanName)) {
					beanName = field.getType().getName();
				}
				field.setAccessible(true);
				try {
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 初始化
	 * 工厂模式的体现
	 */
	private void doInstance() {
		if (classNames.isEmpty()) {
			return;
		}
		for (String className : classNames) {
			try {
				Class<?> clazz = Class.forName(className);
				// 什么样的类才需要初始化呢?
				// 加了注解的类才初始化,怎么判断?
				// 为了简化代码逻辑,主要体会设计思想,只用@Controller和@Service举例
				if (clazz.isAnnotationPresent(GpController.class)) {
					Object instance = clazz.newInstance();
					String beanName = toLowerFirstCase(clazz.getSimpleName());
					ioc.put(beanName, instance);
				} else if (clazz.isAnnotationPresent(GPService.class)) {
					GPService service = clazz.getAnnotation(GPService.class);
					String beanName = service.value();
					if ("".equals(beanName.trim())) {
						beanName = toLowerFirstCase(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);
					// 根据类型自动赋值,这是投机取巧的方式
					// 获取该类实现的接口
					for (Class<?> i : clazz.getInterfaces()) {
						if (ioc.containsKey(i.getName())) {
							throw new Exception("The " + i.getName() + "is exists!");
						}
						ioc.put(i.getName(), instance);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 扫描相关的类
	 *
	 * @param scanPackage 被扫描的包
	 */
	private void doScanner(String scanPackage) {
		URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
		File dirClass = new File(url.getFile());
		for (File file : dirClass.listFiles()) {
			if (file.isDirectory()) {
				doScanner(scanPackage + "." + file.getName());
			} else {
				if (!file.getName().endsWith(".class")) {
					continue;
				}
				String className = scanPackage + "." + file.getName().replace(".class", "");
				classNames.add(className);
			}
		}
	}

	/**
	 * 加载配置文件
	 *
	 * @param config                ServletConfig对象来接收配置的初始化参数
	 * @param contextConfigLocation 配置文件
	 */
	private void doLoadConfig(ServletConfig config, String contextConfigLocation) {
		try (InputStream is = config.getServletContext().getResourceAsStream(contextConfigLocation)) {
			contextConfig.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将类名首字母小写
	 * 这里比较的是ASCII码
	 * (char)小写字母 - (char)大写字母 = 32
	 *
	 * @param simpleName
	 */
	private String toLowerFirstCase(String simpleName) {
		char[] chars = simpleName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
}

DispatcherServlet3.0

package com.sunreal.mvcframework.v3.servlet;

import com.sunreal.mvcframework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author lkz
 * @description DispatcherServlet 3.0
 * 测试 http://localhost:8099/demo/query?name=666
 * @date 2021/1/28 10:11
 */
public class GPDispatcherServlet extends HttpServlet {

	/**
	 * 保存application.properties配置文件中的内容
	 */
	private Properties contextConfig = new Properties();
	/**
	 * 保存扫描的所有的类名
	 */
	private List<String> classNames = new ArrayList<>();
	/**
	 * 传说中的IoC容器
	 * 为了简化程序,暂时不考虑ConcurrentHashMap
	 * 主要还是关注设计思想和原理
	 */
	private Map<String, Object> ioc = new HashMap<>();
	/**
	 * 保存url和method的映射关系
	 */
	private List<Handler> handlerMapping = new ArrayList<>();

	/**
	 * 记录Controller中的RequestMapping和Method的对应关系
	 * 内部类
	 */
	private class Handler {
		/**
		 * 保存方法对应的实例
		 */
		protected Object controller;
		/**
		 * 保存映射的方法
		 */
		protected Method method;
		protected Pattern pattern;
		/**
		 * 参数顺序
		 */
		protected Map<String, Integer> paramIndexMapping;

		/**
		 * 构造一个Handler的基本参数
		 * @param pattern
		 * @param controller
		 * @param method
		 */
		protected Handler(Pattern pattern, Object controller, Method method) {
			this.controller = controller;
			this.method = method;
			this.pattern = pattern;
			paramIndexMapping = new HashMap<>();
			putParamIndexMapping(method);
		}

		private void putParamIndexMapping(Method method) {
			int index = 0;
			// 提取方法中的request和response参数
			Class<?>[] parameterTypes = method.getParameterTypes();
			for (Class<?> parameterType : parameterTypes) {
				if (parameterType == HttpServletRequest.class || parameterType == HttpServletResponse.class) {
					paramIndexMapping.put(parameterType.getName(), index++);
				}
			}
			// 提取方法中加了注解的参数
			Annotation[][] pa = method.getParameterAnnotations();
			for (Annotation[] annotations : pa) {
				for (Annotation annotation : annotations) {
					if (annotation instanceof GPRequestParam) {
						String paramName = ((GPRequestParam) annotation).value();
						if (!"".equals(paramName.trim())) {
							paramIndexMapping.put(paramName, index++);
						}
					}
				}
			}
		}
	}

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

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			e.printStackTrace();
			resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
		}
	}

	/**
	 * 委派模式,分发策略
	 * 匹配URL
	 * @param req
	 * @param resp
	 */
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
		resp.setContentType("text/html;charset=UTF-8");
		Handler handler = getHandler(req);
		if (handler == null) {
			resp.getWriter().write("404 Not Found!");
			return;
		}
		// 获取方法的形参列表
		Method method = handler.method;
		Class<?>[] parameterTypes = method.getParameterTypes();
		Object[] paramValues = new Object[parameterTypes.length];
		Map<String, String[]> params = req.getParameterMap();
		Map<String, Integer> paramIndexMapping = handler.paramIndexMapping;
		for (Map.Entry<String, String[]> param : params.entrySet()) {
			String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "")
					.replaceAll("\\s", ",");
			if (!paramIndexMapping.containsKey(param.getKey())) {
				continue;
			}
			Integer index = paramIndexMapping.get(param.getKey());
			paramValues[index] = convert(parameterTypes[index], value);
		}
		if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
			int reqIndex = paramIndexMapping.get(HttpServletRequest.class.getName());
			paramValues[reqIndex] = req;
		}
		if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
			int respIndex = paramIndexMapping.get(HttpServletResponse.class.getName());
			paramValues[respIndex] = resp;
		}
		Object result = method.invoke(handler.controller, paramValues);
		if (result == null || result instanceof Void) {
			return;
		}
		resp.getWriter().write(result.toString());
	}

	/**
	 * url传输过来的参数都是String类型的,由于HTTP基于字符串协议
	 * 只需要把String转换为任意类型
	 * @param parameterType
	 * @param value
	 * @return
	 */
	private Object convert(Class<?> parameterType, String value) {
		if (Integer.class == parameterType) {
			return Integer.valueOf(value);
		}
		return value;
	}

	private Handler getHandler(HttpServletRequest req) {
		if (handlerMapping.isEmpty()) {
			return null;
		}
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (Handler handler : handlerMapping) {
			try {
				Matcher matcher = handler.pattern.matcher(url);
				// 如果没有匹配上,继续匹配下一个
				if (!matcher.matches()) {
					continue;
				}
				return handler;
			} catch (Exception e) {
				throw e;
			}
		}
		return null;
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		// 1.加载配置文件
		doLoadConfig(config, config.getInitParameter("contextConfigLocation"));
		// 2.扫描相关的类
		doScanner(contextConfig.getProperty("scanPackage"));
		// 3.初始化扫描到的类,并将它们放入IoC容器中
		doInstance();
		// 4.完成依赖注入
		doAutowired();
		// 5.初始化HandlerMapping
		initHandlerMapping();

		System.out.println("GP Spring framework is init.");
	}

	/**
	 * 初始化url和Method的一对一关系
	 */
	private void initHandlerMapping() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			String baseUrl = "";
			if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
				GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
				baseUrl = requestMapping.value();
			}
			// 获取Method的url配置
			for (Method method : clazz.getMethods()) {
				// 没有加GPRequestMapping注解的直接忽略
				if (!method.isAnnotationPresent(GPRequestMapping.class)) {
					continue;
				}
				// 映射url
				GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
				String regex = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
				Pattern pattern = Pattern.compile(regex);
				handlerMapping.add(new Handler(pattern, entry.getValue(), method));
				System.out.println("mapping :" + regex + "," + method);
			}
		}
	}

	/**
	 * 自动进行依赖注入
	 */
	private void doAutowired() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			Field[] fields = entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if (!field.isAnnotationPresent(GPAutowired.class)) {
					continue;
				}
				GPAutowired autowired = field.getAnnotation(GPAutowired.class);
				String beanName = autowired.value().trim();
				// 注意:这里的自动装配,是通过className获取对象
				// 如果是自定义参数,那么用首字母小写的参数获取对象
				if ("".equals(beanName)) {
					beanName = field.getType().getName();
				}
				field.setAccessible(true);
				try {
					field.set(entry.getValue(), ioc.get(beanName));
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 初始化
	 * 工厂模式的体现
	 */
	private void doInstance() {
		if (classNames.isEmpty()) {
			return;
		}
		for (String className : classNames) {
			try {
				Class<?> clazz = Class.forName(className);
				// 什么样的类才需要初始化呢?
				// 加了注解的类才初始化,怎么判断?
				// 为了简化代码逻辑,主要体会设计思想,只用@Controller和@Service举例
				if (clazz.isAnnotationPresent(GpController.class)) {
					Object instance = clazz.newInstance();
					String beanName = toLowerFirstCase(clazz.getSimpleName());
					ioc.put(beanName, instance);
				} else if (clazz.isAnnotationPresent(GPService.class)) {
					GPService service = clazz.getAnnotation(GPService.class);
					String beanName = service.value();
					if ("".equals(beanName.trim())) {
						beanName = toLowerFirstCase(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);
					// 根据类型自动赋值,这是投机取巧的方式
					// 获取该类实现的接口
					for (Class<?> i : clazz.getInterfaces()) {
						if (ioc.containsKey(i.getName())) {
							throw new Exception("The " + i.getName() + "is exists!");
						}
						ioc.put(i.getName(), instance);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 扫描相关的类
	 *
	 * @param scanPackage 被扫描的包
	 */
	private void doScanner(String scanPackage) {
		URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
		File dirClass = new File(url.getFile());
		for (File file : dirClass.listFiles()) {
			if (file.isDirectory()) {
				doScanner(scanPackage + "." + file.getName());
			} else {
				if (!file.getName().endsWith(".class")) {
					continue;
				}
				String className = scanPackage + "." + file.getName().replace(".class", "");
				classNames.add(className);
			}
		}
	}

	/**
	 * 加载配置文件
	 *
	 * @param config                ServletConfig对象来接收配置的初始化参数
	 * @param contextConfigLocation 配置文件
	 */
	private void doLoadConfig(ServletConfig config, String contextConfigLocation) {
		try (InputStream is = config.getServletContext().getResourceAsStream(contextConfigLocation);) {
			contextConfig.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 将类名首字母小写
	 * 这里比较的是ASCII码
	 * (char)小写字母 - (char)大写字母 = 32
	 *
	 * @param simpleName
	 */
	private String toLowerFirstCase(String simpleName) {
		char[] chars = simpleName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值