Spring源码系列:初探底层,手写Spring

大模型数据库容器

picture.image

努力的小雨

读完需要

16 分钟 速读仅需 6 分钟

/ 前言 /

在学习 Spring 框架源码时,记住一句话:源码并不难,只需要给你各种业务场景或者项目经理,你也能实现自己的 Spring。虽然你的实现可能无法与开源团队相媲美,但是你肯定可以实现一个 0.0.1 版本。因此,初次阅读源码时,不要陷入太深的细节中。先了解大体逻辑,再仔细研读。

/ 实现功能 /

本文将带领大家实现一个简易版的 Spring 框架,并介绍以下功能点:

  1. 了解 Spring 的底层源码启动过程

  2. 了解 BeanDefinition 的概念

  3. 了解 Spring 解析配置类等底层源码工作流程

  4. 了解依赖注入,Aware 回调等底层源码工作流程

  5. 了解 Spring AOP 的底层源码工作流程

以上功能点将使我们对 Spring 框架的实现有所了解,但我们并不会一下子实现整个 Spring 框架的业务。我们将从上述功能点入手,通过手写模拟 Spring 框架来实现这些功能。

首先,我们像使用 Spring 一样,传入配置类获取 applicationContext,再通过 getBean 方法获取具体对象。最后,我们调用方法并打印日志。如果你对这些基本流程不熟悉,可以查看我的入门系列文章:Spring 入门系列:浅析知识点 ( https://www.cnblogs.com/guoxiaoyu/p/17301294.html )

详细流程如下:

  1. 解析配置类上的 ComponentScan 注解,获取扫描的基本路径

  2. 开始解析各个被扫描到的文件,是否是需要被 Spring 管理,如果是则暂存到 list 集合中

  3. 开始遍历被 Spring 管理 list 集合,解析各个类上的注解,比如是否是懒加载,然后将这些属性都封装到 applicationContext 中的以 beanName 为 key 的 BeanDefineMap 中

  4. 针对已经解析好的 bean 定义进行创建对象并实例化,并将其放入以 beanName 为 key 的 singletonMap 实例化缓存池中。

  5. 在实例化时,如果发现有依赖注入的对象,则将实例化缓存池中的对象存入。如果缓存池中没有该对象,则进行创建后再注入。

  6. 判断对象是否实现了各个 Aware 接口,如果实现,则进行回调。

  7. 判断对象是否属于增强类。在这里,我们模拟了事务注解。如果有事务注解,则创建一个代理对象,并为所有方法增加拦截器。然后将该代理对象存入单例缓存池中。

/ 详细解析 /

1

项目结构

基本路径:com.user 目录

各个注解及上下文类:config 目录

需要被管理的 Bean:service 目录

启动类:com.user 根目录

picture.image

2

源码分析

  
@Component  
public class UserService implements ApplicationContextAware, BeanNameAware {  
  
 @AutoWired  
 ServiceDemo serviceDemo;  
  
 private XiaoyuApplicationContext applicationContext;  
 private String beanName;  
  
 public void test() {  
 serviceDemo.say();  
// System.out.println(serviceDemo);  
 System.out.println("userService:"+applicationContext.getBean("userService"));  
 System.out.println("beanName:"+beanName);  
 }  
  
 @Override  
 public void setApplicationContext(XiaoyuApplicationContext applicationContext) {  
 this.applicationContext = applicationContext;  
 }  
  
 @Override  
 public void setBeanName(String beanName) {  
 this.beanName = beanName;  
 }  
}

UserService 类主要用于测试是否 Spring 已经管理了相关对象并生成了代理对象,是我们的日常业务类,我们仔细看下 XiaoyuApplicationContext 类,主要的流程在这边:

  
public class XiaoyuApplicationContext {  
  
  
 //配置类  
 private Class config;  
 //初始的bean定义  
 private Map<String,BeanDefinition> beanDefineMap = new HashMap<>();  
 //单例缓存池  
 private Map<String,Object> singleBean = new HashMap<>();  
  
 public XiaoyuApplicationContext(Class myDemoConfigClass) {  
 config = myDemoConfigClass;  
 //解析配置类  
 scan();  
  
 }  
  
 public void scan(){  
 ComponentScan declaredAnnotation = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);  
 String value = declaredAnnotation.basePackages();  
 doScan(value);  
 //将bean定义Map生成具体的Bean对象  
 beanDefineMap.entrySet().stream().forEach(item->{  
 String beanName = item.getKey();  
 BeanDefinition beanDefinition = item.getValue();  
 if (!beanDefinition.isLazy() && "singleton".equals(beanDefinition.getScope())) {  
 Object bean = createBean(beanName);  
 singleBean.put(beanName,bean);  
 }  
 });  
 }  
  
 /**  
 * 解析配置类  
 */  
 private void doScan(String value) {  
 String path = value.replace(".","/");  
 //正常走文件解析  
 ClassLoader classLoader = this.getClass().getClassLoader();  
 URL resource = classLoader.getResource(path);  
 File file = new File(resource.getFile());  
  
 List<File> classFile = new ArrayList<>();  
 //简单点直接双层解析即可  
 if (file.isDirectory()) {  
 for (File f : file.listFiles()) {  
 if (f.isDirectory()) {  
 for (File f1 : f.listFiles()) {  
 if (!f1.isDirectory()) {  
 classFile.add(f1);  
 }  
 }  
 } else {  
 classFile.add(f);  
 }  
 }  
 }  
 //遍历所有解析文件  
 for (File cFile : classFile) {  
 String absolutePath = cFile.getAbsolutePath();  
 String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))  
 .replace("\\", ".");  
  
 try {  
 Class<?> clazz = classLoader.loadClass(className);  
 //是否需要被Spring管理  
 if (clazz.isAnnotationPresent(Component.class)) {  
 //将bean上的注解封装到bean定义中  
 BeanDefinition beanDefinition = new BeanDefinition();  
 beanDefinition.setType(clazz);  
 beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));  
 if (clazz.isAnnotationPresent(Scope.class)) {  
 beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());  
 } else {  
 beanDefinition.setScope("singleton");  
 }  
  
 String beanName = clazz.getAnnotation(Component.class).value();  
 if (beanName.isEmpty()) {  
 //如果不设置beanName会默认生产唯一一个name,Spring底层也是这样做的  
 beanName = Introspector.decapitalize(clazz.getSimpleName());  
 }  
  
 beanDefineMap.put(beanName, beanDefinition);  
  
 }  
 } catch (ClassNotFoundException e) {  
 e.printStackTrace();  
 }  
  
 }  
 }  
  
 public Object createBean(String beanName){  
  
 BeanDefinition beanDefinition = beanDefineMap.get(beanName);  
 Class type = beanDefinition.getType();  
  
 try {  
 Object instance = type.newInstance();  
 //属性填充,依赖注入  
 populateBean(instance);  
  
 if (instance instanceof ApplicationContextAware){  
 ((ApplicationContextAware) instance).setApplicationContext(this);  
 }  
 if (instance instanceof BeanNameAware) {  
 ((BeanNameAware) instance).setBeanName(beanName);  
 }  
 //是否需要AOP增强  
 if (type.isAnnotationPresent(Transaction.class)) {  
 Enhancer enhancer = new Enhancer();  
 enhancer.setSuperclass(type);  
 //简单的方法切面  
 enhancer.setCallback(new MethodInterceptor() {  
 @Override  
 public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
 //开启事务,关闭自动提交  
 System.out.println("事务已开启");  
 Object res = method.invoke(instance, objects);  
 //提交事务  
 System.out.println("事务已提交");  
 return res;  
 }  
 });  
 return enhancer.create();  
 }  
 return instance;  
 } catch (InstantiationException e) {  
 e.printStackTrace();  
 } catch (IllegalAccessException e) {  
 e.printStackTrace();  
 }  
  
 return null;  
 }  
  
 private void populateBean(Object instance) {  
 Field[] declaredFields = instance.getClass().getDeclaredFields();  
  
 Arrays.stream(declaredFields).forEach(item->{  
 //寻找注入点  
 if (item.isAnnotationPresent(AutoWired.class)) {  
 Object bean = getBean(item.getName());  
 item.setAccessible(true);  
 try {  
 item.set(instance,bean);  
 } catch (IllegalAccessException e) {  
 e.printStackTrace();  
 }  
 }  
 });  
  
 }  
  
  
 public Object getBean(String beanName){  
 if (!beanDefineMap.containsKey(beanName)) {  
 throw new NullPointerException();  
 }  
 if ("singleton".equals(beanDefineMap.get(beanName).getScope())) {  
 if (singleBean.containsKey(beanName)) {  
 return singleBean.get(beanName);  
 } else {  
 Object bean = createBean(beanName);  
 singleBean.put(beanName,bean);  
 return bean;  
 }  
 }  
 return createBean(beanName);  
 }  
}

以上即为整个流程的基本梳理。我们在实现过程中没有涉及 Bean 循环依赖以及其他各种创建缓存,但 Spring 在实现 Bean 的创建过程中确实用到了各种本地缓存和同步锁(synchronized)。在学习源码时,不要太关注这些细节。首先要理解整个流程,再深入研究。

/ 结语 /

最后,我们在 gitee 上提供了项目源码。如果需要,可以查看 spring-xiaoyu ( https://gitee.com/guoxiaoyu/spring-xiaoyu.git )。虽然我认为写一遍自己的代码更好,因为这是最简单的流程,有助于理解 Spring 源码。

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
vivo 容器化平台架构与核心能力建设实践
为了实现规模化降本提效的目标,vivo 确定了基于云原生理念构建容器化生态的目标。在容器化生态发展过程中,平台架构不断演进,并针对业务的痛点和诉求,持续完善容器化能力矩阵。本次演讲将会介绍 vivo 容器化平台及主要子系统的架构设计,并分享重点建设的容器化核心能力。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论