spring 对于注入的理解

"注入"的概念

本文讨论的"注入",指的是spring给Bean注入属性(也可以称之为"装配"),并非其他注入。

与"注入"相关的概念

配置spring容器有三种风格:xml、annotation和java config,当然也可以结合使用。其中java config风格注册的Bean由程序员自己管理,因此不在本文讨论范围之内。
spring给Bean注入属性有几种方式:通过构造器注入、通过属性的set方法注入、使用反射注入。
无论通过以上哪种方式进行注入,如果spring依赖程序员提供的配置信息,那么就是手动注入(也可以称之为"手动装配");如果spring不依赖任何配置信息,而是通过自动注入模型完成注入,那么就是自动注入(也可以称之为"自动装配"),因此spring注入策略可以分为以下四种:

  • xml风格依赖配置信息完成注入
  • xml风格使用自动注入模型完成注入
  • annotation风格依赖配置信息完成注入
  • annotation风格使用自动注入模型完成注入
xml风格依赖配置信息完成注入demo
public class XmlBeanA {
	private XmlBeanB xmlBeanB;

	public XmlBeanB getXmlBeanB() {
		return xmlBeanB;
	}

	public void setXmlBeanB(XmlBeanB xmlBeanB) {
		this.xmlBeanB = xmlBeanB;
	}

	public XmlBeanA() {
		System.out.println("new XmlBeanA()");
	}
}
public class XmlBeanB {
	public XmlBeanB() {
		System.out.println("new XmlBeanB()");
	}
}

// xmlspringtest.xml
<beans>	
	<!--xml配置文件描述Bean之间的引用关系,让spring知道如何完成属性注入-->
	<bean id="xmlBeanA" class="com.tbryant.springtest.xmlspringtest.XmlBeanA">
		<property name="xmlBeanB" ref="xmlBeanB"></property>
	</bean>
	<bean id="xmlBeanB" class="com.tbryant.springtest.xmlspringtest.XmlBeanB">
	</bean>
</beans>

// 启动类
public class XmlApplication {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
		context.setConfigLocation("classpath:xmlspringtest.xml");
		context.refresh();
		XmlBeanA beanA = (XmlBeanA) context.getBean("xmlBeanA");
		XmlBeanB beanB = (XmlBeanB) context.getBean("xmlBeanB");
		System.out.println(beanA);
		System.out.println(beanA.getXmlBeanB());
		System.out.println(beanB);
	}
}

运行结果
new XmlBeanA()
new XmlBeanB()
com.tbryant.springtest.xmlspringtest.XmlBeanA@59ec2012
com.tbryant.springtest.xmlspringtest.XmlBeanB@4cf777e8
com.tbryant.springtest.xmlspringtest.XmlBeanB@4cf777e8

xml风格使用自动注入模型完成注入demo
// class XmlBeanA、class XmlBeanB和启动类同上
<beans default-autowire="byType">
	<!--xml配置文件只注册Bean,无需描述Bean之间的引用关系-->
	<bean id="xmlBeanA" class="com.tbryant.springtest.xmlspringtest.XmlBeanA">
	</bean>
	<bean id="xmlBeanB" class="com.tbryant.springtest.xmlspringtest.XmlBeanB">
	</bean>
</beans>

运行结果
new XmlBeanA()
new XmlBeanB()
com.tbryant.springtest.xmlspringtest.XmlBeanA@5cb9f472
com.tbryant.springtest.xmlspringtest.XmlBeanB@cb644e
com.tbryant.springtest.xmlspringtest.XmlBeanB@cb644e

annotation依赖配置信息完成注入demo
@Component
public class AnnotationBeanA {
	@Autowired
	private AnnotationBeanB annotationBeanB;

	public AnnotationBeanB getAnnotationBeanB() {
		return annotationBeanB;
	}

	public AnnotationBeanA() {
		System.out.println("new AnnotationBeanA()");
	}
}
@Component
public class AnnotationBeanB {
	public AnnotationBeanB() {
		System.out.println("new AnnotationBeanB()");
	}
}

// 配置文件
@ComponentScan("com.tbryant.springtest.annotationspringtest")
public class AppConfig {
}

// 启动类
public class AnnotationApplication {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();
		AnnotationBeanA beanA = (AnnotationBeanA) context.getBean("annotationBeanA");
		AnnotationBeanB beanB = (AnnotationBeanB) context.getBean("annotationBeanB");
		System.out.println(beanA);
		System.out.println(beanA.getAnnotationBeanB());
		System.out.println(beanB);
	}
}

运行结果
new AnnotationBeanA()
new AnnotationBeanB()
com.tbryant.springtest.annotationspringtest.AnnotationBeanA@543c6f6d
com.tbryant.springtest.annotationspringtest.AnnotationBeanB@13eb8acf
com.tbryant.springtest.annotationspringtest.AnnotationBeanB@13eb8acf

注意:在AnnotationBeanA类的属性annotationBeanB上添加@Autowired注解,通过这种方式,告诉spring:这个属性需要你帮我注入。如果不加,spring就不会为这属性完成注入。回头再看xml风格使用自动注入模型完成注入demo,属性上无需添加任何注解,只需指定使用哪个自动注入模型,spring就能完成属性注入。

annotation风格使用自动注入模型完成注入
@Component
public class AnnotationBeanA {
	// 无需@Autowired
	private AnnotationBeanB annotationBeanB;

	public AnnotationBeanB getAnnotationBeanB() {
		return annotationBeanB;
	}
	
	// 需要添加set方法
	public void setAnnotationBeanB(AnnotationBeanB annotationBeanB) {
		this.annotationBeanB = annotationBeanB;
	}

	public AnnotationBeanA() {
		System.out.println("new AnnotationBeanA()");
	}
}
@Component
public class AnnotationBeanB {
	public AnnotationBeanB() {
		System.out.println("new AnnotationBeanB()");
	}
}

// 配置文件
@ComponentScan("com.tbryant.springtest.annotationspringtest")
public class AppConfig {
}

// 与xml风格相比,annotation风格只能针对某个Bean进行设置
@Component
public class AnnotationBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		GenericBeanDefinition beanDefinitionA = (GenericBeanDefinition) beanFactory.getBeanDefinition("annotationBeanA");
		beanDefinitionA.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
	}
}

// 启动类
public class AnnotationApplication {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();
		AnnotationBeanA beanA = (AnnotationBeanA) context.getBean("annotationBeanA");
		AnnotationBeanB beanB = (AnnotationBeanB) context.getBean("annotationBeanB");
		System.out.println(beanA);
		System.out.println(beanA.getAnnotationBeanB());
		System.out.println(beanB);
	}
}

运行结果
new AnnotationBeanA()
new AnnotationBeanB()
com.tbryant.springtest.annotationspringtest.AnnotationBeanA@544a2ea6
com.tbryant.springtest.annotationspringtest.AnnotationBeanB@2e3fc542
com.tbryant.springtest.annotationspringtest.AnnotationBeanB@2e3fc542

个人理解

xml风格依赖配置信息完成注入是无自动化,相当于程序员明确指定哪个对象注入到哪个属性上,spring只是去完成注入这个动作。annotation依赖配置信息完成注入是半自动化,因为需要手动添加@Autowired,spring得知该属性需要注入后会读取属性的类型和名字,类似xml配置的标签;但因容器中可能存在多个该类型对象,spring会选择一个注入到属性上,所以还是有一些自动化的部分。使用自动注入模型是全自动化,指定使用哪种自动注入模型,spring就可以完成注入,无需对单个属性进行设置。
很多同学把@Autowired理解成自动注入,因为看字面意思就是自动注入,但spring的自动注入是使用自动注入模型实现的,@Autowired并没有使用四种自动注入模型的任意一种。

自动注入模型 vs @Autowired

自动注入模型在官方文档1.4.5. Autowiring Collaborators有介绍,一共有四种:no、byName、byType、constructor:

  • no(不自动注入)
  • byName(根据set方法名进行匹配,匹配不到就跳过,匹配到就注入)
  • byType(根据set方法参数类型进行匹配,匹配不到就跳过;匹配到多个就报错)
  • constructor(根据构造函数进行自动注入,这个自动注入模型涉及推断构造方法,默认选用参数最多的构造函数)

@Autowired逻辑如下:

  • 根据属性类型匹配,匹配不到就报异常。
  • 根据属性类型匹配,匹配到一个符合的对象,就注入。
  • 根据属性类型匹配,匹配到多个符合的对象,再根据属性名进行匹配,匹配到就注入,匹配不到就报异常。

很多文章说@Autowired是先byType再byName,这个说法不完全正确,经过实验总结出以下几个不同点:

  1. 匹配依据不同:byType根据set方法参数类型进行匹配;@Autowired根据属性类型进行匹配。byName根据set方法名进行匹配,@Autowired根据属性名进行匹配。
  2. 匹配不到的处理方式不同:自动注入模型无论是byType还是byName匹配不到都会跳过;@Autowired匹配不到会报异常。
  3. 匹配到多个的处理方式不同:自动注入模型byType匹配到多个会报异常;@Autowired匹配到多个会再根据属性名进行匹配。
  4. 注入使用的技术不同:自动注入模型使用set方法进行注入;@Autowired使用field.set进行注入。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值