IOC逻辑流程
扫描包
-
在启动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<BeanName,Object>
-
最后一步就是实现自动装配,将之前
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