?周末无事,借鉴Tom老师思路,手撕一个简版的Spring,大致思路如图。
?首先按照spring的设计,把前提条件如对应注解、controller、service提前建好。
1. 新建web项目,创建自己的Spring注解如 @ShenController、@ShenRequestMapping
2. 创建Service、Controller,使用自己的Spring注解
3. 配置文件简单指定需要扫描的包,并在web.xml里加以配置
?核心代码如下,整个小项目可在此处查看 码云demo
package com.bigshen.mvc.spring.servlet;
import com.bigshen.mvc.spring.annonation.*;
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 eddy.shen
* @Date 2020/4/5 15:52
* @Desc dispatcher类模拟Spring处理流程
**/
public class ShenDispatcherServlet extends HttpServlet {
// 配置文件properties,内部配置了类的扫描路径
private Properties properties = new Properties();
// 扫描路径下的所有class
private List<String> classList = new ArrayList<>();
// ioc容器管理bean
private Map<String, Object> iocMap = new HashMap<>();
// url对应映射关系
private Map<String, HandlerMapping> urlMap = new HashMap<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
}
// 分发匹配对应url
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {
String rootPath = req.getContextPath();
// 请求url的路径,去掉项目前缀
String path = req.getRequestURI().replace(rootPath, "");
if (null == urlMap.get(path)) {
throw new RuntimeException("404 not found!");
}
HandlerMapping handler = urlMap.get(path);
Method method = handler.getMethod();
Object instance = handler.getInstance();
Class[] paramTypes = handler.getParameterTypes();
// 参数值数组
Object[] paramValues = new Object[paramTypes.length];
// 本次请求传入参数
Map<String, String[]> requestParamMap = req.getParameterMap();
for (Map.Entry<String, Integer> entry : handler.getParameterIndexMap().entrySet()) {
String paramName = entry.getKey();
Integer index = entry.getValue();
if (paramName.equals(HttpServletRequest.class.getName())) {
// 为方法中的HttpServletRequest类型赋值
paramValues[index] = req;
continue;
}
if (paramName.equals(HttpServletResponse.class.getName())) {
// 为方法中的HttpServletResponse类型赋值
paramValues[index] = resp;
continue;
}
if (requestParamMap.containsKey(paramName)) {
// 为方法中使用spring注解的参数赋值
String[] value = requestParamMap.get(paramName);
// 根据参数类型class将值转换为对应类型
paramValues[index] = convert(paramTypes[index], value);
}
}
try {
//反射执行url所请求的对应method
Object result = method.invoke(instance, paramValues);
if(result == null || result instanceof Void){ return; }
resp.getWriter().write(result.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private Object convert(Class paramType, String[] value) {
String paramValue = Arrays.toString(value).replaceAll("\\[|\\]","")
.replaceAll("\\s",",");
if (paramType.equals(Integer.class)) {
return Integer.valueOf(paramValue);
}
if (paramType.equals(Double.class)) {
return Double.valueOf(paramValue);
}
// .... 简单方式转换,待补充
return paramValue;
}
@Override
public void init(ServletConfig config) throws ServletException {
// 1.加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 2.根据配置中指定的扫描路径进行扫描
doScanner(properties.getProperty("scanPackage"));
// 3.匹配的类放入ioc容器
doIOC();
//TODO doAOP() 在依赖注入前通过字节码重组生成新的代理类,进行依赖注入
// 4.将ioc容器中需要DI依赖注入的进行注入
doDI();
// 5.扫描controller类的method,将url与method绑定映射
initHandlerMapping();
System.out.println("Spring framework is init.");
}
// 加载配置文件,转为properties
private void doLoadConfig(String scanPackage) {
// 读取web.xml中配置的properties文件
InputStream is = this.getClass().getClassLoader().getResourceAsStream(scanPackage);
try {
// 转为properties
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 根据配置中指定的扫描路径进行扫描
private void doScanner(String scanPackage) {
// 将scanPackage扫描路径中的 . 转为 /
String packagePath = scanPackage.replaceAll("\\.", "/");
URL url = this.getClass().getClassLoader().getResource("/" + packagePath);
File file = new File(url.getFile());
if (file.isDirectory()) {
// 如果是路径,遍历子路径,递归查询
File[] childFile = file.listFiles();
for (File child : childFile) {
if (child.isFile() && child.getName().endsWith(".class")) {
// 是文件且是class文件,先统计出来
String className = child.getName();
String classPath = scanPackage + "." + className.replace(".class", "");
classList.add(classPath);
} else if (child.isDirectory()) {
// 文件夹、递归
doScanner(scanPackage + "." + child.getName());
}
}
}
}
// 遍历扫描到的类,如果符合Spring注解则交由IOC容器管理
private void doIOC() {
if (classList.isEmpty()) {return;}
for (String clazzName : classList) {
try {
Class clazz = Class.forName(clazzName);
if (clazz.isAnnotationPresent(ShenController.class)) {
// 如果类上有Controller类型注解
// 以类全名为key,将类对象创建出来放入 ioc容器
iocMap.put(clazz.getName(), clazz.newInstance());
} else if (clazz.isAnnotationPresent(ShenService.class)) {
// 如果类上有Service类型注解
ShenService service = (ShenService) clazz.getAnnotation(ShenService.class);
String value = service.value();
Boolean isEmpty = null == value || "".equals(value.trim()) ? true : false;
Boolean isInterface = clazz.getInterfaces().length > 0 ? true : false;
// ioc中key有几种情况
if (!isEmpty) {
// 1. 注解中value有值
Object exist = iocMap.get(value);
if (null != exist) {
throw new RuntimeException("IOC容器中存在重复的声明Bean【】" + value);
}
iocMap.put(value, clazz.newInstance());
} else if (!isInterface) {
// 2. 注解中value无值,且类没有实现接口
iocMap.put(clazz.getName(), clazz.newInstance());
} else {
// 3. 注解中value无值,类实现了接口
Class[] interfaces = clazz.getInterfaces();
Object instance = clazz.newInstance();
for (Class element : interfaces) {
// 为每个接口类型绑定同样的对象实例
Object exist = iocMap.get(element.getName());
if (null != exist) {
throw new RuntimeException("IOC容器中存在重复的声明Bean【】" + value);
}
iocMap.put(element.getName(), instance);
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
// 遍历IOC容器中的对象的属性,如果使用了注入的注解,对属性进行依赖注入
private void doDI() {
if (iocMap.isEmpty()) {return;}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
String name = entry.getKey();
Object instance = entry.getValue();
Class instanceClazz = instance.getClass();
Field[] fields = instanceClazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(ShenAutowired.class)) {
// ioc容器中的属性上有spring注入注解
field.setAccessible(true);
Class fieldClazz = field.getType();
try {
field.set(instance, iocMap.get(fieldClazz.getName()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
// 将controller中使用注解的method提取出,将URL与ioc容器中对象绑定,在接收到request请求后进行url匹配
private void initHandlerMapping() {
if (iocMap.isEmpty()) {return;}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
String name = entry.getKey();
Object instance = entry.getValue();
Class instanceClazz = instance.getClass();
String rootPath = "";
// 提取controller配置的url根路径
if (instanceClazz.isAnnotationPresent(ShenController.class) && instanceClazz.isAnnotationPresent(ShenRequestMapping.class)) {
ShenRequestMapping controllerMapping = (ShenRequestMapping) instanceClazz.getAnnotation(ShenRequestMapping.class);
rootPath = controllerMapping.value();
}
Method[] methods = instanceClazz.getDeclaredMethods();
for (Method method :methods) {
// 为每个method提取独立的url,并记录映射关系
if (method.isAnnotationPresent(ShenRequestMapping.class)) {
ShenRequestMapping methodMapping = method.getAnnotation(ShenRequestMapping.class);
String methodUrl = rootPath + methodMapping.value();
urlMap.put(methodUrl, new HandlerMapping(methodUrl, instance, method));
}
}
}
}
class HandlerMapping {
private String methodUrl;
private Object instance;
private Method method;
// 方法类型
private Class[] parameterTypes;
// 参数值、参数位置对应关系
private Map<String, Integer> parameterIndexMap = new HashMap<>();
public HandlerMapping(String methodUrl, Object instance, Method method) {
this.methodUrl = methodUrl;
this.instance = instance;
this.method = method;
// 在映射关系中,根据method提取方法入参及顺序
initMethodParameters();
}
// 提取method对应的参数映射关系
private void initMethodParameters() {
// 方法参数类型
parameterTypes = method.getParameterTypes();
// 根据参数注解找对应入参
// 方法参数的注解类型, 一个方法有多个参数,每个参数可以有多个注解,所有是个二维数组
Annotation[][] paramAnnotations = method.getParameterAnnotations();
for(int i = 0; i < paramAnnotations.length; i ++) {
Annotation[] paramAnnotationArray = paramAnnotations[i];
for (Annotation annotation : paramAnnotationArray) {
if (annotation instanceof ShenRequestParam) {
ShenRequestParam requestParam = (ShenRequestParam) annotation;
String paramName = requestParam.value();
if (null != paramName && !"".equals(paramName.trim())) {
parameterIndexMap.put(paramName, i);
}
}
}
}
// 固定的HttpServletRequest、HttpServletResponse
for(int i = 0 ; i < parameterTypes.length; i++) {
Class paramType = parameterTypes[i];
if (paramType.equals(HttpServletRequest.class)) {
parameterIndexMap.put(HttpServletRequest.class.getName(), i);
continue;
}
if (paramType.equals(HttpServletResponse.class)) {
parameterIndexMap.put(HttpServletResponse.class.getName(), i);
continue;
}
}
}
public String getMethodUrl() {
return methodUrl;
}
public Object getInstance() {
return instance;
}
public Method getMethod() {
return method;
}
public Class[] getParameterTypes() {
return parameterTypes;
}
public Map<String, Integer> getParameterIndexMap() {
return parameterIndexMap;
}
}
}
Spring 二、300行代码手写初体验Spring V1.0版本
原文:https://www.cnblogs.com/Qkxh320/p/spring_02.html