Spring深度学习 — 高仿 Spring(IOC/DI)

前言

深度模仿 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 依赖的包

<properties>
        <servlet.api.version>2.4</servlet.api.version>
    </properties>

    <dependencies>
        <!-- required -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- required -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

web.xml 配置

<?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>mmt imitate Web Application</display-name>
    <servlet>
        <servlet-name>mmt-imitate</servlet-name>
        <servlet-class>com.mmt.imitate.spring.framework.webmvc.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mmt-imitate</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

实现我们自己的 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)
@Documented
public @interface MmtController {
 
	String value() default "";
}

自定义 @MmtService

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

自定义 @MmtAutowired

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

自定义 @MmtRequestMapping

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

自定义 @MmtRequestParam

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

Beans模块【配置封装】

BeanDefinition

package com.mmt.imitate.spring.framework.beans.config;

import lombok.Data;

@Data
public 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 入口类
 * <p>
 * 委派模式
 * 职责:负责任务调度,请求分发
 *
 */
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<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();
	private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
	private Map<String, BeanWrapper> factoryBeanInstanceCache = new HashMap<String, BeanWrapper>();

	public ApplicationContext(String... configLocations) {
		//1、 加载配置文件
		reader = new BeanDefinitionReader(configLocations);
		try {
			//2、解析配置文件,封装成BeanDefinition
			List<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
			//3、把BeanDefintion缓存起来
			doRegistBeanDefinition(beanDefinitions);
			//4、自动装配
			doAutowrited();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void doRegistBeanDefinition(List<BeanDefinition> 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<String, BeanDefinition> 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进行下载:https://gitee.com/heliang086/imitate_spring.git

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值