手写实现spring ioc自动注入、包扫描、单例多例模式(@Component @Autowired @Scope)
源码地址:https://gitee.com/HumorChen/spring_ioc_impl.git
见测试类
主要结构:
- 实体类
package com.humorchen.spring_ioc_impl.bean;
import com.humorchen.spring_ioc_impl.annotation.MyAutowired;
import com.humorchen.spring_ioc_impl.annotation.MyComponent;
import com.humorchen.spring_ioc_impl.annotation.MyScope;
import com.humorchen.spring_ioc_impl.constant.ScopeType;
/**
* 用户实体类
*/
//@MyScope(ScopeType.PROTOTYPE)
@MyComponent(scope = ScopeType.PROTOTYPE)
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 上下文接口
提供了两种手动获取bean的方法 Object getBean(String name); Object getBean(Class cls);
自动注入由@MyAutowired实现(仅支持被ioc容器管理的对象(@Component))
package com.humorchen.spring_ioc_impl.interfaces;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* spring 上下文接口
*/
public interface MySpringContext {
//IOC容器
Map<String, Object> IOC=new HashMap<String, Object>();
//存储上下文的属性
Map<Object,Object> ATTRS=new HashMap<Object, Object>();
/**
* 根据名称获取 bean 实例
* @param name bean实例的名称
* @return Object bean实例
*/
Object getBean(String name);
/**
* 获取 bean
* @param cls bean的类型
* @return Object bean实例
*/
Object getBean(Class cls);
/**
* 获取上下文的属性
* @param object 键
* @return Object 值
*/
Object getAttr(Object object);
/**
* 设置上下文属性
* @param key 键
* @param value 值
*/
void setAttrs(Object key,Object value);
/**
* 销毁所有实例
*/
void dispose();
/**
* 设置 bean
* @param name 名称
* @param object bean实例
*/
void setBean(String name,Object object);
/**
* 打印下ioc容器概览
*/
void overview();
}
- 定义了三个注解
@MyAutowired 对应Spring里的@Autowired
@MyComponent 对应Spring里的@Component
@MyScope 对应Spring里的@Scope
package com.humorchen.spring_ioc_impl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自动填充注解
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
public String value() default "";
}
package com.humorchen.spring_ioc_impl.annotation;
import com.humorchen.spring_ioc_impl.constant.ScopeType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 组件注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
//component name
public String name() default "";
public String scope() default ScopeType.SINGLETON;
}
package com.humorchen.spring_ioc_impl.annotation;
import com.humorchen.spring_ioc_impl.constant.ScopeType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* scope注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScope {
public String value() default ScopeType.SINGLETON;
}
- 一个常量类用来存scope
package com.humorchen.spring_ioc_impl.constant;
/**
* Scope 单例/多例
*/
public interface ScopeType {
//单例模式
String SINGLETON ="singleton";
//多例模式
String PROTOTYPE ="prototype";
}
- 一个自定义的类加载器
package com.humorchen.spring_ioc_impl.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
/**
* 自定义类加载器
*/
public class MyClassLoader extends ClassLoader {
private static String MINE=MyClassLoader.class.getName();
/**
* 传入绝对路径则 loadClass("F:\\project\\spring_ioc_impl\\target\\classes\\com\\humorchen\\spring_ioc_impl\\annotation\\MyAutowired.class",false)
* @param absolutePath 绝对路径
* @return
*/
public Class loadClass(String absolutePath,String name){
//如果已经加载了直接返回
try {
Class cls=MyClassLoader.class.getClassLoader().loadClass(name);
if(cls!=null){
return cls;
}
}catch (Exception e){}
//没有加载的继续加载
byte[] data=loadData(absolutePath);
return this.defineClass(name,data,0,data.length);
}
private byte[] loadData(String path){
try {
FileInputStream inputStream=new FileInputStream(path);
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
int b;
while ((b=inputStream.read())!=-1){
byteArrayOutputStream.write(b);
}
inputStream.close();
return byteArrayOutputStream.toByteArray();
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
- 上下文Context的两种实现
- MySpringDefaultContext
package com.humorchen.spring_ioc_impl.context;
import com.humorchen.spring_ioc_impl.annotation.MyAutowired;
import com.humorchen.spring_ioc_impl.annotation.MyComponent;
import com.humorchen.spring_ioc_impl.annotation.MyScope;
import com.humorchen.spring_ioc_impl.constant.ScopeType;
import com.humorchen.spring_ioc_impl.exception.MyAutowiredException;
import com.humorchen.spring_ioc_impl.exception.MyBeanNotFoundException;
import com.humorchen.spring_ioc_impl.exception.NotSupportException;
import com.humorchen.spring_ioc_impl.interfaces.MySpringContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 默认上下文
*/
public class MySpringDefaultContext implements MySpringContext {
public Object getBean(String name) {
Object bean=IOC.get(name);
if(bean!=null){
return bean;
}
return null;
}
/**
* 概览
*/
public void overview(){
System.out.println("\n\n");
System.out.println("-----------------IOC容器概览---------------------");
System.out.println("IOC容器中共有"+IOC.size()+"个bean");
for(String name:IOC.keySet()){
System.out.println(name);
}
System.out.println("-------------------------------------------------");
System.out.println("\n\n");
}
/**
* 获取bean
* @param cls bean的类型
* @return
*/
public Object getBean(Class cls) {
MyComponent myComponent=(MyComponent)cls.getAnnotation(MyComponent.class);
MyScope myScope=(MyScope) cls.getAnnotation(MyScope.class);
String type=ScopeType.SINGLETON;
if((myComponent!=null&&myComponent.scope().equals(ScopeType.PROTOTYPE))||(myScope!=null&&myScope.value().equals(ScopeType.PROTOTYPE)))
{
type=ScopeType.PROTOTYPE;
}
if(type.equals(ScopeType.SINGLETON)){
//单例模式
String name=genName(cls);
Object object=getBean(name);
//直接找这个类的实例
if(object!=null){
return object;
}
if(cls.isAnnotation()){
throw new NotSupportException("类型:注解"+cls.getName());
}
//找同类型的且优先同类型
List<Object> instances=new ArrayList<Object>();
for(Map.Entry<String,Object> entry:IOC.entrySet()){
if(cls.isInstance(entry.getValue())){
instances.add(entry.getValue());
}
}
//找不到这个类的实例如果是个接口就找这个类实现类的实例
if(cls.isInterface()){
//找实现类的实例
for (Object o:instances){
if(cls.isInstance(o)){
return o;
}
}
//找不到实现类的实例对象
throw new MyBeanNotFoundException("找不到"+cls.getName()+"的任何实现类bean");
}
//先找同类型
for (Object o:instances){
if (cls.equals(o.getClass())){
return o;
}
}
//后找子类
for (Object o:instances){
if (cls.isInstance(o)){
return o;
}
}
//子类的实例也找不到就去创建一个
return genObjectByClass(cls);
}else{
//多例模式
//找同类型的且优先同类型
List<Object> instances=new ArrayList<Object>();
for(Map.Entry<String,Object> entry:IOC.entrySet()){
if(cls.isInstance(entry.getValue())){
instances.add(entry.getValue());
}
}
//找不到这个类的实例如果是个接口就找这个类实现类的实例
if(cls.isInterface()){
//找实现类的实例
for (Object o:instances){
if(cls.isInstance(o)){
return genObjectByClass(o.getClass());
}
}
//找不到实现类的实例对象
throw new MyBeanNotFoundException("找不到"+cls.getName()+"的任何实现类bean");
}
//先找同类型
for (Object o:instances){
if (cls.equals(o.getClass())){
return genObjectByClass(o.getClass());
}
}
//后找子类
for (Object o:instances){
if (cls.isInstance(o)){
return genObjectByClass(o.getClass());
}
}
return genObjectByClass(cls);
}
}
/**
* 根据类生成对象实例
* @param cls
* @return
*/
protected Object genObjectByClass(Class cls){
try {
Object object=cls.newInstance();
for(Field field:cls.getDeclaredFields()){
MyAutowired annotation=field.getAnnotation(MyAutowired.class);
//是否存在需要注入的方法
if(annotation!=null){
field.setAccessible(true);
//如果注入的时候传入了bean的名字
String name=annotation.value();
Object fieldObj;
if(name.equals("")){
fieldObj=getBean(field.getType());
}else{
fieldObj=getBean(name);
}
if(fieldObj!=null){
field.set(object,fieldObj);
}else{
throw new MyAutowiredException("创建对象时自动注入异常"+cls.getName()+":"+field.getName());
}
}
}
String name=genName(cls);
IOC.put(name,object);
return object;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 销毁
*/
public void dispose() {
IOC.clear();
ATTRS.clear();
}
public void setBean(String name, Object object) {
IOC.put(name,object);
Class cls=object.getClass();
}
public void setBean(Class cls, Object object) {
String name=genName(cls);
IOC.put(name,object);
}
private String genName(Class cls){
return cls.getName();
}
public Object getAttr(Object object) {
return ATTRS.get(object);
}
public void setAttrs(Object key, Object value) {
ATTRS.put(key,value);
}
}
- MySpringPackageContext包扫描的上下文
package com.humorchen.spring_ioc_impl.context;
import com.humorchen.spring_ioc_impl.annotation.MyComponent;
import com.humorchen.spring_ioc_impl.bean.User;
import com.humorchen.spring_ioc_impl.util.ClassFileScanUtil;
import java.util.List;
/**
* 带包扫描的上下文
*/
public class MySpringPackageContext extends MySpringDefaultContext {
public MySpringPackageContext(String packageName){
//扫描出这个包下面的所有类
List<Class> classes= ClassFileScanUtil.scanPackageRetClass(packageName);
for (Class cls:classes){
//对需要IOC管理的进行解析初始化容器
MyComponent myComponent=(MyComponent) cls.getAnnotation(MyComponent.class);
if (myComponent!=null){
super.genObjectByClass(cls);
}
}
}
}
- 类加载的时候扫描用到的工具类
package com.humorchen.spring_ioc_impl.util;
import com.humorchen.spring_ioc_impl.bean.User;
import com.humorchen.spring_ioc_impl.classloader.MyClassLoader;
import java.io.File;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
/**
* 扫描某个目录下的所有class文件工具
*/
public class ClassFileScanUtil {
/**
* 扫描出一个目录下所有的class文件
* @param directory
* @return
*/
public static List<String> scanRetFilePath(String directory){
List<String> classes=new ArrayList<String>();
File root=new File(directory);
if(root.isDirectory()){
File[] files=root.listFiles();
for(File file:files){
if(file.isDirectory()){
//如果是目录则递归
classes.addAll(scanRetFilePath(file.getAbsolutePath()));
}else if(file.isFile()&&file.getName().endsWith(".class")){
//符合则加入
classes.add(file.getAbsolutePath());
}
}
}
return classes;
}
public static List<Class> scanPackageRetClass(String packageName){
String rootDir=getClassRootDirPath();
MyClassLoader classLoader=new MyClassLoader();
List<String> classes=scanRetFilePath(rootDir);
List<Class> ret=new ArrayList<Class>();
for(String cls:classes){
String name=getClassNameByAbsolutePath(cls,rootDir);
if(name.startsWith(packageName)){
Class loadedClass=classLoader.loadClass(cls,name);
ret.add(loadedClass);
}
}
return ret;
}
/**
* 对某个目录下的所有类扫描出来并返回
* @param directory
* @return
*/
public static List<Class> scanRetClass(String directory){
File root=new File(directory);
String prefix=root.getAbsolutePath();
List<String> filesPath=scanRetFilePath(directory);
List<Class> ret=new ArrayList<Class>();
MyClassLoader classLoader=new MyClassLoader();
for(String path:filesPath){
if(path.startsWith(prefix)){
System.out.println(path);
String name=getClassNameByAbsolutePath(path,prefix);
System.out.println(name);
Class cls=classLoader.loadClass(path,name);
if(cls!=null){
ret.add(cls);
}
}
}
return ret;
}
/**
* 获得class存放的目录的绝对路径
* @return
*/
public static String getClassRootDirPath(){
return new File(URLDecoder.decode(ClassFileScanUtil.class.getClassLoader().getResource("").getPath())).getAbsolutePath();
}
/**
* 给定一个类的完整路径和项目源码根路径,生成类名
* @param absolutePath
* @param srcDirPath
* @return
*/
public static String getClassNameByAbsolutePath(String absolutePath,String srcDirPath){
String name=absolutePath.replace(srcDirPath+File.separator,"").replace(File.separator,".");
name=name.substring(0,name.length()-6);
return name;
}
}
- 三个异常类
MyAutowiredException
MyBeanNotFoundException
NotSupportException
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039673
分类:
Java
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~