Spring源码的解析

张开发
2026/4/12 7:52:51 15 分钟阅读

分享文章

Spring源码的解析
大家在spring框架中都知道加注解还有ioc容器还有一开始就会加载配置类然后自动创建对象放入ioc中这些是怎么完成的呢下面我们来自己定义一下spring先来创建主启动类package com.code.liyunmiao.service; import com.code.liyunmiao.spring.liyunmiaocontext; public class Test { public static void main(String[] args) { liyunmiaocontext context new liyunmiaocontext(configclass.class); Userservice userservice(Userservice) context.getBean(Userservice); } }liyunmiaocontext就是容器类package com.code.liyunmiao.spring; import com.code.liyunmiao.service.Userservice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class liyunmiaocontext { private Class configclass; public liyunmiaocontext(Class configclass) { this.configclassconfigclass; if(configclass.isAnnotationPresent(ComponentScan.class)) { ComponentScan componentScan(ComponentScan)configclass.getAnnotation(ComponentScan.class); String valuecomponentScan.value(); } } public Object getBean(String beanname) { return null; } }当在主启动类里面run的时候也就是 liyunmiaocontext context new liyunmiaocontext(configclass.class); 这一步就是相当于new这个容器然后也会正常的去执行他的构造函数也就是对应类的构造函数就会执行以及父类构造函数链。再看配置类package com.code.liyunmiao.service; import com.code.liyunmiao.spring.ComponentScan; ComponentScan(com.code.liyunmiao.service) public class configclass { }配置类上面componentscan决定了要扫描哪些目录文件下。自定义component注解package com.code.liyunmiao.spring; public interface Component { String value() default ; }自定义componentscan注解package com.code.liyunmiao.spring; public interface ComponentScan { String value() default ; }userService类package com.code.liyunmiao.service; import com.code.liyunmiao.spring.Component; Component(Userservice) public class Userservice { }下面直接来看完整版这是容器package com.code.liyunmiao.spring; import com.code.liyunmiao.service.Definationbean; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.security.KeyStore; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 手写版「应用上下文」从配置类读取 {link ComponentScan}在 classpath 上找到对应包目录 * 遍历其中的 .class再用 {link Component} 判断是否注册为 Bean并把「Bean 定义」放进 map思路接近 Spring 的组件扫描。 */ public class liyunmiaocontext { /** 传入的配置类如 configclass.class作为读取 ComponentScan 的入口。 */ private Class configclass; /** * Bean 定义表key 为 {link Component#value()}Bean 名称value 为封装了类型、作用域的 {link Definationbean}。 * 扫描阶段只登记定义真正创建实例在 {link #getBean(String)} 中按作用域处理。 */ private final ConcurrentHashMapString, Definationbean map new ConcurrentHashMap(); /** * 单例 Bean 的已创建实例缓存仅当 {link Definationbean#getScope()} 为 singleton 时使用。 */ private final ConcurrentHashMapString, Object singletonObjects new ConcurrentHashMap(); public liyunmiaocontext(Class configclass) { this.configclass configclass; // 仅在配置类上声明了 ComponentScan 时才做包扫描注解需带 Retention(RUNTIME) 才能在运行时被反射读到。 if (configclass.isAnnotationPresent(ComponentScan.class)) { // 取出 ComponentScan(com.xxx.yyy) 的 value要扫描的根包点分包名。 ComponentScan componentScan (ComponentScan) configclass.getAnnotation(ComponentScan.class); String path componentScan.value(); // ClassLoader#getResource 使用「目录式」路径包名中的 . 换成 /对应磁盘上 com/xxx/yyy 层级。 path path.replace(., /); // 用加载本类的 ClassLoader在 classpath 上解析该包对应的 URL再转成 File 以便列目录。 ClassLoader classLoader liyunmiaocontext.class.getClassLoader(); // 部分环境下目录资源需以 / 结尾才能解析到 URL故先尝试 path再尝试 path/。 URL resource classLoader.getResource(path); if (resource null !path.endsWith(/)) { resource classLoader.getResource(path /); } if (resource null) { throw new IllegalStateException(classpath 上未找到包目录请检查 ComponentScan 的 value 是否与编译输出目录一致: path); } File file new File(resource.getFile()); // 若是真实目录开发时常见target/classes 下可列出该包内所有条目若在 jar 内则需另行处理JarURLConnection 等。 if (file.isDirectory()) { File[] files file.listFiles(); if (files null) { return; } // 只遍历当前包目录下「一层」条目子包子目录需递归或其它扫描方式才能覆盖。 for (File f : files) { String filename f.getAbsolutePath(); if (filename.endsWith(.class)) { // filename 是绝对路径如 ...\classes\com\code\...\Foo.class。 // ClassLoader#loadClass 需要的是 JVM「二进制类名」如 com.code.xxx.Foo不是磁盘路径 // 此处从路径中截取从 com 开始到 .class 前的片段再替换为点分全限定名。 // 注意依赖路径中首次出现的 com 即包名起点包名不以 com 开头或路径含多个 com 时可能截错。 String classname filename.substring(filename.indexOf(com), filename.lastIndexOf(.class)); classname classname.replace(\\, .); try { // 变量名仅作占位此处得到的是「类元数据」后续用其读 Component、Scope。 Class? Class classLoader.loadClass(classname); // 仅当类上标了 Component 时才登记为 Bean 定义。 if (Class.isAnnotationPresent(Component.class)) { Component component Class.getAnnotation(Component.class); String beanname component.value(); Definationbean definationbean new Definationbean(); definationbean.setType(Class); // 若有 Scope 则取注解值否则与 Spring 类似默认为 singleton。 if (Class.isAnnotationPresent(Scope.class)) { Scope scope Class.getAnnotation(Scope.class); definationbean.setScope(scope.value()); } else { definationbean.setScope(singleton); } map.put(beanname, definationbean); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } } // 扫描结束后对作用域为 singleton 的 Bean 可在此「预创建」并放入 singletonObjects与 Spring 预实例化单例思路类似。 for (Map.EntryString, Definationbean entry : map.entrySet()) { String beanname entry.getKey(); Definationbean definationbean entry.getValue(); if (entry.getValue().getScope().equals(singleton)) { Object bean creatbean(beanname, definationbean); singletonObjects.put(beanname, bean); } } } } } /** * 根据 Bean 名称与定义创建实例具体创建方式由你自行实现反射无参构造、工厂等。 */ private Object creatbean(String beanname, Definationbean definationbean) { //通过反射也就是拿到calss类元信息然后进行创建对象这一过程称之为反射对象的创建 Class clazz definationbean.getType(); try { Object bean clazz.getConstructor().newInstance(); return bean; } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } /** * 按名称从容器中获取 Bean 实例先查 {link #map} 得类型与作用域 * singleton 则复用 {link #singletonObjects} 中已创建实例否则每次反射新建prototype 等。 */ public Object getBean(String beanname) { // 未登记过该名称则无定义直接返回 null。 Definationbean definationbean map.get(beanname); if (definationbean null) { return null; } // singleton优先用缓存缓存没有则创建后放入 map 并返回新建实例注意应 return 新建对象勿 return 旧的 null 引用。 if (definationbean.getScope().equals(singleton)) { Object bean singletonObjects.get(beanname); if (bean null) { Object bean2 creatbean(beanname, definationbean); singletonObjects.put(beanname, bean2); return bean2; } return bean; } // 非 singleton如 prototype每次 getBean 都新建不放入 singletonObjects。 return creatbean(beanname, definationbean); } }自定义scope注解package com.code.liyunmiao.spring; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * 作用域手写简化版标在类上未标注时容器内默认按 singleton 处理。 */ Retention(RetentionPolicy.RUNTIME) public interface Scope { String value() default ; }Definationbean实体类package com.code.liyunmiao.service; /** * Bean 定义扫描阶段为每个带 {code Component} 的类生成一份保存「类型 作用域」 * 供 {link com.code.liyunmiao.spring.liyunmiaocontext} 在 {code getBean} 时按作用域创建或复用实例。 */ public class Definationbean { /** 作用域字符串如 singleton与 {link com.code.liyunmiao.spring.Scope} 或默认值对应。 */ private String scope; /** Bean 对应的类元数据用于反射创建实例。 */ private Class type; public void setScope(String scope) { this.scope scope; } public String getScope() { return scope; } public void setType(Class type) { this.type type; } public Class getType() { return type; } }当给userService加package com.code.liyunmiao.service; import com.code.liyunmiao.spring.Component; import com.code.liyunmiao.spring.Scope; /** * 示例组件{code Component} 的 value 为容器中注册的 Bean 名称{code getBean(Userservice)} 与之对应。 */ Component(Userservice) Scope(singleton) public class Userservice { }下面来看测试类package com.code.liyunmiao.service; import com.code.liyunmiao.spring.liyunmiaocontext; /** * 入口显式传入配置类 {link configclass}构造手写容器后按 Bean 名称取出 {link Userservice}。 */ public class Test { public static void main(String[] args) { liyunmiaocontext context null; // 构造过程中可能抛异常如 classpath 上找不到扫描包目录等此处统一捕获并包装。 try { context new liyunmiaocontext(configclass.class); } catch (Exception e) { throw new RuntimeException(e); } // Bean 名需与 Component(Userservice) 中 value 一致。 Userservice userservice(Userservice) context.getBean(Userservice); System.out.println(context.getBean(Userservice)); System.out.println(context.getBean(Userservice)); System.out.println(context.getBean(Userservice)); System.out.println(context.getBean(Userservice)); } }这是输出输出都是同一个对象也就是对应的单例bean。下面看另一种package com.code.liyunmiao.service; import com.code.liyunmiao.spring.Component; import com.code.liyunmiao.spring.Scope; /** * 示例组件{code Component} 的 value 为容器中注册的 Bean 名称{code getBean(Userservice)} 与之对应。 */ Component(Userservice) Scope(proporty) public class Userservice { }当scope里面加这个的时候每次get的就是新的实例也就是说是多例模式每次获取都是new一个新的而不是用那个旧的了或者说而不是一直用那一个了。这是输出。

更多文章