博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring深度学习 — 高仿 Spring(IOC/DI)
阅读量:37194 次
发布时间:2020-08-01

本文共 10243 字,大约阅读时间需要 34 分钟。

前言

深度模仿 Spring 源码,完成 IOC \ DI 操作,较上一个版本,内容更加贴近 Spring,主要也是先学习设计思想,为后续学习源码做铺垫。通过简单易懂的代码学习思想,带着思想去理解 Spring,加深学习效果,更加容易吸收。

一、从 Servlet 到 ApplicationContext

在上一篇中,我们已经知道 SpringMVC 的入口是 DispatcherServlet,我们通过自己手写的DispatcherServlet类继承了 HttpServlet类,并在 init 方法中完成了一系列的初始化动作。而在我们使用的 Spring 经验中,我们见的最多的就是 ApplicationContext,似乎Spring 托管的所有的实例 Bean都可以通过getBean()方法来获得。那么ApplicationContext又是从何而来的呢?从 Spring 的源码中,我们可以通过类图看到DispatcherServlet的关系网如下:

 DispatcherServlet 继承了FrameworkServlet,FrameworkServlet继承了 HttpServletBean,HttpServletBean继承了 HttpServlet。在HttpServletBean的 init()方法中调用了FrameworkServlet中的 initServletBean()方法,在initServletBean()方法中初始化了 webApplicationContext实例。在初始化webApplicationContext实例的同时,又调用了DispatcherServlet重写的onRefresh()方法,在DispatcherServlet重写的onRefresh()方法中通过调用 initStrategies()完成了 初始化SpringMVC的九大组件。

 其实,上面复杂的调用关系,我们可以简单的得出一个结论:就是在 Servlet的init()方法中初始化了IOC容器和SpringMVC所依赖的九大组件。

二、项目环境搭建

application.properties配置【使用 properties 文件只是为了代码里面好操作】

#定义需要扫描的包路径scanPackage=com.mmt.imitate

 pom.xml 配置

只引入一个 servlet 依赖的包

2.4
javax.servlet
servlet-api
${servlet.api.version}
provided
org.projectlombok
lombok
1.16.10
org.slf4j
slf4j-api
1.7.25
ch.qos.logback
logback-classic
1.2.3

web.xml 配置

mmt imitate Web Application
mmt-imitate
com.mmt.imitate.spring.framework.webmvc.servlet.DispatcherServlet
contextConfigLocation
classpath:application.properties
1
mmt-imitate
/*

实现我们自己的 DispatcherServlet类

public class DispatcherServlet extends HttpServlet {	@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 {		//	}	@Override	public void init(ServletConfig config) throws ServletException {		super.init(config);	}}

三、IOC 顶层结构设计

annotation 模块【自定义注解】

annotation 注解与上一篇稳重中的版本一致,可直接使用mini 版里面写的,这里直接复制过来

自定义 @MmtController

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MmtController { 	String value() default "";}

自定义 @MmtService

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MmtService {	String value() default "";}

自定义 @MmtAutowired

@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MmtAutowired { 	String value() default "";}

自定义 @MmtRequestMapping

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

自定义 @MmtRequestParam

@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MmtRequestParam {    String value() default "";}

Beans模块【配置封装】

BeanDefinition

package com.mmt.imitate.spring.framework.beans.config;import lombok.Data;@Datapublic class BeanDefinition {	private String factoryBeanName;	private String beanClassName;}

BeanWrapper

package com.mmt.imitate.spring.framework.beans;public class BeanWrapper {	/**	 * 原始对象	 */	private Object wrapperInstance;	/**	 * 对应的 class	 */	private Class
wrappedClass; public BeanWrapper(Object instance) { this.wrapperInstance = instance; this.wrappedClass = instance.getClass(); } public Object getWrapperInstance() { return wrapperInstance; } /** * 返回代理以后的 class,有可能是 $Proxy0 * @return */ public Class
getWrappedClass() { return wrappedClass; }}

context 模块【IOC 容器】

ApplicationContext

package com.mmt.imitate.spring.framework.context;/** * 完成 bean 的创建和 DI */public class ApplicationContext {		// TODO 先将类创建好,后面再完善}

DispatcherServlet 完善

同上一篇 mini 版一样,我们在上一版的基础上进行优化,这次,我们使用ApplicationContext 来完成 Bean 的初始化过程,具体看代码吧!

package com.mmt.imitate.spring.framework.webmvc.servlet;import com.mmt.imitate.spring.framework.context.ApplicationContext;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.IOException;import java.util.Arrays;/** * 高仿 spring - mvc 入口类 * 

* 委派模式 * 职责:负责任务调度,请求分发 * */public class DispatcherServlet extends HttpServlet { private ApplicationContext applicationContext; @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 { //TODO 委派,根据URL去找到一个对应的Method并通过response返回 try { doDispatch(req, resp); } catch (Exception e) { e.printStackTrace(); resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace())); } } /** * 通过 init方法来进行 ioc 容器的初始化动作 和 初始化 MVC 相关的 9 大组件 * * @param config * @throws ServletException */ @Override public void init(ServletConfig config) throws ServletException { // 初始化 IOC 相关 applicationContext = new ApplicationContext(config.getInitParameter("contextConfigLocation")); //TODO 初始化 MVC 部分,下次继续 } /** * mvc 响应 * * @param req * @param resp * @throws Exception */ private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { //TODO }}

注意 init 方法,在 init 方法中,进行了 IOC 容器的初始化以及 MVC 相关的初始化动作,这里需要完善一下 ApplicationContext 类:

package com.mmt.imitate.spring.framework.context;import com.mmt.imitate.spring.framework.annotation.MmtAutowired;import com.mmt.imitate.spring.framework.annotation.MmtController;import com.mmt.imitate.spring.framework.annotation.MmtService;import com.mmt.imitate.spring.framework.beans.BeanWrapper;import com.mmt.imitate.spring.framework.beans.config.BeanDefinition;import com.mmt.imitate.spring.framework.beans.support.BeanDefinitionReader;import java.lang.reflect.Field;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 完成 bean 的创建和 DI * */public class ApplicationContext {	private BeanDefinitionReader reader;	private Map
beanDefinitionMap = new HashMap
(); private Map
factoryBeanObjectCache = new HashMap
(); private Map
factoryBeanInstanceCache = new HashMap
(); public ApplicationContext(String... configLocations) { //1、 加载配置文件 reader = new BeanDefinitionReader(configLocations); try { //2、解析配置文件,封装成BeanDefinition List
beanDefinitions = reader.loadBeanDefinitions(); //3、把BeanDefintion缓存起来 doRegistBeanDefinition(beanDefinitions); //4、自动装配 doAutowrited(); } catch (Exception e) { e.printStackTrace(); } } private void doRegistBeanDefinition(List
beanDefinitions) throws Exception { for (BeanDefinition beanDefinition : beanDefinitions) { if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) { throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists"); } beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition); beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition); } } /** * 自动装配 */ private void doAutowrited() { //调用getBean() //这一步,所有的Bean并没有真正的实例化,还只是配置阶段 for (Map.Entry
beanDefinitionEntry : this.beanDefinitionMap.entrySet()) { String beanName = beanDefinitionEntry.getKey(); getBean(beanName); } } /** * Bean的实例化,DI是从而这个方法开始的 * * @param beanName * @return */ public Object getBean(String beanName) { //1、先拿到BeanDefinition配置信息 BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName); //2、反射实例化newInstance(); Object instance = instantiateBean(beanName, beanDefinition); //3、封装成一个叫做BeanWrapper BeanWrapper beanWrapper = new BeanWrapper(instance); //4、保存到IoC容器 factoryBeanInstanceCache.put(beanName, beanWrapper); //5、执行依赖注入 populateBean(beanName, beanDefinition, beanWrapper); return beanWrapper.getWrapperInstance(); } /** * 创建真正的实例对象 * * @param beanName * @param beanDefinition * @return */ private Object instantiateBean(String beanName, BeanDefinition beanDefinition) { String className = beanDefinition.getBeanClassName(); Object instance = null; try { Class
clazz = Class.forName(className); //2、默认的类名首字母小写 instance = clazz.newInstance(); this.factoryBeanObjectCache.put(beanName, instance); } catch (Exception e) { e.printStackTrace(); } return instance; } private void populateBean(String beanName, BeanDefinition beanDefinition, BeanWrapper beanWrapper) { //可能涉及到循环依赖? //A{ B b} //B{ A b} //用两个缓存,循环两次 //1、把第一次读取结果为空的BeanDefinition存到第一个缓存 //2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值 Object instance = beanWrapper.getWrapperInstance(); Class
clazz = beanWrapper.getWrappedClass(); //在Spring中@Component,这里只是单纯的处理了 controller 和 service 两个注解 if (!(clazz.isAnnotationPresent(MmtController.class) || clazz.isAnnotationPresent(MmtService.class))) { return; } //把所有的包括private/protected/default/public 修饰字段都取出来 for (Field field : clazz.getDeclaredFields()) { if (!field.isAnnotationPresent(MmtAutowired.class)) { continue; } MmtAutowired autowired = field.getAnnotation(MmtAutowired.class); //如果用户没有自定义的beanName,就默认根据类型注入 String autowiredBeanName = autowired.value().trim(); if ("".equals(autowiredBeanName)) { //field.getType().getName() 获取字段的类型 autowiredBeanName = field.getType().getName(); } //暴力访问 field.setAccessible(true); try { if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) { continue; } //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例 field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance()); } catch (IllegalAccessException e) { e.printStackTrace(); continue; } } }}

由于代码太多,不一一贴于此处,如有需要源码的小伙伴,可到Gitee进行下载:

后续更新,初始化 SpringMVC 9大组件及使用。

转载地址:http://fupwwy.baihongyu.com/

你可能感兴趣的文章
说说 Oracle 的 TRUNC 函数
查看>>
说说 Oracle 的时间格式化参数以及在 TO_CHAR() 与 TO_DATE() 中的应用
查看>>
系统架构设计笔记(41)—— 系统过渡计划
查看>>
系统架构设计笔记(42)—— 软件架构概述
查看>>
系统架构设计笔记(57)—— 测试自动化与面向对象的测试
查看>>
系统架构设计笔记(58)—— 嵌入式系统概论
查看>>
说说 Python 的生成器表达式
查看>>
说说 Activiti 中的用户与组的概念
查看>>
系统架构设计笔记(62)—— 嵌入式数据库管理系统
查看>>
系统架构设计笔记(63)—— 实时嵌入式操作系统
查看>>
说说如何使用 Canvas 绘制弧线与曲线
查看>>
系统架构设计笔记(64)—— 嵌入式系统设计
查看>>
系统架构设计笔记(65)—— 项目的范围、时间与成本
查看>>
系统架构设计笔记(66)—— 配置管理与文档管理
查看>>
说说 Python 元组的高级用法
查看>>
系统架构设计笔记(66)—— 配置管理与文档管理
查看>>
系统架构设计笔记(67)—— 软件需求管理
查看>>
系统架构设计笔记(68)—— 软件开发的质量与风险
查看>>
系统架构设计笔记(69)—— 人力资源管理
查看>>
系统架构设计笔记(70)—— 软件运行评价与过程改进
查看>>