spring源码分析-依赖注入

154 阅读4分钟

什么是Spring 框架中的控制反转 (IoC)和依赖注入 (DI)?

  • spring官网说依赖注入(DI)是一种过程,是spring在创建bean过程中,对bean中依赖属性的填充的一种过程,是控制反转(ioc:控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理)的具体实现。使用 DI 原则,代码更清晰,当对象提供其依赖时,解耦更有效。对象不查找其依赖,也不知道依赖的位置或类。

spring-di.png

依赖注入的方式有两种,构造方法注入和setter方法注入

  • 构造方法注入

    • 基于xml实现
      <beans> 
         <bean id="beanOne" class="x.y.ThingOne">
              <constructor-arg ref="beanTwo"/> <constructor-arg ref="beanThree"/> 
         </bean> 
         <bean id="beanTwo" class="x.y.ThingTwo"/>
         <bean id="beanThree" class="x.y.ThingThree"/> 
      </beans>
      
    • 基于java代码方式
         public class ThingOne { 
             
             //1.该构造方法注入时,容器中必须有ThingTwo,ThingThree相对应的bean
             public ThingOne(ThingTwo thingTwo, ThingThree thingThree) { // ... 
             } 
         }
      
  • setter方法注入

    • 实现对于属性的setter方法
          public class SimpleMovieLister { 
      
              // the SimpleMovieLister has a dependency on the MovieFinder
              private MovieFinder movieFinder;
          
              // a setter method so that the Spring container can inject a MovieFinder
             public void setMovieFinder(MovieFinder movieFinder) { 
                 this.movieFinder = movieFinder; 
             }
              
         // business logic that actually uses the injected MovieFinder is omitted... 
        }
          
      
  • spring注入模型spring一共有四种,作为静态常量定义在AbstractBeanDefinition类当中,默认为手动注入AUTOWIRE_NO:0

    • 手动注入 AbstractBeanDefinition.AUTOWIRE_NO
    • 自动注入通过名字 AbstractBeanDefinition.AUTOWIRE_BY_NAME
    • 自动注入通过类型 AbstractBeanDefinition.AUTOWIRE_BY_TYPE
    • 自动注入通过构造方法 AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR
  • 什么时候采用构造方法注入或者setter注入?

  • 注入模型和查找bean的方式不能混为一谈,注入模型做给bean的一个特征会影响bean的一些行为、查找bean的方式是一种找到某个bean的策略

  • @Autowired,@Resource,@Order,@Lookup注解源码分析

    • @Autowired
      • 只能对非静态的方法或属性进行注入,会根据类型进行查找bean,如果有多个的话,放入一个map中然后通过bean的名称去map中获取,不存在时抛异常
    • @Resource
      • 在没有配置name的情况下首先根据名字查找;如果名字能查找到则返回这个查找到的(spring容器的原则是name唯一;所以不存在通过名字能查找到多个的情况);如果通过名字查找不到(需要注意的是这里的前提是没有配置name的情况,spring觉得名字无所谓);因为对名字无要求,所以会再根据类型查找;那么走的就是@Autowired这一套;
      • 如果配置了名字,spring觉得对名字有严格要求,所以只能根据你配置的名字查找;如果查找不到则报错,找到了则用;不会走@Autowired这一套了
    • 源码解析

      • spring在初始化的过程中,在管理bean的生命周期会有两个类CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor类分别完成@Resource和@Autowired注解的解析和处理

@Resource与@Autowired源码解析.png

  • @Order
    • @Order注解spring如没有特殊处理是不会影响类的实例化和扫描顺序;
      • 比如spring Aop当中通知的执行对于这个注解做了特殊处理所以这个注解能影响通知的执行顺序;但是大多数情况下这个注解是没有被特殊处理的;比如你给一个bean上面加这个注解,基本是没什么效果的;
      • 需要说的是如果你给一个数组或者List集合当中注入一些bean;那么这些bean在被注入的时候会根据注解的值进行排序进入集合当中;值越小则顺序越高
      • @Order的优先级低于Ordered低于PriorityOrdered;换言之上面说集合当中的类的顺序如果实现了PriorityOrdered接口则先按PriorityOrdered的规则排序,如果没有实现PriorityOrdered,但是实现了Ordered则按Ordered排序,如果么有实现Ordered最后才是@Order
    • spring加载类顺序=spring扫描+spring实例化bean
      • 无论是spring的扫描还是spring实例化以个类顺序都是默认按照类名的字母顺序来的;和bean的名字无关;
  • @Lookup
    • 如果在一个单例bean当中多次获取一个原型的bean对象时会得到相同的原型bean对象

      • 解决办法、可以单例类实现ApplicationContextAware接口,从而获取ApplicationContext对象;每次在单例当中获取原型bean对象的时候通过ApplicationContext对象调用getBean()方法获取;这每次获取出来的原型对象都是通过spring容器产生的;达到原型的目的;但是这种方法的依赖性太强
      • 解决方法2、使用@Lookup注解,@Lookup的原理就是代理,也就是CommandManager这个类被代理了(cglib)createCommand()方法被增强了;每次调用createb方法都会返回一个新的Command对象
      public abstract class CommandManager { 
              public Object process(Object commandState) { 
                  Command command = createCommand(); 
                  command.setState(commandState); 
                  return command.execute(); 
              }
              @Lookup("myCommand") 
              //@Lookup都行
              protected abstract Command createCommand(); 
         }