1. 看源码找不到入口,每次都晕车
- 如何下手,从哪里开始看,不要一开始就去调试
1.1 mini版本Spring实现的基本思路
[外链图片转存失败(img-4bBLSphb-1566873582828)(evernotecid://DE37D598-33A4-4708-800A-6D4CF841DDE2/wwwevernotecom/149352153/ENResource/p286)]
2. 手写spring源码V1版本
2.1 配置阶段
- 配置web.xml
- 设定init-param
- 设定url-pattern
- 配置Annotation
2.1.1 配置web.xml,设定init-param,设定url-pattern
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>My Web Application</display-name>
<servlet>
<!-- 拦截器名称 -->
<servlet-name>mymvc</servlet-name>
<!-- 拦截器对应的类路径 -->
<servlet-class>com.zhunongyun.toalibaba.myspring1.mvcframework.v2.servlet.GPDispatcherServlet</servlet-class>
<!-- init初始化参数 -->
<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>mymvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
2.1.2 配置Annotation
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
String value() default "";
}
2.2 初始化阶段
- 调用init()方法
- IOC容器初始化
- 扫描相关的类
- 创建实例化并保存至容器
- 进行DI操作
- 初始化HandlerMapping
2.2.1 调用init()方法
@Override
public void init(ServletConfig config) throws ServletException {
//模板模式
//1、加载配置文件
this.doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关的类
this.doScanner(contextConfig.getProperty("scanPackage"));
//3、初始化所有相关的类的实例,并且放入到IOC容器之中
this.doInstance();
//4、完成依赖注入
this.doAutowired();
//5、初始化HandlerMapping
this.initHandlerMapping();
System.out.println("My Spring framework is init.");
}
2.2.2 IOC容器初始化
/**
* 加载配置文件
*
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
try (InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);) {
//1、读取配置文件
contextConfig.load(fis);
} catch (Exception e) {
e.printStackTrace();
}
}
2.2.3 扫描相关的类
/**
* 扫描相关的类
* @param scanPackage
*/
private void doScanner(String scanPackage) {
//包传过来包下面的所有的类全部扫描进来的
URL url = this.getClass().getClassLoader()
.getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.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);
}
}
}
2.2.4 创建实例化并保存至容器
/**
* 初始化所有相关的类的实例,并且放入到IOC容器之中
*/
private void doInstance() {
//控制反转过程
//工厂模式来实现的
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)) {
Object instance = clazz.newInstance();
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(MyService.class)) {
//1、默认的类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
//2、自定义命名
MyService service = clazz.getAnnotation(MyService.class);
if (!"".equals(service.value())) {
beanName = service.value();
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3、根据类型注入实现类,投机取巧的方式
//clazz.getInterfaces()能获取当前类实现的接口类的类名
for (Class<?> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("The beanName is exists!!");
}
ioc.put(i.getName(), instance);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
2.2.5 进行DI操作
/**
* 完成依赖注入
*/
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(MyAutowired.class)) {
continue;
}
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
//不管你愿不愿意,强吻
field.setAccessible(true); //设置私有属性的访问权限
try {
//执行注入动作
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
2.2.6 初始化HandlerMapping
/**
* 初始化HandlerMapping
*/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(MyController.class)) {
continue;
}
String baseUrl = "";
//获取Controller的url配置
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if (!method.isAnnotationPresent(MyRequestMapping.class)) {
continue;
}
//映射URL
MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
// /demo/query
// (//demo//query)
String url = ("/" + baseUrl + "/" + requestMapping.value())
.replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Mapped " + url + "," + method);
}
}
}
2.3 运行阶段
- 调用doPost()/doGet()方法
- 匹配HandlerMapping
- 反射调用method.invoker()
- response.getWrite().write()
2.3.1 调用doPost()/doGet()方法,response.getWrite().write()
@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 Excetion Detail:" + Arrays.toString(e.getStackTrace()));
}
}
2.3.2 匹配HandlerMapping,反射调用method.invoker(),
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
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);
//第一个参数:方法所在的实例
//第二个参数:调用时所需要的实参
//获取方法的形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
//保存请求的url参数列表
Map<String, String[]> parameterMap = req.getParameterMap();
//保存赋值参数的位置
Object[] paramValues = new Object[parameterTypes.length];
Annotation[][] pa = method.getParameterAnnotations();
//按根据参数位置动态赋值
for (int i = 0; i < parameterTypes.length; i++) {
Class parameterType = parameterTypes[i];
if (parameterType == HttpServletRequest.class) {
paramValues[i] = req;
continue;
} else if (parameterType == HttpServletResponse.class) {
paramValues[i] = resp;
continue;
} else {
//提取方法中加了注解的参数
for (Annotation a : pa[i]) {
if (a instanceof MyRequestParam) {
String paramName = ((MyRequestParam) a).value();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "");
paramValues[i] = value;
}
break;
}
}
}
}
//投机取巧的方式
//通过反射拿到method所在class,拿到class之后还是拿到class的名称
//再调用toLowerFirstCase获得beanName
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName), paramValues);
}
2.4 拦截器MyDispatcherServlet类
package com.zhunongyun.toalibaba.myspring1.mvcframework.v2.servlet;
import com.zhunongyun.toalibaba.myspring1.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.Method;
import java.net.URL;
import java.util.*;
/**
* 拦截器
*/
public class MyDispatcherServlet extends HttpServlet {
//存储aplication.properties的配置内容
private Properties contextConfig = new Properties();
//存储所有扫描到的类
private List<String> classNames = new ArrayList<String>();
//IOC容器,保存所有实例化对象
//注册式单例模式
private Map<String, Object> ioc = new HashMap<String, Object>();
//保存Contrller中所有Mapping的对应关系
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
@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 Excetion Detail:" + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
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);
//第一个参数:方法所在的实例
//第二个参数:调用时所需要的实参
//获取方法的形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
//保存请求的url参数列表
Map<String, String[]> parameterMap = req.getParameterMap();
//保存赋值参数的位置
Object[] paramValues = new Object[parameterTypes.length];
Annotation[][] pa = method.getParameterAnnotations();
//按根据参数位置动态赋值
for (int i = 0; i < parameterTypes.length; i++) {
Class parameterType = parameterTypes[i];
if (parameterType == HttpServletRequest.class) {
paramValues[i] = req;
continue;
} else if (parameterType == HttpServletResponse.class) {
paramValues[i] = resp;
continue;
} else {
//提取方法中加了注解的参数
for (Annotation a : pa[i]) {
if (a instanceof MyRequestParam) {
String paramName = ((MyRequestParam) a).value();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "");
paramValues[i] = value;
}
break;
}
}
}
}
//投机取巧的方式
//通过反射拿到method所在class,拿到class之后还是拿到class的名称
//再调用toLowerFirstCase获得beanName
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName), paramValues);
}
@Override
public void init(ServletConfig config) throws ServletException {
//模板模式
//1、加载配置文件
this.doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关的类
this.doScanner(contextConfig.getProperty("scanPackage"));
//3、初始化所有相关的类的实例,并且放入到IOC容器之中
this.doInstance();
//4、完成依赖注入
this.doAutowired();
//5、初始化HandlerMapping
this.initHandlerMapping();
System.out.println("My Spring framework is init.");
}
/**
* 加载配置文件
*
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
try (InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);) {
//1、读取配置文件
contextConfig.load(fis);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 扫描相关的类
* @param scanPackage
*/
private void doScanner(String scanPackage) {
//包传过来包下面的所有的类全部扫描进来的
URL url = this.getClass().getClassLoader()
.getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.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);
}
}
}
/**
* 初始化所有相关的类的实例,并且放入到IOC容器之中
*/
private void doInstance() {
//控制反转过程
//工厂模式来实现的
if (classNames.isEmpty()) {
return;
}
try {
for (String className : classNames) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)) {
Object instance = clazz.newInstance();
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(MyService.class)) {
//1、默认的类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
//2、自定义命名
MyService service = clazz.getAnnotation(MyService.class);
if (!"".equals(service.value())) {
beanName = service.value();
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3、根据类型注入实现类,投机取巧的方式
//clazz.getInterfaces()能获取当前类实现的接口类的类名
for (Class<?> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("The beanName is exists!!");
}
ioc.put(i.getName(), instance);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 完成依赖注入
*/
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(MyAutowired.class)) {
continue;
}
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
//不管你愿不愿意,强吻
field.setAccessible(true); //设置私有属性的访问权限
try {
//执行注入动作
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
/**
* 初始化HandlerMapping
*/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(MyController.class)) {
continue;
}
String baseUrl = "";
//获取Controller的url配置
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if (!method.isAnnotationPresent(MyRequestMapping.class)) {
continue;
}
//映射URL
MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
// /demo/query
// (//demo//query)
String url = ("/" + baseUrl + "/" + requestMapping.value())
.replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("Mapped " + url + "," + method);
}
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}