Spring6--手写简易IOC容器

  • 控制反转(IOC):控制反转是一种思想,即将对象的创建权力和对象与对象之间的关系交出去,给第三方容器负责
  • 依赖注入(DI):实现了控制反转的思想。
  • Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或者叫Bean对象之间关系的维护)。      

步骤:

1.搭建子模块

2.准备测试需要的bean

3.定义注解

4.定义bean容器接口

5.编写注解bean容器接口实现

6.编写扫描bean逻辑

7.java类标识Bean注解

8.测试Bean加载

9.依赖注入

10.依赖注入实现


1.搭建Spring6模块

 使用IDEA大家Spring6的模块,便于实现IOC思想并完成打包。

 

2.准备测试需要的bean

 为实现后的产品准备验证测试工具:

  1. 添加依赖
  2. 创建UserDao接口以及创建UserDaoImpl实现
  3. 创建UserService接口以及创建UserServiceImpl实现类
1.添加依赖 -- 测试所需依赖
<dependencies>
    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.1</version>
    </dependency>
</dependencies>
2.准备测试接口以及实现类
package com.czy.spring.test.service;

/**
 * ClassName: UserService
 * Package: com.czy.spring6.test.service
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
public interface UserService {
    public void out();
}

package com.czy.spring.test.service.impl;

import com.czy.spring.core.annotation.Bean;
import com.czy.spring.test.dao.UserDao;
import com.czy.spring.test.service.UserService;

/**
 * ClassName: UserServiceImpl
 * Package: com.czy.spring6.test.service.impl
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
@Bean
public class UserServiceImpl implements UserService {
    private UserDao userDao;

    @Override
    public void out() {
        // userDao.print();
        System.out.println("Service层执行结束");
    }
}
package com.czy.spring.test.dao;

/**
 * ClassName: UserDao
 * Package: com.czy.spring6.test.dao
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
public interface UserDao {
    public void print();
}
package com.czy.spring.test.dao.impl;

import com.czy.spring.core.annotation.Bean;
import com.czy.spring.test.dao.UserDao;

/**
 * ClassName: UserDaoImpl
 * Package: com.czy.spring6.test.dao.impl
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
@Bean
public class UserDaoImpl implements UserDao {
    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}

3.定义注解

 通过注解的形式加载Bean与实现依赖注入

 Bean注解:

package com.czy.spring.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * ClassName: Bean
 * Package: com.czy.spring.core.annotation
 * Description:
 *  通过注解的形式加载bean与实现依赖注入
 * @Author Chen Ziyun
 * @Version 1.0
 */
@Target(ElementType.TYPE)// 注解作用范围
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期
public @interface Bean {
}

 依赖注入注解:

package com.czy.spring.core.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * ClassName: Di
 * Package: com.czy.spring.core.annotation
 * Description:
 *  依赖注入注解
 * @Author Chen Ziyun
 * @Version 1.0
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

4.定义bean容器接口

 与Spring6中的ApplicationContext功能类似,即相当于BeanFactory的子接口

package com.czy.spring.core;

/**
 * ClassName: ApplocationContext
 * Package: com.czy.spring.core
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
public interface ApplicationContext {
    Object getBean(Class clazz);
}

5.编写注解bean容器接口实现

 AnnotationApplicationContext基于注解扫描bean

package com.czy.spring;

import com.czy.spring.core.ApplicationContext;
import java.util.HashMap;
import java.util.Map;

/**
 * ClassName: AnnotationApplicationContext
 * Package: com.atczy.annotation
 * Description:
 *  初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
 * @Author Ziyun Chen
 * @Create 2024/2/28 18:36
 * @Version 1.0  
 */
public class AnnotationApplicationContext implements ApplicationContext {
    private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     *  创建有参构造,传递包路径,设置包扫描规则
     *  当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
     */
    public AnnotationApplicationContext(String basePackage){
       
    }
}

6.编写扫描bean逻辑

 通过构造方法传入包的base路径,扫描被@Bean注解的java对象 ---- 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中

  1. 查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
  2. 遍历当前文件夹下的所有文件
  3. 当前文件是否是文件夹?是-递归,否-查看当前文件
  4. 查看当前class文件是否有Bean注解:是 -- 将非接口类的实例放入Map中;否 -- 直接跳过
package com.czy.spring;

import com.czy.spring.core.ApplicationContext;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.core.annotation.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * ClassName: AnnotationApplicationContext
 * Package: com.atczy.annotation
 * Description:
 *  初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
 * @Author Ziyun Chen
 * @Create 2024/2/28 18:36
 * @Version 1.0  
 */
public class AnnotationApplicationContext implements ApplicationContext {
    private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器
    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     *  创建有参构造,传递包路径,设置包扫描规则
     *  当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
     */
    public AnnotationApplicationContext(String basePackage){
        // 1.获取该包的绝对路径
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        try {
            Enumeration<URL> urls =
                    Thread.currentThread().getContextClassLoader().getResources(packagePath);

            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                rootPath = filePath.substring(0, filePath.length()-packagePath.length());// 获取跟路径

                // 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
        if (!file.exists()){
            return;
        }
        if (file.isDirectory()){
            // 2.遍历当前文件夹下的所有文件
            File[] files = file.listFiles();
            if (files == null || files.length == 0){
                return;
            }
            for (File childFile :  files){
                // 3.当前文件是否是文件夹?是-递归,否-查看当前文件
                if (childFile.isDirectory()){
                    loadBean(childFile);
                }else {
                    // 4.查看当前class文件是否有Bean注解
                    // 4.1 通过文件名获取全类名,第一步把绝对路径去掉
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length()-1);
                    // 选中class文件
                    if (pathWithClass.contains(".class")){
                        // 将路径中的/转化为.,把.class去掉 -- 获取class的全类名
                        String fullName = pathWithClass.replaceAll("\\\\", "\\.").
                                replace(".class", "");
                        // 4.2 是否有Bean注解,
                        Class<?> clazz = Class.forName(fullName);

                        // 将非接口类的实例放入Map中
                        if (!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null){
                                // 实例化该对象
                                Object instance = clazz.getDeclaredConstructor().newInstance();
                                if (clazz.getInterfaces().length > 0){
                                    // 判断是否有Implement接口 是-以该接口为clazz传入Map中
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                }else {
                                    // 否-以该类为clazz传入Map
                                    beanFactory.put(clazz, instance);
                                }

                            }
                        }
                    }

                }


            }
        }

    }
}

7.java类标识Bean注解

 

8.测试Bean加载

package com.czy.spring;

import com.czy.spring.core.ApplicationContext;
import com.czy.spring.test.service.UserService;
import org.junit.jupiter.api.Test;

/**
 * ClassName: SpringIocTest
 * Package: com.czy.spring
 * Description:
 *
 * @Author Chen Ziyun
 * @Version 1.0
 */
public class SpringIocTest {
    @Test
    public void testIoc(){
        ApplicationContext applicationContext = new AnnotationApplicationContext("com.czy.spring.test");

        UserService userService = (UserService) applicationContext.getBean(UserService.class);

        userService.out();
        System.out.println("run success");
    }
}

 

以上,对象的创建功能已经实现了,但是对于对象之间的关系管理需要进一步实现。即依赖的注入(Di)。 

9.依赖注入

 在创建UserService的过程中,需要完成依赖的注入,此时,依赖注入功能还没有实现,userDao是个空对象,其中的方法都无法调用

10.依赖注入实现

需要对实现类进行扫描,查看其是否具备依赖注入相关的注解,并进行对象的创建 --- 实例化

对象在beanFactory的map集合中

  1. 遍历beanFactory的map集合
  2. 获取map的value值,每个属性获取到
  3. 遍历得到每个对象属性数组,得到每个属性
  4. 判断属性上面是否有@Di注解
  5. 如果有@Di注解,把对象进行设置(注入)
package com.atczy.annotation;

import com.czy.spring.core.ApplicationContext;
import com.czy.spring.core.annotation.Bean;
import com.czy.spring.core.annotation.Di;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Filter;

/**
 * ClassName: AnnotationApplicationContext
 * Package: com.atczy.annotation
 * Description:
 *  初始化该对象时,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中,初始化
 * @Author Ziyun Chen
 * @Create 2024/2/28 18:36
 * @Version 1.0  
 */
public class AnnotationApplicationContext implements ApplicationContext {
    private static Map<Class, Object> beanFactory = new HashMap<>();// 存储bean的容器
    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     *  创建有参构造,传递包路径,设置包扫描规则
     *  当前包及其子包,那个类有@Bean注解,把这个类通过反射实例化
     */
    public AnnotationApplicationContext(String basePackage){
        // 1.获取该包的绝对路径
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        try {
            Enumeration<URL> urls =
                    Thread.currentThread().getContextClassLoader().getResources(packagePath);

            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                rootPath = filePath.substring(0, filePath.length()-packagePath.length());// 获取跟路径

                // 加载Bean,获取所有的具备Bean注释的类,将其封装到beanFactory的Map容器当中
                loadBean(new File(filePath));

                // 加载Di
                loadDi();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1.查看当前文件夹是否是空文件夹?是-返回,否-查看当前文件夹下的所有文件
        if (!file.exists()){
            return;
        }
        if (file.isDirectory()){
            // 2.遍历当前文件夹下的所有文件
            File[] files = file.listFiles();
            if (files == null || files.length == 0){
                return;
            }
            for (File childFile :  files){
                // 3.当前文件是否是文件夹?是-递归,否-查看当前文件
                if (childFile.isDirectory()){
                    loadBean(childFile);
                }else {
                    // 4.查看当前class文件是否有Bean注解
                    // 4.1 通过文件名获取全类名,第一步把绝对路径去掉
                    String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length()-1);
                    // 选中class文件
                    if (pathWithClass.contains(".class")){
                        // 将路径中的/转化为.,把.class去掉 -- 获取class的全类名
                        String fullName = pathWithClass.replaceAll("\\\\", "\\.").
                                replace(".class", "");
                        // 4.2 是否有Bean注解,
                        Class<?> clazz = Class.forName(fullName);

                        // 将非接口类的实例放入Map中
                        if (!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null){
                                // 实例化该对象
                                Object instance = clazz.getDeclaredConstructor().newInstance();
                                if (clazz.getInterfaces().length > 0){
                                    // 判断是否有Implement接口 是-以该接口为clazz传入Map中
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                }else {
                                    // 否-以该类为clazz传入Map
                                    beanFactory.put(clazz, instance);
                                }

                            }
                        }
                    }

                }


            }
        }

    }

    private void loadDi() throws IllegalAccessException {
        // 实例化对象在beanFactory的map集合中
        // 1.遍历beanFactory的map集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries){
            // 2.获取map的value值,每个属性获取到
            Object obj = entry.getValue();
            Class<?> clazz = obj.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();

            // 3.遍历得到每个对象属性数组,得到每个属性
            for (Field field : declaredFields){
                //4.判断属性上面是否有@Di注解
                Di annotation = field.getAnnotation(Di.class);
                if (annotation != null){
                    // 5.如果有@Di注解,把对象进行设置(注入)
                    field.setAccessible(true);
                    field.set(obj, beanFactory.get(field.getType()));
                }

            }
        }

    }
}
  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring-IOCSpring框架的核心部分之一,它是一种设计模式,全称为Inversion of Control(控制反转)。它通过将对象的创建、依赖关系的管理和对象的生命周期交给Spring容器来实现,从而降低了组件之间的耦合度,提高了代码的可重用性和可维护性。Spring-IOC的实现主要依靠Spring容器Spring容器Spring框架的核心,它负责创建、管理和装配Bean对象,其中Bean是Spring框架中最基本的组件。 Spring-IOC的实现主要有两种方式:BeanFactory和ApplicationContext。其中,BeanFactory是Spring-IOC的基本实现,而ApplicationContext是BeanFactory的子接口,提供了更多高级特性。ApplicationContext是Spring框架中最常用的IOC容器,它除了提供BeanFactory的所有功能外,还提供了更多的企业级特性,例如AOP、事务管理、国际化、事件传播等。 下面是一个简单的Spring-IOC的例子,假设我们有一个UserService接口和一个UserServiceImpl实现类,我们可以通过Spring-IOC容器来创建和管理UserServiceImpl对象: 1.定义UserService接口和UserServiceImpl实现类 ```java public interface UserService { void addUser(User user); } @Service public class UserServiceImpl implements UserService { @Override public void addUser(User user) { // 添加用户的具体实现 } } ``` 2.在Spring配置文件中配置UserService实例 ```xml <bean id="userService" class="com.example.service.UserServiceImpl"/> ``` 3.在代码中获取UserService实例并使用 ```java ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean("userService", UserService.class); User user = new User(); userService.addUser(user); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值