Spring Bean注入的另一种方式-AutowireMode的一些理解

也可以参考:https://blog.csdn.net/qq_27409289/article/details/100753656

Mybatis源码中的应用可以参考这篇文章:https://blog.csdn.net/z69183787/article/details/104544276

先简单说一下原理,大家也可以自己看看

1、设置了AutowiredMode之后,在最后生成Bean实例并注入属性的过程中,这个方法

AbstractAutowireCapableBeanFactory#populateBean 设置属性时

2、对AutowiredMode进行了解析和判断,实现了在不加@Autowired及@Resource注解的情况下,成功注入Bean实例

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}

autowireByType:利用BeanUtils根据PropertyDescription属性描述符获取属性的write方法进行注入,最后获取到实例放入Bean的属性MutablePropertyValues中:

protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}

		Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
		for (String propertyName : propertyNames) {
			try {
				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
				// Don't try autowiring by type for type Object: never makes sense,
				// even if it technically is a unsatisfied, non-simple property.
				if (Object.class != pd.getPropertyType()) {
					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
					// Do not allow eager init for type matching in case of a prioritized post-processor.
					boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
					if (autowiredArgument != null) {
						pvs.add(propertyName, autowiredArgument);
					}
					for (String autowiredBeanName : autowiredBeanNames) {
						registerDependentBean(autowiredBeanName, beanName);
						if (logger.isDebugEnabled()) {
							logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
									propertyName + "' to bean named '" + autowiredBeanName + "'");
						}
					}
					autowiredBeanNames.clear();
				}
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}

--------------------------------------------------------------------------------------------------

灵魂拷问:

不使用@Autowired、 @Resource、@Inject 注解,可以正常注入Bean吗?

查看Spring官方文档时,发现以下原文:
1.4.5. Autowiring Collaborators
    When using XML-based configuration metadata (see Dependency Injection), you can specify the autowire mode for a bean definition with the autowire attribute of the element. The autowiring functionality has four modes. You specify autowiring per bean and can thus choose which ones to autowire.
    使用基于XML的配置元数据时(请参阅“依赖注入”),可以使用元素的autowire属性为bean定义指定自动装配模式。自动装配功能具有四种模式。您可以为每个bean指定自动装配,因此可以选择要自动装配的装配。

在这之前我从来没怀疑过上面的那个问题。
看到这里我去翻了一下源码:

public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
复制代码

原来他默认支持五种装配模式,但是其中一种已经摒弃,不在推荐使用。其实笔者推荐都不要使用,实际开发中只使用默认的AutowireCapableBeanFactory.AUTOWIRE_NO装配模式。
官方文档有以下说明:
对于较大的部署,建议不要更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。 在某种程度上,它记录了系统的结构。

ModeExplanation
no(Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
byNameAutowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property.
byTypeLets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set).
constructorAnalogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.

Limitations and Disadvantages of Autowiring Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions.
Consider the limitations and disadvantages of autowiring:
Explicit dependencies in property and constructor-arg settings always override autowiring. You cannot autowire simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design.
Autowiring is less exact than explicit wiring. Although, as noted in the earlier table, Spring is careful to avoid guessing in case of ambiguity that might have unexpected results. The relationships between your Spring-managed objects are no longer documented explicitly.
Wiring information may not be available to tools that may generate documentation from a Spring container.
Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Map instances, this is not necessarily a problem. However, for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown.
In the latter scenario, you have several options: Abandon autowiring in favor of explicit wiring.
Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false, as described in the next section.
Designate a single bean definition as the primary candidate by setting the primary attribute of its element to true.
Implement the more fine-grained control available with annotation-based configuration, as described inAnnotation-based Container Configuration.

一句话翻译:使用注解显式的进行装配,是最常用也最安全高效的方式
我们验证一下是否可以在不使用注解的情况下实现注入:

package com.spring.post_processor.bean_factory;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;  

// 配置类
@Configuration
@ComponentScan("com.spring.post_processor.bean_factory")
public class Config {
}
复制代码
package com.spring.post_processor.bean_factory;

import org.springframework.stereotype.Component;

@Component
public class Consumer {

    private Providera providera;
    private Providerb providerb;

    public void setProvidera(Providera a) {
        this.providera = a;
    }

    public void setProviderb(Providerb b) {
        this.providerb = b;
    }

    public void providerSout () {
        System.out.println(providera);
        System.out.println(providerb);
    }

}
复制代码
package com.spring.post_processor.bean_factory;

import org.springframework.stereotype.Component;

@Component
public class Providera {

}

package com.spring.post_processor.bean_factory;

import org.springframework.stereotype.Component;

@Component
public class Providerb {

}
复制代码
package com.spring.post_processor.bean_factory;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 调用consumer的providerSout方法,
 * 如果已经成功注入必然可以打印出对象的hash值
 * @Author: Raphael
 */
public class ProcessorMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(Config.class);
        Consumer consumer = context.getBean(Consumer.class);
        consumer.providerSout();
        System.err.println(context.getBean(Providera.class));
        System.err.println(context.getBean(Providerb.class));
        context.close();
    }
}
复制代码

但是事实上并没有如我所愿,实际上注入的对象全部为null.

 

其实这样才是合理的,因为我们并没有修改Consumer的装配模式,所以自然是获取不到想要装载的对象的。
那怎么才可以达到自己的目的呢?

 

package com.spring.post_processor.bean_factory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.stereotype.Component;

/**
 * 实现{@link BeanFactoryPostProcessor}接口获取Bean的定义信息
 * 修改Bean的装配模型
 * @Author: Raphael
 */
@Component
public class BeanFactoryProcessor implements BeanFactoryPostProcessor {

    private static final String BEAN_NAME = "consumer";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
            throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)beanFactory.getBeanDefinition(BEAN_NAME);
        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }

}
复制代码

不信你看,现在就一定可以了。

 

只是简单的谈论一下Spring的多种装配模式,日常开发中使用的频率并不高。实际上这一块的内容在Mybatis源码中会有相关应用。

 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值