spring-IOC原理分析,手写分析

IOC逻辑流程

spring-IOC原理分析,手写分析_第1张图片

扫描包

  • 在启动spring项目时,需要加载注解容器,所有的对象创建都在该容器启动时完成。在该容器启动时,需要指定扫描注解的包

  • 	MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext("com.pmy.myspring.entity");
    
  • 在该类的构造函数中,便是IOC容器初始化的全过程

  • 		//遍历包,找到目标类(原材料)
            Set<BeanDefinition> beanDefinitions = findBeanDefinitions(pack);
            //根据原材料创建bean
            createObject(beanDefinitions);
            //自动装载
            autowireObject(beanDefinitions);
    
  • 首先是扫描包,获取包下所有类的class对象

  • 然后遍历这些类,找到拥有注解的类(这里是检查的@Component注解)

  • 即遍历所有扫描到的class对象,判断其是否有注解,如果有,则查看其中是否有指定BeanName,如果没有,则用类名首字母转小写来表示

  •   	     Class<?> clazz = iterator.next();
             Component componentAnnotation = clazz.getAnnotation(Component.class);
             if(componentAnnotation!=null){
                 //获取Component注解的值
                 String beanName = componentAnnotation.value();
                 if("".equals(beanName)){
                     //获取类名首字母小写
                     String className = clazz.getName().replaceAll(clazz.getPackage().getName() + ".", "");
                     beanName = className.substring(0, 1).toLowerCase()+className.substring(1);
                 }
                 //3、将这些类封装成BeanDefinition,装载到集合中
                 beanDefinitions.add(new BeanDefinition(beanName, clazz));
                 beanNames.add(beanName);
    
  • 这里的@Component注解为自定义

  •     @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface Component {
            String value() default "";
        }
    
  • 在上步操作中,获取到了所有注解的BeanDefinitions

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

创建对象,注入基本类型属性

  • 然后就是根据这些BeanDefinitions来创建具体的单例对象了

  • 遍历所有的BeanDefinition,然后根据其中的Class对象,使用反射来生成对象

        //创建的对象
        Object object = clazz.getConstructor().newInstance();
    
  • 因为该对象中用了value注解,和Autowired,Qualifier注解,所以这里需要自动装配(自动装配在后面讲)

  •     @Data
        @Component
        public class Account {
            @Value("1")
            private Integer id;
            @Value("张三")
            private String name;
            @Value("22")
            private Integer age;
            @Autowired
            @Qualifier("myOrder")
            private Order order;
        }
    
  • 所以这里在创建了对象之后,要对该对象的属性进行赋值

  •     Value valueAnnotation = declaredField.getAnnotation(Value.class);
        if(valueAnnotation!=null){
            String value = valueAnnotation.value();
            String fieldName = declaredField.getName();
            String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
            Method method = clazz.getMethod(methodName,declaredField.getType());
            //完成数据类型转换
            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);
    
  • 这里是通过反射来获取属性的名称与set方法(因为是javaBean,所以set方法就是set属性名),然后对数据类型进行转换,之后使用反射来进行赋值

  • 最终,将创建好的对象放入IOC容器

自动装配

  • 此处的IOC容器是一个单级的HashMap

  • 最后一步就是实现自动装配,将之前Account类的属性order依赖注入(自动装配就是将一个Bean注入到其它的Bean的Property中)

  • 实现方式是,遍历BeanDefinition,获取其中的每个字段,看看其中是不是含有@Autowired注解,然后将其中的所需要注入对象根据BeanName从IOC容器中取出来注入

  • 	for (Field declaredField : declaredFields) {
        	Autowired annotation = declaredField.getAnnotation(Autowired.class);
            	if(annotation!=null){
                	Qualifier qualifier = declaredField.getAnnotation(Qualifier.class);
                    if(qualifier!=null){
                    //byName
                    try {
                    	String beanName = qualifier.value();
                        Object bean = getBean(beanName);
                        String fieldName = declaredField.getName();
                        String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
                        Method method = clazz.getMethod(methodName, declaredField.getType());
                        Object object = getBean(beanDefinition.getBeanName());
                        method.invoke(object, bean);
    

手写spring效果

  • 实现结果,运行IOC容器,将IOC容器中的对象打印出来

  • account
    Account(id=1, name=张三, age=22, order=Order(orderId=xxx123, price=1000.5))
    myOrder
    Order(orderId=xxx123, price=1000.5)
    

存在问题

  • 这里并不存在循环依赖问题,因为这里实际将单例对象的创建和属性赋值给分割开了。
  • 所以,虽然是单级的容器,但是由于上面说的缘故,并不会产生循环依赖。

参考资料

https://www.bilibili.com/video/BV1AV411i7VH

你可能感兴趣的:(框架学习,java,反射,ioc,spring,annotation)