纯手写SpringFramework-完结版(原创)
个人简介
作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。
@[toc]
纯手写SpringFramework-完结版
详细介绍Spring是什么?
- 再来介绍一下什么是Spring,一方面为了字数防止限流、另一方面是为了有的朋友直接看第二代手写SpringFramework而不知道什么是Spring。为了防止这种情况(之后的每一篇都要介绍一下伟大的Spring),当然这是题外话了=__=
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
(以上摘选自:《百度百科》)
这一代的Spring新增了什么?
- 1:提供了基于XML配置文件管理bean。目前实现的XML配置文件功能有:
- 1.1:目前实现的标签有:my-beans、my-bean、my-properties、my-ref
- 2:新增了Spring容器的另外一个实现类:MyClassPathXmlApplicationContext。
手写Spring的生命周期
- 推断构造方法(这里我全都采用无參构造)->创建bean对象->DI依赖注入->Aware回调(BeanName和BeanFactory)->初始化前->初始化->初始化后(AOP)
- 我们的Spring并没有实现完整的生命周期,所以很多并不完善。后面应该不会继续完善这个生命周期。以上的生命周期均已实现。
项目结构图
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
</dependencies>
- 1:cglib:用来做动态代理。Spring的底层亦是如此
- 2:dom4j:用来操作XML文件。dom4j也可以说是目前最好用的操作XML文件的工具了,且此项目中会大量用到。
MyClassPathXmlApplicationContext底层核心
细节-1
public class MyClassPathXmlApplicationContext implements ApplicationContext {
}
- 说明MyClassPathXmlApplicationContext是ApplicationContext的实现类,可以通过多态的方式创建对象
细节-2
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap;
private ConcurrentHashMap<String,Object> singletonMap;
private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors;
private List<Element> elements;
- beanDefinitionMap:bean定义Map
- singletonObjects:单例池,也就是Spring的一级缓存
- beanPostProcessors:beanPostProcessors集合
- elements:root结点下一个结点的集合
细节-3
String uri=this.getClass().getResource("/spring/"+configLocation).toURI().getPath();
String scp = uri.substring(1, uri.length());
SAXReader saxReader=new SAXReader();
Document document = saxReader.read(new File(scp));
Element rootElement = document.getRootElement();
if(!rootElement.getName().equals("my-beans")){
throw new NullPointerException("标签不正确");
}
ClassLoader classLoader = this.getClass().getClassLoader();
elements = rootElement.elements();
- 获取扫描xml文件,只扫描resources目录下的spring目录
- 通过SAX阅读器去加载XML配置文件,并获取该XML配置文件的root结点也就是my-beans标签
- 然后校验标签是否正确,如果标签不正确则抛出异常
- 通过当前对象的类去拿到类加载器(ClassLoader)
- 获取root结点的下一级结点的集合并且赋值给全局变量elements
细节-4
for (Element element : elements) {
if(!element.getName().equals("my-bean")){
throw new NullPointerException("标签不正确");
}
//beanName
String beanName=null;
//定义一个BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
List<Attribute> attributes = element.attributes();
for (Attribute attribute : attributes) {
if(attribute.getName().equals("id")){
beanName=attribute.getValue();
}else if(attribute.getName().equals("class")){
Class<?> aClass = classLoader.loadClass(attribute.getValue());
if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
BeanPostProcessor obj = (BeanPostProcessor) aClass.getConstructor().newInstance();
beanPostProcessors.add(obj);
}
beanDefinition.setType(aClass);
}else if(attribute.getName().equals("scope")){
beanDefinition.setScope(attribute.getValue());
}else {
throw new NullPointerException("属性不正确");
}
}
beanDefinitionMap.put(beanName,beanDefinition);
}
- 遍历root结点的下一级结点,这个结点其实就是我们定义的一个个bean
- 校验标签的正确性,不正确则抛出异常
- 定义局部变量beanName和BeanDefinition(存储bean所属类、单例还是多例)
- 获取root结点的下一级结点的属性,并进行遍历
- 通过attribute.getValue()给beanName赋值、classLoader.loadClass(attribute.getValue())给BeanDefinition的type赋值、和beanDefinition.setScope(attribute.getValue())
- 并且判断这个类是否实现BeanPostProcessor接口,如果有则单独创建一个对象放到beanPostProcessors集合中。
- 扫描到此结束
细节-5
ConcurrentHashMap.KeySetView<String, BeanDefinition> keySet = beanDefinitionMap.keySet();
//加载入单例池
for (String beanName : keySet) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
String scope = beanDefinition.getScope();
if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){
//不做任何事
}else {
if(!singletonObjects.containsKey(beanName)){
Object obj = createBean(beanName);
singletonObjects.put(beanName,obj);
}
}
}
- 遍历beanDefinitionMap,找出所有单例的BeanDefinition对象,然后将其加载入单例池。
细节-6
for (Element element : elements) {
String curBeanName = element.attributeValue("id");
xxx
}
- 遍历elements集合(二级标签),获取当前beanName,通过判断beanName(也就是需要创建bean的bean名)和curBeanName(当前遍历到的bean名相比较)找出需要创建的bean。
细节-7
List<Element> els = element.elements();
for (Element el : els) {
if(el.getName().equals("my-properties")){
if(el.element("my-ref")!=null){
String fieldName = el.attributeValue("name");
String bn = el.element("my-ref").attributeValue("bean");
Field field = aClass.getDeclaredField(fieldName);
field.setAccessible(true);
if(singletonObjects.containsKey(bn)){
Object beanObj = singletonObjects.get(bn);
field.set(obj,beanObj);
}
else{
Object createBean = createBean(bn);
field.set(obj,createBean);
}
}else{
List<Attribute> attributes = el.attributes();
//用于保存注入的字段
String field=null;
//用于保存注入的值
String val=null;
for (Attribute attribute : attributes) {
if(attribute.getName().equals("name")){
field=attribute.getValue();
}else if(attribute.getName().equals("value")){
val=attribute.getValue();
}else {
throw new NullPointerException("请检查定义bean的属性");
}
}
Field fd = aClass.getDeclaredField(field);
fd.setAccessible(true);
String type = fd.getType().getName().toLowerCase();
if(type.equals("byte")||type.equals("java.lang.byte")){
fd.set(obj,Byte.parseByte(val));
}else if(type.equals("short")||type.equals("java.lang.short")){
fd.set(obj,Short.parseShort(val));
}else if(type.equals("int")||type.equals("java.lang.integer")){
fd.set(obj,Integer.parseInt(val));
}else if(type.equals("long")||type.equals("java.lang.long")){
fd.set(obj,Long.parseLong(val));
}else if(type.equals("double")||type.equals("java.lang.double")){
fd.set(obj,Double.parseDouble(val));
}else if(type.equals("float")||type.equals("java.lang.float")){
fd.set(obj,Float.parseFloat(val));
}else if(type.equals("char")){
fd.set(obj,val.charAt(0));
}else if(type.equals("boolean")||type.equals("java.lang.boolean")){
if(val.equals("true")){
fd.set(obj,true);
}else{
fd.set(obj,false);
}
}else if(type.equals("java.lang.string")){
fd.set(obj,val);
}
else{
throw new NullPointerException("暂不支持这个字段类型");
}
}
}else {
throw new NullPointerException("请检查注入属性的标签");
}
}
- 获取当前二级标签(my-bean)的第三级标签(my-properties)集合
- 遍历这个三级标签集合,判断这个标签的子标签是否是my-ref标签
- 如果含有my-ref标签的话,则通过bn去单例池里面找是否有这个对象,单例池如果有的话则用单例池的;
- 单例池如果没有则直接调用createBean方法创建对象并赋值
- 如果没有my-ref标签的话,就拿出他的name和value属性,通过这个name匹配它的实体类中字段名为这个name的字段。
- 为了防止注入报错,我们对注入的所有基本数据类型+String进行判断(变小写再判断)
- 然后接下来就是Aware回调和初始化前、初始化、初始化后、如果是单例则放入单例池的操作,这里省略了不写了。
MyClassPathXmlApplicationContext源码
package com.springframework.core.context;
import com.springframework.core.constant.ScopeType;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
/**
* 纯手写ClassPathXmlApplicationContext
* @author 游政杰
* TODO: 2022/3/22
*/
//说明MyClassPathXmlApplicationContext是ApplicationContext的实现类,可以通过多态的方式创建对象
public class MyClassPathXmlApplicationContext implements ApplicationContext {
//bean定义Map
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap;
//单例池,也就是Spring的一级缓存
private ConcurrentHashMap<String,Object> singletonObjects;
//beanPostProcessors集合
private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors;
//root结点下一个结点的集合
private List<Element> elements;
//私有化构造器
private MyClassPathXmlApplicationContext(){
}
public MyClassPathXmlApplicationContext(String configLocation){
this.beanDefinitionMap=new ConcurrentHashMap<>();
this.singletonObjects=new ConcurrentHashMap<>();
this.beanPostProcessors=new CopyOnWriteArrayList<>();
try {
//获取扫描xml文件,只扫描resources目录下的spring目录
String uri=this.getClass().getResource("/spring/"+configLocation).toURI().getPath();
String scp = uri.substring(1, uri.length());
//通过SAX阅读器去加载XML配置文件,并获取该XML配置文件的root结点也就是my-beans标签
//然后校验标签是否正确,如果标签不正确则抛出异常
//通过当前对象的类去拿到类加载器(ClassLoader)
//获取root结点的下一级结点的集合并且赋值给全局变量elements
//创建SAX阅读器
SAXReader saxReader=new SAXReader();
//加载XML文件
Document document = saxReader.read(new File(scp));
//获取root结点
Element rootElement = document.getRootElement();
//校验标签的正确性
if(!rootElement.getName().equals("my-beans")){
throw new NullPointerException("标签不正确");
}
//获取类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//获取root结点的下一级结点的集合
elements = rootElement.elements();
//遍历root结点的下一级结点,这个结点就是我们定义的一个个bean
for (Element element : elements) {
//校验标签的正确性
if(!element.getName().equals("my-bean")){
throw new NullPointerException("标签不正确");
}
//beanName
String beanName=null;
//定义一个BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
//获取root结点的下一级结点的属性
List<Attribute> attributes = element.attributes();
//root结点的下一级结点的属性的值
for (Attribute attribute : attributes) {
//分别找出id、class、scope属性
if(attribute.getName().equals("id")){
beanName=attribute.getValue();
}else if(attribute.getName().equals("class")){
Class<?> aClass = classLoader.loadClass(attribute.getValue());
//判断这个类是否实现BeanPostProcessor
if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
BeanPostProcessor obj = (BeanPostProcessor) aClass.getConstructor().newInstance();
beanPostProcessors.add(obj);
}
beanDefinition.setType(aClass);
}else if(attribute.getName().equals("scope")){
beanDefinition.setScope(attribute.getValue());
}else {
throw new NullPointerException("属性不正确");
}
}
beanDefinitionMap.put(beanName,beanDefinition);
}
//扫描结束
ConcurrentHashMap.KeySetView<String, BeanDefinition> keySet = beanDefinitionMap.keySet();
//加载入单例池
for (String beanName : keySet) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
String scope = beanDefinition.getScope();
if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){
//不做任何事
}else {
if(!singletonObjects.containsKey(beanName)){
Object obj = createBean(beanName);
singletonObjects.put(beanName,obj);
}
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition==null){
return null;
}else {
String scope = beanDefinition.getScope();
//如果是多例
if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){
return createBean(beanName);
}else {//如果是单例的话先去单例池中找
Object obj = singletonObjects.get(beanName);
//如果单例池没有
if(obj==null){
obj=createBean(beanName);
}else {//单例池有的话直接返回obj
return obj;
}
}
}
return null;
}
private Object createBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition==null){
throw new NullPointerException("创建bean失败");
// return null;
}else {
try {
//依赖注入,二级标签,这既是一个个bean
for (Element element : elements) {
//遍历获取当前beanName
String curBeanName = element.attributeValue("id");
if(curBeanName.equals(beanName)){
Class<?> aClass = beanDefinition.getType();
//反射创建对象
Object obj = aClass.getConstructor().newInstance();
//注入的属性集,也就是第三级标签
List<Element> els = element.elements();
for (Element el : els) {
//只有这种情况才合法,因为我们目前只支持my-properties标签注入
if(el.getName().equals("my-properties")){
//判断这个标签的子标签是否是my-ref标签
if(el.element("my-ref")!=null){
String fieldName = el.attributeValue("name");
String bn = el.element("my-ref").attributeValue("bean");
Field field = aClass.getDeclaredField(fieldName);
field.setAccessible(true);
//通过bn去单例池里面找是否有
if(singletonObjects.containsKey(bn)){
//单例池如果有则用单例池的
Object beanObj = singletonObjects.get(bn);
field.set(obj,beanObj);
}
else{
//直接调用createBean方法创建对象并赋值
Object createBean = createBean(bn);
field.set(obj,createBean);
}
}else{
List<Attribute> attributes = el.attributes();
//用于保存注入的字段
String field=null;
//用于保存注入的值
String val=null;
for (Attribute attribute : attributes) {
if(attribute.getName().equals("name")){
field=attribute.getValue();
}else if(attribute.getName().equals("value")){
val=attribute.getValue();
}else {
throw new NullPointerException("请检查定义bean的属性");
}
}
//此时进行依赖注入,必须要是getDeclaredField方法
Field fd = aClass.getDeclaredField(field);
fd.setAccessible(true);
//为了防止注入报错,我们对注入的所有基本数据类型+String进行判断(变小写再判断)
String type = fd.getType().getName().toLowerCase();
if(type.equals("byte")||type.equals("java.lang.byte")){
fd.set(obj,Byte.parseByte(val));
}else if(type.equals("short")||type.equals("java.lang.short")){
fd.set(obj,Short.parseShort(val));
}else if(type.equals("int")||type.equals("java.lang.integer")){
fd.set(obj,Integer.parseInt(val));
}else if(type.equals("long")||type.equals("java.lang.long")){
fd.set(obj,Long.parseLong(val));
}else if(type.equals("double")||type.equals("java.lang.double")){
fd.set(obj,Double.parseDouble(val));
}else if(type.equals("float")||type.equals("java.lang.float")){
fd.set(obj,Float.parseFloat(val));
}else if(type.equals("char")){
fd.set(obj,val.charAt(0));
}else if(type.equals("boolean")||type.equals("java.lang.boolean")){
if(val.equals("true")){
fd.set(obj,true);
}else{
fd.set(obj,false);
}
}else if(type.equals("java.lang.string")){
fd.set(obj,val);
}
else{
throw new NullPointerException("暂不支持这个字段类型");
}
}
}else {
throw new NullPointerException("请检查注入属性的标签");
}
}
//依赖注入完成之后
//判断对象是否需要Aware回调
if(obj instanceof Aware){
//再具体判断是什么回调
if(obj instanceof BeanNameAware){
((BeanNameAware) obj).setBeanName(beanName);
}
}
//初始化前
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
beanPostProcessor.postProcessorBeforeInitialization(obj,beanName);
}
//初始化
if(obj instanceof InitializingBean){
((InitializingBean) obj).afterPropertiesSet();
}
//初始化后
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
beanPostProcessor.postProcessorAfterInitialization(obj,beanName);
}
//如果是单例就放入单例池
if(beanDefinition.getScope()==null||beanDefinition.getScope().equals(ScopeType.SINGLETON)){
singletonObjects.put(beanName,obj);
}
return obj;
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
//如果找不到就会返回null
return null;
}
}
}
使用教程
xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 手写Spring基于XML版 -->
<my-beans>
<my-bean id="orderBean1" class="com.springframework.core.service.impl.OrderServiceImpl" >
<my-properties name="aByte" value="111"/>
<my-properties name="aShort" value="222"/>
<my-properties name="aInt" value="333"/>
<my-properties name="aLong" value="6666"/>
<my-properties name="aDouble" value="123.123"/>
<my-properties name="aFloat" value="233.567"/>
<my-properties name="aChar" value="c"/>
<my-properties name="aBoolean" value="true"/>
<my-properties name="aString" value="8大基本数据类型与String的注入测试"/>
</my-bean>
<my-bean id="orderBean2" class="com.springframework.core.service.impl.OrderServiceImpl" scope="prototype">
<my-properties name="aByte" value="111"/>
<my-properties name="aShort" value="222"/>
<my-properties name="aInt" value="333"/>
<my-properties name="aLong" value="6666"/>
<my-properties name="aDouble" value="123.123"/>
<my-properties name="aFloat" value="233.567"/>
<my-properties name="aChar" value="c"/>
<my-properties name="aBoolean" value="true"/>
<my-properties name="aString" value="8大基本数据类型与String的注入测试---prototype"/>
</my-bean>
<my-bean id="userBean1" class="com.springframework.core.service.impl.UserServiceImpl" scope="singleton">
<my-properties name="orderServiceImpl">
<my-ref bean="orderBean2"/>
</my-properties>
</my-bean>
<my-bean id="myBeanPostProcess" class="com.springframework.core.service.impl.MyBeanPostProcess" scope="singleton">
</my-bean>
</my-beans>
- 点赞
- 收藏
- 关注作者
评论(0)