栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring的轻量级实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring的轻量级实现

作者: Grey

原文地址:Spring的轻量级实现

源码

github

使用TDD开发方法

示例

  1. 写一个测试用例
  2. 运行:失败
  3. 写Just enough的代码,让测试通过
  4. 重构代码保持测试通过,

然后循环往复。

说明
  • 本文是参考公众号:码农翻身 的从零开始造Spring 教程的学习笔记
  • 仅实现核心功能
  • 基于spring-framework-3.2.18.RELEASE版本
  • 日志和异常处理待完善

Step1 通过XML实例化一个对象

解析XML文件,拿到Bean的id和完整路径,通过反射方式实例化一个对象。

tag: step1

Step2 基础工作
  • 日志支持:log4j2 + SLF4j
  • 异常处理

所有异常的顶层:BeansException

Step3 封装BeanDefinition

DefaultBeanFactory中的BEAN_MAP目前只包括了beanClassName信息,后续如果要扩展其他的信息,肯定需要增加字段,所以我们需要抽象出一个接口,方便后续扩展其他的字段。

Step4 封装Resource

在BeanFactory初始化的时候,传入的是XML格式的配置信息,比如bean-v1.xml, Spring会把这个抽象成一个Resource,常见Resource有
FileSystemResource->从文件地址读配置
ClassPathResource->从classpath下读配置
BeanFactory在创建Bean的时候,只关注Resource即可。

tag: step-4-2-resource

Step5 封装XML的解析逻辑到专门的一个类中

XmlBeanDefinitionReader
用于解析XML,传入Resource,即可获取所有BeanDefinition
由于要把BeanDefinition放入BEAN_MAP中,所以XmlBeanDefinitionReader需要持有一个DefaultBeanFactory,且DefaultBeanFactory需要有注册BeanDefinition和获取BeanDefintion的能力,这样DefaultBeanFactory的职责就不单一了,所以需要抽象出一个BeanDefinitionRegistry,这个BeanDefinitionRegistry负责注册BeanDefinition和获取BeanDefintion的能力,XmlBeanDefinitionReader持有BeanDefinitionRegistry即可将解析生成的BeanDefinition注入BEAN_MAP中。

修改BeanFactoryV1Test这个测试用例,重新执行测试

tag: vstep5-final

Step6 单例多例模式的配置实现

测试:BeanFactoryV1Test:testSingletonBean,testGetPrototypeBean

解析XML的XmlBeanDefinitionReader需要增加scope的解析逻辑
BeanDefinition这个数据结构增加是否单例,是否多例的属性
DefaultBeanFactory中getBean的时候,判断是否单例,如果是单例,则复用对象,如果是多例,则new新的对象。
抽象SingletonBeanRegistry这个接口,用于注册和获取单例对象
DefaultSingletonBeanRegistry实现这个接口

tag:vstep6-scope

Step7 整合,抽象ApplicationContext

我们使用Spring的时候,一般是这样做的:

ApplicationContext ctx = new ClassPathXmlApplicationContext("mycontainer.xml");
UserService userService = (UserService) ctx.getBean("userService");

ApplicationContext ctx = new FileSystemApplicationContext("src\test\resources\bean-v1.xml");
UserService userService = (UserService) ctx.getBean("userService");

现在,我们需要抽象出ApplicationContext这个类来实现如上的功能
ApplicationContext

  • ClassPathXmlApplicationContext(从classpath中读配置文件)
  • FileSystemApplicationContext(从文件目录中读取配置文件)
  • ....

tag: vstep7-applicationcontext-v1

通过观察发现,ClassPathXmlApplicationContext和FileSystemApplicationContext大部分代码都是相同的,只有在获取Resource的时候,方法不一样,所以,我们通过模板方法这个设计模式,来抽象出公有方法到一个抽象类,所有ClassPathXmlApplicationContext和FileSystemApplicationContext都实现这个抽象类。

tag: vstep7-applicationcontext-v2

Step8 注入Bean和字符串常量

形如:




    
        
        
        
        
        
    

    
    
    
    

达到的目的就是,可以把“整型,字符串类型,简单对象类型”注入到一个Bean中。
这里我们需要解决以下几个问题:

  1. 把字符串转成各种各样的Value
    1. String->Integer/int
    2. String->Boolean/boolean
    3. ......

注:以上转换器的实现都是基于jdk中java.bean包中的PropertyEditorSupport这个类来完成的。

  • CustomBooleanEditor
  • CustomNumberEditor
  • ....

每种类型的转换都通过类似的方式实现,然后Spring抽象出了TypeConvert这个接口,并把这些转换器加入一个特定的Map中,Map的key就是要转换的目标的类型,Value就是对应的转换器的实现类,即可实现类型转换。

  1. 调用Bean的setXXX方法把这些Value值set到目标Bean中
    1. 抽象出PropertyValue
    2. BeanDefiniton需要增加方法获取PropertyValue
    3. GenericBeanDefinition中需要增加List
    4. XmlBeanDefinitionReader解析XML的时候,把List 识别出来并加入BeanDefinition中(RuntimeBeanReference,TypedStringValue)
    5. BeanDefinitionValueResolver 把对应的PropertyValue给初始化好
    6. **setXXX的背后实现利用的是jdk原生:java.beans.Introspector 来实现 **见(DefaultBeanFactory的populateBean方法)

tag: vstep8-inject

Step9 构造器注入

处理形如以下的配置:


        
        
        
    

    
    
    
    

参考测试:
ApplicationContextTestV3,在v3版本的UserService中,定义两个构造方法,需要识别出正确的构造方法。

  1. 和Step8中类似,抽象出ConstructorArgument用于表示一个构造函数信息,每个BeanDefinition中持有这个对象
  2. XmlBeanDefinitionReader需要负责解析出ConstuctorArgument(parseConstructorArgElements)
  3. DefaultBeanFactory通过指定构造函数来生成Bean对象(ConstructorResolver注入Bean实例到构造方法中)

注:这里指定的构造函数的查找逻辑为:解析出XML的构造函数的参数列表,和通过反射拿到对应的构造函数的参数列表进行对比(每个参数的类型和个数必须一样)

tag:vstep9-constructor

Step10 实现注解

实现两个注解:@Component @Autowired(只针对属性注入,暂时不考虑方法注入)
且需要实现如下的XML的解析:










  1. 定义注解Component ,Autowired
  2. 需要实现的第一个功能是:给一个包名,扫描获取到这个包以及子包下面的所有Class【PackageResourceLoaderTest】包名--> *.class 【涉及一个递归调用】
  3. 由于第二步中的BeanDefinition不像之前的xml那样,会对Bean配置一个id,所以,这里解析出来的Bean定义需要自动生成一个BeanId(默认先取value的配置,否则就就是类名第一个字母小写,抽象BeanNameGenerator来专门对Bean定义ID),同时,Spring中单独新建了一个AnnotatedBeanDefinition接口来定义包含注解的BeanDefinition, 封装第二步中的方法+BeanId的生成到一个类中:【ClassPathBeanDefinitionScannerTest】。
  4. 实现了第3步以后,我们得到了对应的Class文件,我们需要通过某种方式去解析这个Class文件,拿到这个Class中的所有信息,特别是注解信息。使用ASM这个jar【ASM的原始用法见:ClassReaderTest】 *.class -> class Info
  5. 步骤3虽然实现了读取Class中的信息这件事。但是用ASM的原生方式解析不太方便,解析ClassmetaData和Annotation都需要定义一个Visitor,所以Spring抽象了一个接口(metadataReader)来封装ASM的实现【metadataReaderTest】
  6. 拿到Bean中的所有Field(带注解的),并把他实例化成一个对象 : class info 中的Field -> new instance()【DependencyDescriptorTest】
  7. 将这个对象注入目标Bean中,new Instance() ——注入——>bean 【InjectionmetadataTest】
  8. 处理XML解析,注入ScannerBeanDefinition
  9. 整合,涉及到Bean初始化和Bean的生命周期【AutowiredAnnotationProcessorTest】


tag:vstep10-annotation-final

step11 实现Aop

	

	
	
	
		
			
			
				
					
		
	

  1. 一些术语:Joint Point, Pointcut, Advice(拦截器,Before, After, Around....)
  2. 为什么要用aop:日志,安全,事务 作为切面和业务代码正交
  3. 运行时动态生成类

  1. PointcutTest: 给定一个表达式,然后判断某个类的某个方法是不是匹配这个表达式【依赖AspectJ】
    1. Pointcut:
    2. MethodMatcher: 给一个method,判断是否满足指定条件
    3. AspectJexpressionPointcut
  2. MethodLocatingFactoryTest:通过Bean的名称("tx")和方法名("start")定位到这个Method,然后反射调用这个Method
  3. 指定指定顺序的链式调用 (Aop alliance,使用了责任链这个设计模式) ReflectiveMethodInvocationTest,需要debug查看调用链路。

  1. 动态代理,在一个方法前后增加一些逻辑,而不用改动原始代码。CGlibTest, 其中testFilter方法是表示支持多个aop操作,(使用)

    1. 普通类:CGLib
    2. 接口:Jdk自带
  2. CglibAopProxyTest

tag: vaop-v1
  1. 抽象AbstractV5Test
  2. BeanFactoryTestV5:配置文件->Advice
  3. XML解析生成BeanDefinition
    1. aop标签的内容其实是包含在GenericDefinitionBean里面, 通过人工合成的,嵌套的Beandefinition处理
  4. BeandefinitionTestV5

ConfigBeanDefinitionParser.java

  1. BeanFactoryTestV5

嵌套Bean的处理

  1. ApplicationContextTest5

tag: vaop-v2
  1. 实现jdk动态代理

AspectJAutoProxyCreator
JdkAopProxyFactory

tag:vaop-v3

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/323052.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号