Spring之手写IoC

本文涉及的产品
应用实时监控服务-用户体验监控,每月100OCU免费额度
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【1月更文挑战第15天】我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。

文章目录

前言

一、回顾Java反射

二、实现Spring的IoC

①搭建子模块

②准备测试需要的bean

③定义注解

④定义bean容器接口

⑤编写注解bean容器接口实现

⑥编写扫描bean逻辑

⑦java类标识Bean注解

⑧测试Bean加载

⑨依赖注入

⑩依赖注入实现

总结


前言

我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。


一、回顾Java反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API**(1)java.lang.Class(2)java.lang.reflect**,所以,Class对象是反射的根源

自定义类

packagecom.atguigu.reflect;
publicclassCar {
//属性privateStringname;
privateintage;
privateStringcolor;
//无参数构造publicCar() {
    }
//有参数构造publicCar(Stringname, intage, Stringcolor) {
this.name=name;
this.age=age;
this.color=color;
    }
//普通方法privatevoidrun() {
System.out.println("私有方法-run.....");
    }
//get和set方法publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicintgetAge() {
returnage;
    }
publicvoidsetAge(intage) {
this.age=age;
    }
publicStringgetColor() {
returncolor;
    }
publicvoidsetColor(Stringcolor) {
this.color=color;
    }
@OverridepublicStringtoString() {
return"Car{"+"name='"+name+'\''+", age="+age+", color='"+color+'\''+'}';
    }
}

编写测试类

packagecom.atguigu.reflect;
importorg.junit.jupiter.api.Test;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
publicclassTestCar {
//1、获取Class对象多种方式@Testpublicvoidtest01() throwsException {
//1 类名.classClassclazz1=Car.class;
//2 对象.getClass()Classclazz2=newCar().getClass();
//3 Class.forName("全路径")Classclazz3=Class.forName("com.atguigu.reflect.Car");
//实例化Carcar= (Car)clazz3.getConstructor().newInstance();
System.out.println(car);
    }
//2、获取构造方法@Testpublicvoidtest02() throwsException {
Classclazz=Car.class;
//获取所有构造// getConstructors()获取所有public的构造方法//        Constructor[] constructors = clazz.getConstructors();// getDeclaredConstructors()获取所有的构造方法public  privateConstructor[] constructors=clazz.getDeclaredConstructors();
for (Constructorc:constructors) {
System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());
        }
//指定有参数构造创建对象//1 构造public//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);//        Car car1 = (Car)c1.newInstance("夏利", 10, "红色");//        System.out.println(car1);//2 构造privateConstructorc2=clazz.getDeclaredConstructor(String.class, int.class, String.class);
c2.setAccessible(true);
Carcar2= (Car)c2.newInstance("捷达", 15, "白色");
System.out.println(car2);
    }
//3、获取属性@Testpublicvoidtest03() throwsException {
Classclazz=Car.class;
Carcar= (Car)clazz.getDeclaredConstructor().newInstance();
//获取所有public属性//Field[] fields = clazz.getFields();//获取所有属性(包含私有属性)Field[] fields=clazz.getDeclaredFields();
for (Fieldfield:fields) {
if(field.getName().equals("name")) {
//设置允许访问field.setAccessible(true);
field.set(car,"五菱宏光");
System.out.println(car);
            }
System.out.println(field.getName());
        }
    }
//4、获取方法@Testpublicvoidtest04() throwsException {
Carcar=newCar("奔驰",10,"黑色");
Classclazz=car.getClass();
//1 public方法Method[] methods=clazz.getMethods();
for (Methodm1:methods) {
//System.out.println(m1.getName());//执行方法 toStringif(m1.getName().equals("toString")) {
Stringinvoke= (String)m1.invoke(car);
//System.out.println("toString执行了:"+invoke);            }
        }
//2 private方法Method[] methodsAll=clazz.getDeclaredMethods();
for (Methodm:methodsAll) {
//执行方法 runif(m.getName().equals("run")) {
m.setAccessible(true);
m.invoke(car);
            }
        }
    }
}

二、实现Spring的IoC

我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。

①搭建子模块

搭建模块:guigu-spring,搭建方式如其他spring子模块

②准备测试需要的bean

添加依赖

<dependencies><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency></dependencies>

创建UserDao接口

packagecom.atguigu.spring6.test.dao;
publicinterfaceUserDao {
publicvoidprint();
}

创建UserDaoImpl实现

packagecom.atguigu.spring6.test.dao.impl;
importcom.atguigu.spring.dao.UserDao;
publicclassUserDaoImplimplementsUserDao {
@Overridepublicvoidprint() {
System.out.println("Dao层执行结束");
    }
}

创建UserService接口

packagecom.atguigu.spring6.test.service;
publicinterfaceUserService {
publicvoidout();
}

创建UserServiceImpl实现类

packagecom.atguigu.spring.test.service.impl;
importcom.atguigu.spring.core.annotation.Bean;
importcom.atguigu.spring.service.UserService;
@BeanpublicclassUserServiceImplimplementsUserService {
//    private UserDao userDao;@Overridepublicvoidout() {
//userDao.print();System.out.println("Service层执行结束");
    }
}

③定义注解

我们通过注解的形式加载bean与实现依赖注入

bean注解

packagecom.atguigu.spring.core.annotation;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceBean {
}

依赖注入注解

packagecom.atguigu.spring.core.annotation;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceDi {
}

说明:上面两个注解可以随意取名

④定义bean容器接口

packagecom.atguigu.spring.core;
publicinterfaceApplicationContext {
ObjectgetBean(Classclazz);
}

⑤编写注解bean容器接口实现

AnnotationApplicationContext基于注解扫描bean

packagecom.atguigu.spring.core;
importjava.util.HashMap;
publicclassAnnotationApplicationContextimplementsApplicationContext {
//存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>();
@OverridepublicObjectgetBean(Classclazz) {
returnbeanFactory.get(clazz);
    }
/*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) {
    }
}

⑥编写扫描bean逻辑

我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:

packagecom.atguigu.spring.core;
importcom.atguigu.spring.core.annotation.Bean;
importjava.io.File;
importjava.util.HashMap;
publicclassAnnotationApplicationContextimplementsApplicationContext {
//存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>();
privatestaticStringrootPath;
@OverridepublicObjectgetBean(Classclazz) {
returnbeanFactory.get(clazz);
    }
/*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) {
try {
StringpackageDirName=basePackage.replaceAll("\\.", "\\\\");
Enumeration<URL>dirs=Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URLurl=dirs.nextElement();
StringfilePath=URLDecoder.decode(url.getFile(),"utf-8");
rootPath=filePath.substring(0, filePath.length()-packageDirName.length());
loadBean(newFile(filePath));
            }
        } catch (Exceptione) {
thrownewRuntimeException(e);
        }
    }
privatevoidloadBean(FilefileParent) {
if (fileParent.isDirectory()) {
File[] childrenFiles=fileParent.listFiles();
if(childrenFiles==null||childrenFiles.length==0){
return;
            }
for (Filechild : childrenFiles) {
if (child.isDirectory()) {
//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);
                } else {
//通过文件路径转变成全类名,第一步把绝对路径部分去掉StringpathWithClass=child.getAbsolutePath().substring(rootPath.length() -1);
//选中class文件if (pathWithClass.contains(".class")) {
//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .StringfullName=pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
try {
Class<?>aClass=Class.forName(fullName);
//把非接口的类实例化放在map中if(!aClass.isInterface()){
Beanannotation=aClass.getAnnotation(Bean.class);
if(annotation!=null){
Objectinstance=aClass.newInstance();
//判断一下有没有接口if(aClass.getInterfaces().length>0) {
//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getInterfaces()[0] +"】,实例对象是:"+instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0], instance);
                                    }else{
//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getName() +"】,实例对象是:"+instance.getClass().getName());
beanFactory.put(aClass, instance);
                                    }
                                }
                            }
                        } catch (ClassNotFoundException|IllegalAccessException|InstantiationExceptione) {
e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

⑦java类标识Bean注解

@BeanpublicclassUserServiceImplimplementsUserService
@BeanpublicclassUserDaoImplimplementsUserDao

⑧测试Bean加载

packagecom.atguigu.spring;
importcom.atguigu.spring.core.AnnotationApplicationContext;
importcom.atguigu.spring.core.ApplicationContext;
importcom.atguigu.spring.test.service.UserService;
importorg.junit.jupiter.api.Test;
publicclassSpringIocTest {
@TestpublicvoidtestIoc() {
ApplicationContextapplicationContext=newAnnotationApplicationContext("com.atguigu.spring.test");
UserServiceuserService= (UserService)applicationContext.getBean(UserService.class);
userService.out();
System.out.println("run success");
    }
}

控制台打印测试

⑨依赖注入

只要userDao.print();调用成功,说明就注入成功

packagecom.atguigu.spring.test.service.impl;
importcom.atguigu.spring.core.annotation.Bean;
importcom.atguigu.spring.core.annotation.Di;
importcom.atguigu.spring.dao.UserDao;
importcom.atguigu.spring.service.UserService;
@BeanpublicclassUserServiceImplimplementsUserService {
@DiprivateUserDaouserDao;
@Overridepublicvoidout() {
userDao.print();
System.out.println("Service层执行结束");
    }
}

执行第八步:报错了,说明当前userDao是个空对象

⑩依赖注入实现

packagecom.atguigu.spring.core;
importcom.atguigu.spring.core.annotation.Bean;
importcom.atguigu.spring.core.annotation.Di;
importjava.io.File;
importjava.lang.reflect.Field;
importjava.util.HashMap;
importjava.util.Map;
publicclassAnnotationApplicationContextimplementsApplicationContext {
//存储bean的容器privateHashMap<Class, Object>beanFactory=newHashMap<>();
privatestaticStringrootPath;
@OverridepublicObjectgetBean(Classclazz) {
returnbeanFactory.get(clazz);
    }
/*** 根据包扫描加载bean* @param basePackage*/publicAnnotationApplicationContext(StringbasePackage) {
try {
StringpackageDirName=basePackage.replaceAll("\\.", "\\\\");
Enumeration<URL>dirs=Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URLurl=dirs.nextElement();
StringfilePath=URLDecoder.decode(url.getFile(),"utf-8");
rootPath=filePath.substring(0, filePath.length()-packageDirName.length());
loadBean(newFile(filePath));
            }
        } catch (Exceptione) {
thrownewRuntimeException(e);
        }
//依赖注入loadDi();
    }
privatevoidloadBean(FilefileParent) {
if (fileParent.isDirectory()) {
File[] childrenFiles=fileParent.listFiles();
if(childrenFiles==null||childrenFiles.length==0){
return;
            }
for (Filechild : childrenFiles) {
if (child.isDirectory()) {
//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);
                } else {
//通过文件路径转变成全类名,第一步把绝对路径部分去掉StringpathWithClass=child.getAbsolutePath().substring(rootPath.length() -1);
//选中class文件if (pathWithClass.contains(".class")) {
//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .StringfullName=pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
try {
Class<?>aClass=Class.forName(fullName);
//把非接口的类实例化放在map中if(!aClass.isInterface()){
Beanannotation=aClass.getAnnotation(Bean.class);
if(annotation!=null){
Objectinstance=aClass.newInstance();
//判断一下有没有接口if(aClass.getInterfaces().length>0) {
//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getInterfaces()[0] +"】,实例对象是:"+instance.getClass().getName());
beanFactory.put(aClass.getInterfaces()[0], instance);
                                    }else{
//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+aClass.getName() +"】,实例对象是:"+instance.getClass().getName());
beanFactory.put(aClass, instance);
                                    }
                                }
                            }
                        } catch (ClassNotFoundException|IllegalAccessException|InstantiationExceptione) {
e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
privatevoidloadDi() {
for(Map.Entry<Class,Object>entry : beanFactory.entrySet()){
//就是咱们放在容器的对象Objectobj=entry.getValue();
Class<?>aClass=obj.getClass();
Field[] declaredFields=aClass.getDeclaredFields();
for (Fieldfield : declaredFields){
Diannotation=field.getAnnotation(Di.class);
if( annotation!=null ){
field.setAccessible(true);
try {
System.out.println("正在给【"+obj.getClass().getName()+"】属性【"+field.getName() +"】注入值【"+beanFactory.get(field.getType()).getClass().getName() +"】");
field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessExceptione) {
e.printStackTrace();
                    }
                }
            }
        }
    }
}

执行第八步:执行成功,依赖注入成功


总结

以上就是Spring之容器:IOC(3)的相关知识点,希望对你有所帮助。

积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!

相关文章
|
4月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
16天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
3月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
245 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
3月前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
39 0
|
2月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
144 9
|
2月前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
37 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
3月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
46 4
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
79 0