Spring的IOC实现原理

IOC容器的启动流程(基于注解)

IOC的基本概念:

IOC:IoC 控制反转 Inverse of Control 创建对象的权限,Java 程序中需要用到的对象不再由程序员自己创建,而是交给 IoC 容器来创建。
IOC的启动流程

1.扫描包的组件:AnnotationConfigApplicationContext,负责扫描指定包下的所有类,将为被@Component注解的类动态创建对象,并完成自动注入;
2.工厂创建对象需要的条件:ClassName(反射创建对象)BeanName(创建对象再工厂中的唯一标识):
  Spring中采用BeanDefinition来封装上述材料;
3.根据上面定义的BeanDefinition来动态创建对象;
4.将创建的对象装载到IOC容器中进行管理;

自己实现IOC的主要流程

1.自定义一个MyAnnotationConfigApplicationContext,构造器中传入要扫描的包名;
2.获取这个包下的所有类;
3.遍历这些类,找到@Component注解的类,获取它的Claa和对应的BeanName,封装为BeanDefinition,存入Set;
4.遍历Set集合,利用反射机制创建对象,同时对Value注解下的属性进行赋值,对Autowired的注解完成自动注入完成赋值,将这些动态生成的对象以key-value的形式存入缓存区;
5.提供getBean方法,通过BeanName方法拿到对应的Bean;

项目的流程图
在这里插入图片描述

自定义注解

@MyComponent

该注解类似Component,包扫描时会将该注解下的类获取

@Target(ElementType.TYPE)//表示该注解作用再类上
@Retention(RetentionPolicy.RUNTIME)//注解在运行时生效
public @interface MyComponent {
    String value() default "";// 指定容器BeanName
}

@MyValue

完成基本类型的属性赋值

@Target(ElementType.FIELD)//表示该注解作用在成员变量上
@Retention(RetentionPolicy.RUNTIME)//运行时注解生效
public @interface MyValue {
    String value();//Value必须手动赋值
}

@MyAutowired

完成引用类型的自动注入:ByType

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}

@MyQualifier

完成引用类型的自动注入:ByName

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyQualifier {
    String value();
}

自定义实体类

Account

@Data
@MyComponent("account")
public class Account {
    @MyValue("1")
    private Integer Id;
    @MyValue("Taoger")
    private String name;
    @MyValue("20")
    private Integer age;
}

Persion

@Data
@MyComponent()
public class Persion {
    @MyAutowired
    //@MyQualifier("account")
    private Account account;
}

自定义BeanDefinition

BeanDefinition用来封装扫描得到的Class和BeanName,这些就是创建对象的基本材料;

@Data
@AllArgsConstructor
public class BeanDefinition {
    private String beanName;
    private Class beanClass;
}

包扫描工具类的编写

这个是套的模板,现在还不是特别熟悉Java的IO流方面的知识,暂且直接调用,感觉个才是这里最难写的,这个工具类的主要作用就是将对应包名下的所有类全部扫描到并封装为Set返回:

public class PackageUtils {
    public static Set<Class<?>> getClasses(String packageName){
        //第一个class类的集合
        Set<Class<?>> classes=new LinkedHashSet<Class<?>>();
        //是否循环迭代
        boolean recursive=true;
        //对传进来的参数进行分割
        String packName=packageName;
        String packDirName=packName.replace('.','/');
        //定义一个枚举的集合并进行循环来处理当前目录
        Enumeration<URL> dirs;
        try {
            dirs=Thread.currentThread().getContextClassLoader().getResources(packDirName);
            //迭代循环
            while(dirs.hasMoreElements()){
                //获取下一个元素
                URL url=dirs.nextElement();
                //得到协议的名称
                String protocol=url.getProtocol();
                //如果是以文件的形式保存在服务器上
                if("file".equals(protocol)){
                    //获取包的物理路径
                    String filePath= URLDecoder.decode(url.getFile(),"UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findClassesInPackageByFile(packageName, filePath, recursive, classes);
                }
                else if ("jar".equals(protocol)){
                    // 如果是jar包文件
                    // 定义一个JarFile
                    System.out.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        findClassesInPackageByJar(packageName, entries, packDirName, recursive, classes);
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packDirName, boolean recursive, Set<Class<?>> classes) {
        // 同样的进行循环迭代
        while (entries.hasMoreElements()) {
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }
            // 如果前半部分和定义的包名相同
            if (name.startsWith(packDirName)) {
                int idx = name.lastIndexOf('/');
                // 如果以"/"结尾 是一个包
                if (idx != -1) {
                    // 获取包名 把"/"替换成"."
                    packageName = name.substring(0, idx).replace('/', '.');
                }
                // 如果可以迭代下去 并且是一个包
                if ((idx != -1) || recursive) {
                    // 如果是一个.class文件 而且不是目录
                    if (name.endsWith(".class") && !entry.isDirectory()) {
                        // 去掉后面的".class" 获取真正的类名
                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                        try {
                            // 添加到classes
                            classes.add(Class.forName(packageName + '.' + className));
                        } catch (ClassNotFoundException e) {
                            // .error("添加用户自定义视图类错误 找不到此类的.class文件");
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }

    private static void findClassesInPackageByFile(String packageName, String filePath, boolean recursive, Set<Class<?>> classes) {
        // 获取此包的目录 建立一个File
        File dir = new File(filePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            @Override
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    // classes.add(Class.forName(packageName + '.' +
                    // className));
                    // 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
   }
}

MyAnnotationConfigApplicationContext(基于注解的扫描组件)

这个就是IOC的核心API,也就是IOC生产对象的工厂,主要功能是根据包名动态生成对象并完成属性注入;

构造器

public class MyAnnotationConfigApplicationContext {

    // 模拟IOC容器
    private Map<String,Object> MyIoc=new HashMap<>();
    //管理BeanNames
    private List<String> beanNames = new ArrayList<>();


    //定义构造器
    public MyAnnotationConfigApplicationContext(String packageName) {
        //遍历包名,拿到对应的BeanDefinition
        Set<BeanDefinition> beanDefinitions=getBeanDefinitions(packageName);

        //根据拿到的BeanDefinition,动态生成对象
        createTargetObject(beanDefinitions);

        //根据拿到的BeanDefinition,完成自动装配
        autowireObject(beanDefinitions);
    }
}

getBeanDefinitions

注意:这里类比Spring,对于未指定BeanName的类名默认为类名的首字母小写;

/*
     将添加了注解的类的class与BeanName封装为BeanDefinition存入Set
     */
    public Set<BeanDefinition> getBeanDefinitions(String  packageName){

        Set<BeanDefinition> beanDefinitions=new HashSet<>();
        //获取包下所有的类
        Set<Class<?>> classes=PackageUtils.getClasses(packageName);

        //遍历Set集合,拿到添加了注解的类
        Iterator<Class<?>> iterator=classes.iterator();

        while(iterator.hasNext()){
          Class<?> clazz=iterator.next();
          //判断是否加上注解
            MyComponent myComponent = clazz.getAnnotation(MyComponent.class);

            //通过myComponent来判断是否加上注解
            if(myComponent!=null){
                //获取注解标记对象在容器中的名字BeanName
                String beanName=myComponent.value();
                //如果用户没有写,默认为类名的首字母小写
                if("".equals(beanName)){
                    //clazz.getName(); 拿到类的全限定名称
                   // clazz.getPackage().getName() 拿到类的包名
                 //  String defaultName=clazz.getName().replaceAll(clazz.getPackage().getName()+".","");
                    String defaultName=clazz.getSimpleName();
                   defaultName= defaultName.substring(0,1).toLowerCase()+defaultName.substring(1);
                   beanName=defaultName;
                }
                BeanDefinition beanDefinition=new BeanDefinition(beanName,clazz);
                beanDefinitions.add(beanDefinition);
            }
        }
        return beanDefinitions;
    }

createTargetObject

坑点:@MyValue注解时赋值的String,对应的属性可能是其他类型,一定要注意类型转换,不然会发生类型转换错误;

/*
      根据拿到的BeanDefinitions根据反射来创建目标对象
     */
    public void createTargetObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while(iterator.hasNext()){
            BeanDefinition beanDefinition=iterator.next();
            //拿到Class
            Class beanClass=beanDefinition.getBeanClass();
            //拿到BeanName
            String beanName=beanDefinition.getBeanName();
            try {
                //根据对象的模板来生产对象,利用反射机制,底层采用无参构造机制
                Object object = beanClass.getConstructor().newInstance();
                //根据注解MyValue来自动装配,完成属性的赋值
                Field[] declaredFields=beanClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    //判断成员变量是否被MyValue注解标识
                    MyValue myValue = declaredField.getAnnotation(MyValue.class);
                    if(myValue!=null){
                        //拿到注解中的赋值,这里用String来接受可能会产生类型不匹配
                        String value=myValue.value();
                      //拿到方法名:
                       String fieldName = declaredField.getName();
                       String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                     // 通过Set方法来赋值
                        Method method = beanClass.getMethod(methodName, declaredField.getType());

                        //完成类型转换,这里只是做了简单的类型处理,真正的IOC需要更完善的类型转换
                        Object val=null;
                        switch (declaredField.getType().getName()){
                            case "java.lang.Integer":
                              val=  Integer.parseInt(value);
                              break;
                            case "java.lang.String":
                                val=  value;
                                break;
                            case "java.lang.Float":
                                val=  Float.parseFloat(value);
                                break;
                        }
                        method.invoke(object,val);
                    }
                }
                //将创建的对象存入IOC容器中
                MyIoc.put(beanName,object);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

autowireObject

注意:这里提供ByName和ByType两种方法

 /*
      根据拿到的原材料进行自动装载
     */
    public void autowireObject(Set<BeanDefinition> beanDefinitions){
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while(iterator.hasNext()){
            BeanDefinition beanDefinition=iterator.next();
            //拿到Class,遍历当前Class每一个属性,判断是否需要自动装载
            Class beanClass=beanDefinition.getBeanClass();
            Field[] declaredFields=beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                MyAutowired myAutowired = declaredField.getAnnotation(MyAutowired.class);
                //该属性需要自动装配
                if(myAutowired!=null){
                    //判断是否通过名字注入
                    MyQualifier myQualifier = declaredField.getAnnotation(MyQualifier.class);
                    //通过名称注入,byName
                    if(myQualifier!=null){
                     //   System.out.println("需要自动装配的属性:"+declaredField);
                        try {
                            String beanName=myQualifier.value();

                            Object autowiredValue = getBean(beanName);
                            //拿到方法名:
                            String fieldName = declaredField.getName();
                            String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                            Method method=beanClass.getMethod(methodName,declaredField.getType());

                            //拿到需要赋值的对象
                            Object object = getBean(beanDefinition.getBeanName());
                            System.out.println("通过ByNameXSx");
                            method.invoke(object,autowiredValue);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                    // 通过类型注入,ByType
                    else{
                        //遍历容器中的所有对象
                        for (String beanName : MyIoc.keySet()) {
                            //如果类型与filed的属性一样
                            if(getBean(beanName).getClass()==declaredField.getType()){
                                //拿到方法名:
                                try {
                                    String fieldName = declaredField.getName();
                                    String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                                    Method method=beanClass.getMethod(methodName,declaredField.getType());
                                    //拿到需要赋值的对象
                                    Object object = getBean(beanDefinition.getBeanName());
                                    System.out.println("通过ByType");
                                    method.invoke(object,getBean(beanName));
                                } catch (NoSuchMethodException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

对外提供的方法

根据key,拿value

 /*
     提供getBean方法,根据在容器中的标记值拿到对应的对象
     */
    public Object getBean(String beanName){
        return MyIoc.get(beanName);
    }
    /*
       提供getBeanDefinitionNames,返回容器中管理对象的BeanName
     */
    public String[] getBeanDefinitionNames(){
        return beanNames.toArray(new String[0]);
    }
    /*
       提供getBeanDefinitionCount,返回容器中管理对象的数量
     */
    public Integer getBeanDefinitionCount(){
        return beanNames.size();
    }

Test

public class Test {
    public static void main(String[] args) {
        MyAnnotationConfigApplicationContext applicationContext=new
                MyAnnotationConfigApplicationContext("com.xu.myspring.entity");
        System.out.println(applicationContext.getBean("persion"));
        //Account account= (Account) applicationContext.getBean("Account");
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringIOC(Inverse of Control)实现原理是通过IOC容器来实现的。IOC容器负责实例化、定位、配置应用程序中的对象,并建立这些对象间的依赖关系,从而实现对象之间的松耦合。 在Spring中,通过配置文件或注解的方式告诉Spring哪些Bean需要进行管理,Spring会根据配置文件或注解来实例化这些Bean,并将它们放入IOC容器中。当我们需要使用这些Bean时,只需从IOC容器中获取即可,而不需要手动创建对象。这样就实现了将控制对象创建的过程反转给Spring容器来管理的效果。 SpringIOC容器充当了一个类似于餐馆的角色,我们只需要告诉Spring哪些Bean需要进行管理,然后通过指定的方式从IOC容器中获取相应的Bean。Spring提供了多种类型的IOC容器,例如基于XML配置的ApplicationContext,基于注解的AnnotationConfigApplicationContext等等。无论使用哪种类型的IOC容器,Spring都会负责创建和管理Bean的生命周期,并根据依赖关系进行自动注入。 总结来说,SpringIOC实现原理是通过IOC容器管理Bean的实例化、定位和配置,实现对象之间的解耦,并提供便利的方式来获取和使用这些Bean。通过IOC容器,我们可以更加灵活地组织和管理应用程序的对象。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springIoc实现原理](https://download.csdn.net/download/zhangcongyi420/11131211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文带你深入剖析Spring IOC 实现原理](https://blog.csdn.net/SQY0809/article/details/118678588)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值