对于Spring自动注入模型你了解多少

引言

如果把一套代码比作秋名山430KM/H高速行驶的布加迪威龙16.4,毫不夸张的说Spring就是整个8.0升W16缸4涡轮增压的发动机的启动器,每每拾起Spring源码开始读,耳畔就布满了汹涌翻滚的声浪,像蓄势待发的猛兽不断冲击我骚动的内心。

看到这大家是不是跟我一样急需想轰一脚油门了。

须知

spring-framework 中逻辑都是串联交织的,一个节点啃完了,中间必定新冒出一个节点。在源码里面,无论你是英语6级还是8级,统统都要忘记,因为英文片面的解释不足以证明问题反而会对造成困扰,就像一杯热气腾腾的咖啡,只有品的醇香才能敲击你的味蕾。这里我的宗旨就是以最少的语言解释更多的事情

@Autowired

@Autowired大家已经不能再熟悉了吧,看大家的理解是不是跟我一样。@Autowired属于注入方式(Setter、Constructor、@Autowired)的其中一种,如果Bean在 单例池 中,可以通过@Autowired将某一个Bean注入(循环依赖 又是另一回事了),但是@Autowired跟自动注入模型有关系吗?

我们做一个测试:

//配置类
@ComponentScan("com.practice")
//@ImportResource("SpringConfig.xml")
public class AppConfig {
}

@Component
public class DavisService {

	public DavisService() {
		System.out.println("davis constructor");
	}

	public void downtown() {
		System.out.println("davis downtown");
	}
}

@Component
public class JamesService {

	@Autowired
	private DavisService davisService;

	public JamesService() {
		System.out.println("james constructor");
	}

	public void win() {
		davisService.downtown();
		System.out.println("Los has win the Boston");
	}
}
public class ContextTest {

	public static void main(String[] args) {
		ApplicationContext app
				= new AnnotationConfigApplicationContext(AppConfig.class);

		app.getBean(JamesService.class).win();
	}
}

描述: 将两个service添加到 容器 当中,jamesService注入davisService,也就是说jamesService依赖davisService。
执行结果:

davis constructor
james constructor
davis downtown
Los has win the Boston

简单描述: 实例化顺序spring有他自己的规则,但是先初始化davis 和先初始化james 是有区别的。jamesService注入davisService,如果在初始化jamesService注入davisService当中发现davisService没有被实例化,那么就会去创建bean,然后注入。如果davisService已经在 单例池,那么直接注入。帮助大家有个印象,不做深入。
在这里插入图片描述

我们来一波小操作看一下自动注入模型:

@Component
public class DoMyProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		GenericBeanDefinition beanDefinition = 
				(GenericBeanDefinition) beanFactory.getBeanDefinition("jamesService");
		System.out.println("自动注入模型: " + beanDefinition.getAutowireMode());
	}
}

描述: 将自定义后置处理器加入 容器 当中,Spring会专门处理,Spring的后置处理器非常多,包括参与扫描、beanDefinition的实例化、Bean的生命周期、属性注入、循环依赖、AOP等等。

运行看打印结果:

自动注入模型: 0
davis constructor
james constructor
davis downtown
Los has win the Boston

源码中对自动注入模型的标识:

/**
	 * Constant that indicates no externally defined autowiring. Note that
	 * BeanFactoryAware etc and annotation-driven injection will still be applied.
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
	int AUTOWIRE_NO = 0;

	/**
	 * Constant that indicates autowiring bean properties by name
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
	int AUTOWIRE_BY_NAME = 1;

	/**
	 * Constant that indicates autowiring bean properties by type
	 * (applying to all bean property setters).
	 * @see #createBean
	 * @see #autowire
	 * @see #autowireBeanProperties
	 */
	int AUTOWIRE_BY_TYPE = 2;

	/**
	 * Constant that indicates autowiring the greediest constructor that
	 * can be satisfied (involves resolving the appropriate constructor).
	 * @see #createBean
	 * @see #autowire
	 */
	int AUTOWIRE_CONSTRUCTOR = 3;

官网有提到过关于自动注入模型:
在这里插入图片描述
也就是说在我们的xml配置当中可以指定autowire这个bean定义标签的模型,模型具体四种:

no在这里插入图片描述
描述:(默认)没有自动装配。Bean引用必须由ref标签指定,官方建议推荐使用默认,为了可观性强,项目庞大之后,就不知道谁依赖谁了。

举例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean class="com.practice.service.JamesService" id="jamesService" >
		<property name="davisService" ref="davisService"/>
	</bean>

	<bean class="com.practice.service.DavisService" id="davisService"></bean>
</beans>
//@Component
public class JamesService {


	private DavisService davisService;

	public void setDavisService(DavisService davisService) {
		this.davisService = davisService;
	}

	public JamesService() {
		System.out.println("james constructor");
	}

	public void win() {
		davisService.downtown();
		System.out.println("Los has win the Boston");
	}
}
//@Component
public class DavisService {

	public DavisService() {
		System.out.println("davis constructor");
	}

	public void downtown() {
		System.out.println("davis downtown");
	}
}

执行结果:

自动注入模型: 0
james constructor
davis constructor
davis downtown
Los has win the Boston

结论: 采用默认方式,不自动装配,自动注入模型标识为0,证明 @Autowired 没有定义自动装配,自动装配模型为 no

byName在这里插入图片描述
> 描述: 通过属性名自动装配。Spring按照bean的名字找,setName中Name和xml中id值一定不要写错,否则Spring找不到也就无法注入(空指针异常)。

举例:
在这里插入图片描述
在这里插入图片描述
执行结果:

自动注入模型: 1
james constructor
davis constructor
davis downtown
Los has win the Boston

结论: byName自动注入模型比较苛刻,注意Name不要写错。

byType
在这里插入图片描述
描述: 容器中存有该类型得bean只有一个,则允许获取属性类型,如果存在多个相同类型的bean,就会报异常。

举例:
在这里插入图片描述

public interface NBAService {
}

public class DavisService implements NBAService {

	public DavisService() {
		System.out.println("davis constructor");
	}

	public void downtown() {
		System.out.println("davis downtown");
	}
}
//@Component
public class JamesService {


	private NBAService nbaService;

	public void setNbaService(NBAService nbaService) {
		this.nbaService = nbaService;
	}

	public JamesService() {
		System.out.println("james constructor");
	}

	public void win() {
//		davisService.downtown();
		System.out.println("Los has win the Boston");
	}
}

执行结果:

自动注入模型: 2
james constructor
davis constructor
Los has win the Boston

如果相同类型有多个实例呢?
在这里插入图片描述

public class IrvingService implements NBAService {

	public IrvingService() {
		System.out.println("irving constructor");
	}
}

直接报错:
在这里插入图片描述

constructor
在这里插入图片描述
描述: 适用于构造函数参数。

举例说明:
在这里插入图片描述

//@Component
public class JamesService {


	private DavisService davisService;

	public JamesService(DavisService davisService) {
		System.out.println("james constructor");
		this.davisService = davisService;
	}


	public void win() {
//		davisService.downtown();
		System.out.println("Los has win the Boston");
	}
}

打印结果:

自动注入模型: 3
davis constructor
james constructor
Los has win the Boston

如果存在相同类型多个实例,Spring也会报错:
在这里插入图片描述

//@Component
public class JamesService {


	private NBAService nbaService;

	public JamesService(NBAService nbaService) {
		System.out.println("james constructor");
		this.nbaService = nbaService;
	}


	public void win() {
//		davisService.downtown();
		System.out.println("Los has win the Boston");
	}
}

执行结果:
在这里插入图片描述

源码解析

根据 constructor 自动注入模型,我们做一下源码分析:
BeanDefinitionParserDelegate这个类当中,解析标签,获取属性值,这些属性描述了一个类的状态,因为此时还没有被实例化。

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

		if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
			error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
		}
		//单例还是原型
		else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
			bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
		}
		else if (containingBean != null) {
			// Take default from containing bean in case of an inner bean definition.
			bd.setScope(containingBean.getScope());
		}

		if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
			bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
		}
		//是否懒加载
		String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
		if (isDefaultValue(lazyInit)) {
			lazyInit = this.defaults.getLazyInit();
		}
		bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
		//设置自动注入模型
		String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
		bd.setAutowireMode(getAutowireMode(autowire));
		//......
		//省略了N多代码

		return bd;
	}

在这里插入图片描述
上面主要是实例化jamesService之前,对beanDefinition的一些属性设置操作,在实例化过程中又是怎么做的?
AbstractAutowireCapableBeanFactory 主要完成了推断构造方法,属性注入,循环依赖,AOP。我们看一下关键步骤:

在这里插入图片描述

总结: 将xml配置文件中的节点进行解析,获取属性值,将自动注入模型设置到BeanDefinition当中,代表对类的一种描述,当该类在实例化的时候获取自动注入模型标识,判断数值是否为3,如果为3,jamesService将以构造器形式注入davisService。

补充:
如果不是constructor注入模型呢,该如何判断?
我们以byType为例:
在这里插入图片描述

往下走:
在这里插入图片描述
获取set方法,判断Setter语法是否正确,以及对set方法参数的处理。
在这里插入图片描述

最后还是通过大家熟悉的 method.invoke((Object obj, Object… args) 方法执行完成:
在这里插入图片描述

这时我们再回头看AbstractAutowireCapableBeanFactorypopulateBean 方法:
在这里插入图片描述
到这里完成自动注入模型byType属性注入。

总结: 如果说道格 Doug Lea的Lock是细节简化艺术,那么Spring FrameWork就是思维逻辑艺术,或者说两者完美融合,就像一杯热气腾腾的咖啡,需要通过精细研磨才能发挥出他最大的香气。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值