Spring IoC原理及简单实现

文章介绍了Spring框架的两大核心特性:IoC(控制反转)和AOP(面向切面编程)。IoC通过降低代码耦合度来提高系统可维护性,其基本步骤包括配置对象及依赖关系、利用反射生成对象并注入。文中还提供了一个简单的IoC容器实现,涉及注解、包扫描和自动注入的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

Spring 的两大特性 IoC和AOP

IoC

在 Java 软件开发过程中,系统中的各个对象之间、各个模块之间、软件系统和硬件系统之间,或多或少都存在一定的耦合关系。

若一个系统的耦合度过高,那么就会造成难以维护的问题,但完全没有耦合的代码几乎无法完成任何工作,这是由于几乎所有的功能都需要代码之间相互协作、相互依赖才能完成。因此我们在程序设计时,所秉承的思想一般都是在不影响系统功能的前提下,最大限度的降低耦合度。

IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。

  1. 在配置文件(例如 Bean.xml)中,对各个对象以及它们之间的依赖关系进行配置;
  2. 我们可以把 IoC 容器当做一个工厂,这个工厂的产品就是 Spring Bean;
  3. 容器启动时会加载并解析这些配置文件,得到对象的基本信息以及它们之间的依赖关系;
  4. IoC 利用 Java 的反射机制,根据类名生成相应的对象(即 Spring Bean),并根据依赖关系将这个对象注入到依赖它的对象中。

由于对象的基本信息、对象之间的依赖关系都是在配置文件中定义的,并没有在代码中紧密耦合,因此即使对象发生改变,我们也只需要在配置文件中进行修改即可,而无须对 Java 代码进行修改,这就是 Spring IoC 实现解耦的原理。

根据上面的原理,来简单实现一个IoC容器

首先我们把思路理一下 :

  1. 首先需要两个注解(两个注解配合使用)

    @MyBean:把对象交给容器管理

    @MyAutowired:自动注入,把容器里的实例赋值给加了注解的属性

  2. 需要ApplicationContext来操作容器

  3. 需要一个HashMap来存储Bean对象

  4. 需要开启注解启用范围,包扫描

代码逻辑

  1. 第一步实例化一个操作容器,并且把要扫描的包当形参
new ApplicationContextImpl("com.zn.ioc");
  1. 第二步加载容器,扫描包下的所有文件,发现类上有@MyBean注解就把此类放到HashMap中(key:类的类型,value:类的实例化)。
  2. 第三步自动注入,逻辑 自动注入@MyAutowired和MyBean一起连用,判断用了MyBean的类中属性有没有加@MyAutowired注解,有的话就设置属性,根据属性名的类型去HashMap中找对用的实例赋值给此属性,保证单例。

具体实现

目录:

在这里插入图片描述

依赖:

<dependencies>
    <!--spring context依赖-->
    <!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.2</version>
    </dependency>

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

两个注解:@MyAutoWired,@MyBean

@Target(ElementType.FIELD) // 在属性上使用注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
@Target(ElementType.TYPE) // 在类上使用注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
}

LoginController

@MyBean
public class LoginController {

    @MyAutowired
    private LoginServiceImpl loginServiceImpl;
    public void login() {
        System.out.println("LoginController..........login");
        loginServiceImpl.login();
    }
}

ApplicationContext

public interface ApplicationContext {

    // 获取bean对象
    public Object getBean(Class clazz);
}

LoginDao

@MyBean
public class LoginDao {
    public void login() {
        System.out.println("LoginDao..........login.......");
    }
}

LoginService

public interface LoginService {
    abstract void login();
}

LoginServiceImpl

@MyBean
public class LoginServiceImpl implements LoginService {
    @MyAutowired
    private LoginDao loginDao;

    @Override
    public void login() {
        System.out.println("LoginServiceImpl......login.........");
        loginDao.login();
    }
}

ApplicationContextImpl (核心实现)

public class ApplicationContextImpl implements ApplicationContext{

    // 使用hashMap来存储bean实列的容器
    private HashMap beanFactory = new HashMap<Class,Object>();
    // 根路径
    private static String rootPath;

    // 获取bean容器中的对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    // 加载带@MyBean实例到容器中
    public ApplicationContextImpl(String basePackage)  {
        // 逻辑 :首先扫描所有文件,发现类上有@MyBean注解就把此类放到beanFactory中
        // 1、把包的路径中的 .转换成 /
        String pathReplace = basePackage.replace(".", File.separator);
        try {
            // 获取class路径
            Enumeration<URL> dirs  = Thread.currentThread().getContextClassLoader().getResources(pathReplace);
               while (dirs.hasMoreElements()){
                   URL url = dirs.nextElement();
                   String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                   rootPath = filePath.substring(0, filePath.length()-pathReplace.length());

                   // /E:/workspace-java/learn/learn_spring/spirng-ioc/target/test-classes/
                   ///E:/workspace-java/learn/learn_spring/spirng-ioc/target/classes/
        //           System.out.println(rootPath);

                   // 加载bean
                   loadBean(new File(rootPath));
               }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 自动注入   逻辑 自动注入@MyAutowired和MyBean一起连用,判断用了MyBean的类中属性有没有加@MyAutowired注解的有的话就设置属性
        // 遍历beanFactory
        beanFactory.forEach((key,value)->{
            // 使用反射 获取类中的属性 包括私有属性
            Field[] fields = value.getClass().getDeclaredFields();
            for (Field field : fields) {
                // 判断属性上是否有@MyAutowired注解
                if(field.getAnnotation(MyAutowired.class)!=null){
                    // 如果有此注解就把beanFactory中的对应的对象实例设置给此属性

                    // 设置此属性可更改
                    field.setAccessible(true);
                    try {
                        // 赋值
                        field.set(value,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    // 加载Bean对象
    private void loadBean(File file) {

        // 如果是一个文件
        if(file.isDirectory()){
            File[] files = file.listFiles();

            for (File childFile : files) {
                // 如果是子文件还是一个目录 递归
                if(childFile.isDirectory()) {
                    loadBean(childFile);
                } else {

                    putBean(childFile);
                }
            }
        }else {
            putBean(file);
        }
    }

    // 添加实例到容器中

    private void putBean(File childFile) {
        // 获取字节码的绝对路径
        String absolutePath = childFile.getAbsolutePath();
        // 把根路径删除 获得类的全路径
        String classPath = absolutePath.substring(rootPath.length() -1);
        // 判断后缀是否是 .class
        if(classPath.endsWith(".class")){
            // 替换成全类名
            String replace = classPath.replace(".class", "");
            String fullClassName = replace.replace(File.separator, ".");
            System.out.println(fullClassName);

            try {
                // 获取反射
                Class<?> clas = Class.forName(fullClassName);
                // 判断此类是否有注解MyBean
                if(clas.getAnnotation(MyBean.class)!=null){
                    // 实例化对象
                    Object instance = clas.newInstance();
                    // 判断此类有没有接口
                    if(clas.isInterface()){
                        // 有接口的
                        beanFactory.put(clas.getInterfaces()[0],
                                instance);
                    }else {

                        beanFactory.put(clas,instance);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }
}

ApplicationContextImplTest

@Test
public void myAutoWiredTest() {
    ApplicationContextImpl applicationContext = new ApplicationContextImpl("com.zn.ioc");
    LoginController loginController =(LoginController)applicationContext.getBean(LoginController.class);
    loginController.login();
}
ApplicationContextImplTest

```java
@Test
public void myAutoWiredTest() {
    ApplicationContextImpl applicationContext = new ApplicationContextImpl("com.zn.ioc");
    LoginController loginController =(LoginController)applicationContext.getBean(LoginController.class);
    loginController.login();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值