加入收藏 | 设为首页 | 会员中心 | 我要投稿 北几岛 (https://www.beijidao.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

从零手写 IOC容器(通俗易懂)

发布时间:2021-07-06 05:40:26 所属栏目:大数据 来源: https://blog.csdn.net/u014209
导读:目录 概述 1.Component注解定义 2.Reject注解定义 3.User对象定义 4.UserService实现 5.UserController实现 6.IocContext ioc bean容器 7.IocUtils ioc的依赖注入 8.模拟调用UserController 结果 概述 IOC (Inversion of Control) 控制反转。熟悉Spring的应

目录

概述

1.Component注解定义

2.Reject注解定义

3.User对象定义

4.UserService实现

5.UserController实现

6.IocContext ioc bean容器

7.IocUtils ioc的依赖注入

8.模拟调用UserController

结果


概述

IOC (Inversion of Control) 控制反转。熟悉Spring的应该都知道。那么具体是怎么实现的呢?下面我们通过一个例子说明。

1.Component注解定义

/**
 * 类上的注解:用来告诉ioc容器哪些类需要托管
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {

}

先定义一个Component注解,主要被Component注解修饰的类都是需要被ioc容器管理的类。

2.Reject注解定义

/**
 * 属性上的注解:初始化一个类的时候,如果类中有注解需要注入其他类
 *              去ioc中找,找不到需要再初始化
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {

}

定义一个@Inject注解,只要是被@Inject注解注释的属性都会自动注入,实现IOC功能。

3.User对象定义

@Data
public class User {
    private String userName;
    private int userAge;

    public User(String userName,int userAge){
        this.userAge = userAge;
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User [userName=" + userName + ",userAge=" + userAge + "]";
    }
}

就是一个普通的模型bean

4.UserService实现

@Component
public class UserService {

    public User getUser(){
        return new User("xl",30);
    }
}

使用@Component注解标注该类是受容器管理的类。

5.UserController实现

@Component
public class UserController {

    @Inject
    private UserService userService;

    public void getUser(){
        User user = userService.getUser();
        System.out.println(user.toString());
    }
}

使用@Component注解标注该类是受容器管理的类。

userService使用了@Inject自定义注解,表示该属性是容器自动注入该实例,实现IOC功能。

6.IocContext ioc bean容器

public class IocContext {
    /**ioc容器 */
    public static final Map<Class<?>,Object> applicationContext =
            new ConcurrentHashMap<>(64);

    static {
        String packName = "com.damai.ioc";
        // 遍历包下面全部的类,看是否有@Component注解,决定是否需要ioc容器托管
        try{
            initBean(packName);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private static void initBean(String packName){
        try {
            // 加载到ioc 容器
            Enumeration<URL> urls = Thread.currentThread().
                    getContextClassLoader().getResources(packName.replace(".","/"));
            while (urls.hasMoreElements()){
                addClassByAnnotation(urls.nextElement().getPath(),packName);
            }

            // 实现注入功能
            IocUtils.inject();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static void addClassByAnnotation(String path,String packName) throws Exception{
        System.out.println("path : "+path);
        System.out.println("packName : "+packName);
        File[] files = getClassFile(path);

        if(files!=null){
            for(File file : files){
                String fileName = file.getName();
                System.out.println(">>> fileName : "+fileName);
                if(file.isFile()){
                    // 根据类的全名进行加载
                    Class<?> clazz = Class.forName(packName+"."+
                            fileName.substring(0,fileName.lastIndexOf(".")));
                    if(clazz.isAnnotationPresent(Component.class)){
                        // 将类和类的实例加载到容器中
                        applicationContext.put(clazz,clazz.newInstance());
                    }
                }else{
                    addClassByAnnotation(file.getPath(),packName+"."+fileName);
                }
            }
        }
    }

    private static File[] getClassFile(String filePath) {
        return new File(filePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return file.isFile() && file.getName().endsWith(".class") || file.isDirectory();
            }
        });
    }
}

a.扫描加载指定包路径下的所有Class,并判断该Class 是否是@Component注解的类,如果是,则创建实例,并保存到applicationContext缓存中

b.调用IocUtils.inject(),进行依赖注入

7.IocUtils ioc的依赖注入

public class IocUtils {
    public static void inject() {
        System.out.println("IocUtils inject method");
        // 获取ioc 容器中的map
        Map<Class<?>,Object> map = IocContext.applicationContext;
        try {
            // map 的key 主要是已经加载到容器中的class
            for(Map.Entry<Class<?>,Object> entry : map.entrySet()){
                Class<?> clazz = entry.getKey();
                // map 的value 主要是key 对应的实例
                Object obj = entry.getValue();

                Field[] fields = clazz.getDeclaredFields();
                for(Field field :fields){
                    if(field.isAnnotationPresent(Inject.class)){
                        System.out.println(">>> field : "+field.getName()+field.getType());
                        Class<?> fieldClazz = field.getType();
                        field.setAccessible(true);
                        Object fieldObj = map.get(fieldClazz);
                        // 设置后,属性的实例就会注入,而不是null
                        // 将B 对象的实例fieldObj 注入到  A对象的的实例obj 中
                        field.set(obj,fieldObj);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

8.模拟调用UserController

public class Main {
    public static void main(String[] args) {
        UserController userController =
                (UserController) IocContext.applicationContext.get(UserController.class);

        userController.getUser();
    }
}

循环遍历 applicationContext中所有的Bean,判断每个Bean中是否有被@Inject注解修饰的属性,如果有则从applicationContext中获取要注入的实例,并使用反射实现自动注入功能。

结果

path : /D:/ideaStudy/springioc/target/classes/com/damai/ioc
packName : com.damai.ioc
>>> fileName : Component.class
>>> fileName : Inject.class
>>> fileName : IocContext$1.class
>>> fileName : IocContext.class
>>> fileName : IocUtils.class
>>> fileName : Main.class
>>> fileName : User.class
>>> fileName : UserController.class
>>> fileName : UserService.class
IocUtils inject method
>>> field : userServiceclass com.damai.ioc.UserService
User [userName=xl,userAge=30]

?

(编辑:北几岛)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读