Spring的三种依赖注入方式的使用和源码实现分析

一、自动依赖注入的方式

注解类型

  • spring提供了基于注解的属性自动注入特性,其中可以可用的注解包括spring自身提供的@Autowired和@Value,其中@Autowired是我们在项目中最常用来注入对象属性的,@Value注解通常用于注入属性文件properties的值,除此之外还可以使用JSR-330提供的注解@Inject,类型为javax.inject.Inject。如下:

    @Component
    public class NettyServer {
        @Autowired
        private WebSocketService webSocketService;
        /**
         * 监听端口号
         */
        @Value("${netty.port}")
        private int port;
        
        // 省略其他代码
    }
    

     

  • 关于以上注解的更多特性可参考:Spring实现依赖注入的三个注解:@Autowired,@Resource,@Inject

注解方式

  • 在使用@Autowired注解进行自动属性注入时,通常可以通过以下三种方式进行配置,分别为:构造函数注入,属性值注入,setter方法注入。如下:

    @RestController
    public class AccountController {
        // 属性值注入
        @Autowired
        private AccountService accountService;
        private UserService userService;
        private IndexService indexService;
        
        // 构造函数注入
        @Autowired
        public AccountController(UserService userService) {
            this.userService = userService;
        }
        
        // setter方法注入
        public void setIndexService(IndexService indexService) {
            this.indexService = indexService;
        }
        
        // 省略其他代码
    }
    

     

  • 当通过构造函数和setter方法进行注入时,由于构造函数和setter方法都可以有多个参数,而@Autowired的required的值又是true,如下为@Autowired注解的定义:

    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    	 // 依赖是必须的
    	boolean required() default true;
    
    }
    

     

  • 即默认需要所有属性值都存在,不能为null,否则会创建对象失败,如下:

    // 构造函数注入
    @Autowired
    public AccountController(UserService userService, IndexService indexService) {
        this.userService = userService;
        this.indexService = indexService;
    }
    

     

  • 所以如果某个属性值不是必须的,则可以使用Java8的Optional或者spring5的@Nullable来修饰,如下假如IndexServer不是必需的:

    • Optional的使用:

      private Optional<IndexService> indexService;
      
      // 构造函数注入
      @Autowired
      public AccountController(UserService userService, Optional<IndexService> indexService) {
          this.userService = userService;
          this.indexService = indexService;
      }
      
      

       

    • @Nullable的使用:

      // 构造函数注入
      @Autowired
      public AccountController(UserService userService, @Nullable IndexService indexService) {
          this.userService = userService;
          this.indexService = indexService;
      }
      

       

  • 如果不想使用以上注解,则可以使用setter方式或属性值注入,或者不要将不需要注入的类放在构造函数或者方法上。

二、自动依赖注入的实现

  • 依赖注入是spring的IOC容器的一个重要特性,通过依赖注入来自动解决类对象之间的引用关系,即由spring来创建bean对象,并且在spring的IOC容器内部自动查找或者创建该bean对象所依赖的其他bean对象,从而保证整个bean对象的属性值的完整性。
  • 由以上分析可知,spring可以基于构造函数注入,属性值注入和setter方法注入,在spring的内部实现当中,这三种方式的实现是存在差别的。

构造函数注入

Java在创建每个对象实例时,都需要调用该对象对应的类的构造函数,所以spring在创建bean对象时,也会选择其中一个构造函数来创建该对象。当该类存在多个构造函数时,只能有一个构造函数使用required为true的@Autowired注解,其他构造函数如果也使用了@Autowired,则需要设置required为false。 

spring选择构造函数的规则

  1. 选择能够成功注入最多bean对象的使用了@Autowired注解的构造函数,即基于贪婪的策略,注意不是选择包含最多参数这么简单,而是能够从spring的IOC容器获取bean对象并注入成功最多的构造函数。
  2. 或者如果某个类的所有构造函数都没有使用@Autowired注解,则spring会使用该类的默认构造函数,即如果没有显式定义任何构造函数,则使用默认的无参构造函数;如果只存在一个,则调用这个;如果存在多个且没有无参构造函数,也没有使用@Autowired注解,则会编译出错或者idea会提示构造函数有误,因为这种方式,spring无法确定使用哪个构造函数。 

以上选择构造函数的构造函数的核心源码实现如下:在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类的createBeanInstance方法定义:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	// Make sure bean class is actually resolved at this point.

	// 类加载
	Class<?> beanClass = resolveBeanClass(mbd, beanName);
    
    // 省略其他代码

	// 查找该类的所有的构造函数,包括使用了@Autowired注解和没有使用@Autowired注解的
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	// 基于贪婪原则选择能注入最多bean对象的构造函数
	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	    // 贪婪选择构造函数,执行构造函数属性注入与创建bean对象		
		return autowireConstructor(beanName, mbd, ctors, args);
	}

	// 指定了特定偏好的构造函数,默认为null
	ctors = mbd.getPreferredConstructors();
	if (ctors != null) {
		return autowireConstructor(beanName, mbd, ctors, null);
	}

	// 使用无参构造函数创建bean对象实例
	return instantiateBean(beanName, mbd);
}

构造函数属性注入

  • 以上分析了spring选择构造函数的规则,对于贪婪选择能注入最多bean对象的构造函数和完成构造函数属性注入,创建bean对象是在AbstractAutowireCapableBeanFactory类的autowireConstructor方法实现的:

    protected BeanWrapper autowireConstructor(
    		String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
    
    	return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
    }
    

     

  • 具体在ConstructorResolver类的autowireConstructor方法实现构造函数的选择和构造函数的属性依赖注入,autowireConstructor的核心实现如下:如果只存在一个显式定义的构造函数,则使用这个构造函数;否则先基于构造函数的参数个数对所有构造函数进行降序排序,然后遍历检查这些构造函数。选中最合适的构造函数后,则进行构造函数的属性对象的注入。注意使用构造函数进行属性注入存在循环依赖问题,具体后面文章详细分析spring的解决方案和无法解决的情况。请参考:Spring的构造函数注入的循环依赖问题

    public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
    		@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
    
    	    // 省略其他代码
    
    		// 只有一个构造函数,则使用该构造函数即可
    		if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
    			Constructor<?> uniqueCandidate = candidates[0];
    			if (uniqueCandidate.getParameterCount() == 0) {
    				synchronized (mbd.constructorArgumentLock) {
    					mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
    					mbd.constructorArgumentsResolved = true;
    					mbd.resolvedConstructorArguments = EMPTY_ARGS;
    				}
    				bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
    				return bw;
    			}
    		}
    
    		// 省略其他代码
    
    		// 基于构造函数的参数个数排序,越多的越前,即降序排序
    		AutowireUtils.sortConstructors(candidates);
    		
    		// 从所有构造函数中,基于贪婪规则筛选出能注入成功最多bean对象的构造函数
    		for (Constructor<?> candidate : candidates) {
    
    			// 构造函数的参数的类型
    			Class<?>[] paramTypes = candidate.getParameterTypes();
    
    			// 省略其他代码
    
    			ArgumentsHolder argsHolder;
    			if (resolvedValues != null) {
    				try {
    					// 省略其他代码
    					// 获取构造函数参数对应的bean对象,在这里解决构造函数依赖注入
    					argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
    							getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
    				}
    				// 省略其他代码
    			}
    		
    		// 省略其他代码
    	bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
    	return bw;
    }
    

     

  • 在以上代码中,主要是在createArgumentArray方法处理构造函数的属性注入问题,最终会调用到BeanFactory的getBean方法从BeanFactory获取所依赖的其他对象。如果BeanFactory当前还不存在该依赖的bean对象,则会在getBean方法中创建该bean对象并返回。所以如果该被依赖的bean对象如果也在构造函数中依赖了当前正在创建的bean对象,则该依赖的bean对象就无法创建了,故出现了循环依赖问题,导致程序异常退出。createArgumentArray的核心实现如下: 

    private ArgumentsHolder createArgumentArray(
    		String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
    		BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
    		boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
    
    	// 省略其他代码
    
    	// 遍历构造函数的参数列表
    	for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
    	
    		// 省略其他代码
    		
    			try {
    				// 处理@Autowired自动注入的对象属性
    				Object autowiredArgument = resolveAutowiredArgument(
    						methodParam, beanName, autowiredBeanNames, converter, fallback);
    				args.rawArguments[paramIndex] = autowiredArgument;
    				args.arguments[paramIndex] = autowiredArgument;
    				args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
    				args.resolveNecessary = true;
    			}
    			
    			// 省略其他代码
    			
    		}
    	}
    
    	// 省略其他代码
    
    	return args;
    }
    

     

属性值注入和setter方法注入

  • 属性值注入和setter方法注入是spring在创建该bean对象成功后,即调用构造函数创建了bean对象之后,在对该bean对象的属性值进行赋值时处理的,故属性值注入和setter方法注入不存在循环依赖问题,因为此时对象已经创建成功了,在这步进行属性注入主要是避免依赖的属性值为null。具体的方法调用顺序为:

    1. AbstractBeanFactory的getBean方法:getBean调用doGetBean方法,doGetBean方法内部调用createBean方法。

      public Object getBean(String name) throws BeansException {
      	return doGetBean(name, null, null, false);
      }
      
      protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
          // 省略其他代码
      	if (mbd.isSingleton()) {
      		// 创建bean对象实例并注册到单例bean映射map中
      		sharedInstance = getSingleton(beanName, () -> {
      			try {
      				// bean对象实例创建
      				return createBean(beanName, mbd, args);
      			}
      		}
      	}
      	// 省略其他代码
      }
      

       

    2. AbstractAutowireCapableBeanFactory类定义createBean的方法实现,在createBean方法内部调用doCreateBean方法完成bean对象的创建,包括调用populateBean方法进行属性赋值。populateBean方法的定义如下:

      // 属性值赋值
      protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
      	// 省略其他代码
      	if (hasInstAwareBpps) {
      		if (pvs == null) {
      			pvs = mbd.getPropertyValues();
      		}
      
      		// 调用InstantiationAwareBeanPostProcessor的postProcessProperties和postProcessPropertyValues
      		// 即AutowiredAnnotationBeanPostProcessor
      		for (BeanPostProcessor bp : getBeanPostProcessors()) {
      			if (bp instanceof InstantiationAwareBeanPostProcessor) {
      				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
      
      				// 进行属性处理,包括属性注入和setter方法注入
      				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      				
      				// 省略其他代码
      			}
      		}
      	}
      	
      	// 省略其他代码
      }
      

       

    3. 所以属性值注入和setter方法注入是在AutowiredAnnotationBeanPostProcessor这个类的postProcessProperties方法处理的,AutowiredAnnotationBeanPostProcessor是一个BeanPostProcessor,在创建BeanFactory对象时会创建该BeanPostProcessor对象。

  • 在AutowiredAnnotationBeanPostProcessor内部是先进行属性注入,在进行方法注入,核心实现如下:

    1. 属性和setter方法进行属性值注入

      // 属性和setter方法进行属性值注入
      @Override
      public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
      	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
      	try {
      	    // 依赖注入
      		metadata.inject(bean, beanName, pvs);
      	}
      	catch (BeanCreationException ex) {
      		throw ex;
      	}
      	catch (Throwable ex) {
      		throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
      	}
      	return pvs;
      }
      
      

       

    2. AutowiredAnnotationBeanPostProcessor内部初始化postProcessProperties中所使用的InjectionMetadata的方法:其中属性值注入是在AutowiredFieldElement定义的,方法注入是在AutowiredMethodElement定义的,这两个都是AutowiredAnnotationBeanPostProcessor的内部类。由以下代码可知,在elements数组中,属性值解析器AutowiredFieldElement在数组前面,故先遍历到;方法注入解析器AutowiredMethodElement在数组后面,故后遍历到。

      private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
      	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
      	Class<?> targetClass = clazz;
      
      	do {
      		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
      		
              // 1. 添加属性注入解析器AutowiredFieldElement到elements
      		ReflectionUtils.doWithLocalFields(targetClass, field -> {
      			AnnotationAttributes ann = findAutowiredAnnotation(field);
      			if (ann != null) {
      				// 省略其他代码
      				boolean required = determineRequiredStatus(ann);
      				// 属性注入解析器AutowiredFieldElement
      				currElements.add(new AutowiredFieldElement(field, required));
      			}
      		});
      
              // 2. 添加方法注入解析器AutowiredMethodElement到elements
      		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
      			// 省略其他代码
      			AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
      			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
      				// 省略其他代码
      				boolean required = determineRequiredStatus(ann);
      				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
      				// 方法注入解析器AutowiredMethodElement
      				currElements.add(new AutowiredMethodElement(method, required, pd));
      			}
      		});
      
      		elements.addAll(0, currElements);
      		targetClass = targetClass.getSuperclass();
      	}
      	while (targetClass != null && targetClass != Object.class);
      
      	return new InjectionMetadata(clazz, elements);
      }
      
      

       

    3. InjectionMetadata的解析过程:遍历elements并调用AutowiredFieldElement或者AutowiredMethodElement的inject方法。AutowiredFieldElement先遍历到,AutowiredMethodElement后遍历到,故先进行属性注入,在进行方法注入。

      public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
      	Collection<InjectedElement> checkedElements = this.checkedElements;
      	Collection<InjectedElement> elementsToIterate =
      			(checkedElements != null ? checkedElements : this.injectedElements);
      	if (!elementsToIterate.isEmpty()) {
      	
      	    // 遍历elements并调用对应的inject方法
      		for (InjectedElement element : elementsToIterate) {
      			if (logger.isTraceEnabled()) {
      				logger.trace("Processing injected element of bean '" + beanName + "': " + element);
      			}
      			element.inject(target, beanName, pvs);
      		}
      	}
      }
      

       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring依赖注入(Dependency Injection,简称DI)和控制反转(Inversion of Control,简称IoC)是相关且密切关联的概念。依赖注入是指将一个对象的依赖关系从代码移除,而由容器负责创建和注入这些依赖对象。控制反转则是指将对象的创建和生命周期的管理交给容器来处理,而不是由代码直接控制。 在Spring,IoC容器(例如ApplicationContext)负责实例化、配置和连接bean。当我们使用依赖注入时,我们只需要在需要注入的地方声明相应的依赖,而无需自己负责创建或查找依赖的实例。IoC容器会负责根据配置文件或注解信息,自动将依赖注入到相应的位置。这样可以实现松散耦合,提高代码的可测试性和可维护性。 Spring依赖注入和控制反转的好处在于它们解耦了类之间的依赖关系,提高了代码的灵活性和可扩展性。我们可以通过配置文件或注解来管理对象之间的依赖关系,而不需要修改源代码。这样,我们可以更方便地进行组件替换、模块重用和单元测试。 总结起来,Spring依赖注入和控制反转是一种设计模式,它们通过将对象的依赖关系交给容器来管理,提高了代码的可测试性、可扩展性和可维护性。通过使用IoC容器,我们可以简化对象的创建和配置过程,实现松散耦合,提升了应用程序的灵活性。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值