我的Java Web之路 - Spring IoC(2)- 使用注解装配Bean

介绍

前面的文章介绍了Spring IoC容器使用XML形式的配置元数据来生产和装配Bean,本篇文章讲述如何使用Java注解来生产和装配Bean。

讲解仍然以上篇文章的工程为基础,先将Bean的装配转移到使用Java注解,这意味着Bean的定义仍然使用XML,只是将依赖注入转移到使用Java注解。

最后将Bean的定义也转移到使用Java注解。

移除XML中的依赖注入

spring-ioc-test.xml:

<?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 
		https://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="svcA" class="test.service.ServiceA">
<!-- 		<constructor-arg name="serviceB" ref="svcB" />
		<constructor-arg name="repositoryA" ref="repoA" /> -->
	</bean>
	
	<bean id="svcB" class="test.service.ServiceB">
<!-- 		<property name="repositoyB" ref="repoB" /> -->
	</bean>
	
	<bean id="repoA" class="test.repository.RepositoryA" />
	<bean id="repoB" class="test.repository.RepositoryB" />

</beans>

这里,与之前唯一的不同就是把<constructor-arg/><property/>这两个标记的内容使用XML的注释标记<!-- -->注释掉了,这样就相当于把svcAsvcB这两个Bean的依赖注入去掉了。

我们也可以看到,XML标记在没有内容时可以有两种形式,比如标记:

  • <bean id=...> </bean>
  • <bean id=... />

XML中启用基于注解的装配:annotation-config标记

既然要使用基于注解来装配Bean,那么就需要告诉Spring IoC容器我们要启用基于注解的装配,在哪里告诉呢?还是在XML中:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		https://www.springframework.org/schema/beans/spring-beans.xsd 
		http://www.springframework.org/schema/context 
		https://www.springframework.org/schema/context/spring-context.xsd">
	
	<context:annotation-config />
	
	<!-- 下面的内容与上面的一样,省略 -->

</beans>

使用<annotation-config />标记即可告诉Spring IoC容器我们要启用基于注解的装配,标记名称也是很容易记忆。

不过这个标记是在Spring的context命名空间,所以需要在根标记<beans/>中添加上该命名空间及其对应的模式定义(XSD文件,XML Schema Definition),关于XML的命名空间和XSD暂不细说,可以简单理解为就是定义标记的集合且区分标记以避免标记名称重复。
Spring的context命名空间是:

xmlns:context="http://www.springframework.org/schema/context"

对应的模式定义以key-value的形式添加到xsi:schemaLocation的值中,key是命名空间,value是XSD文件,中间以空格分开:

http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd

后面引用该命名空间的标记时,加上命名空间前缀即可,以冒号分隔,如:

<context:annotation-config />

实际上,这个标记的原理是用了Spring框架的BeanPostProcessor功能扩展,这个标记就是向Spring IoC容器注册了AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorPersistenceAnnotationBeanPostProcessor等Bean后处理器,它们就是用来解析@Autowired等注解并处理装配流程的。

因此,我们其实可以不用这个标记,而是在XML中自己定义这些后处理器的Bean,Spring IoC容器也会识别它们,并用它们来处理装配流程。当然,通常情况下,我们显然没有必要这样做去增加自己的工作。

基于注解的自动装配:@Autowired注解

现在,Spring IoC容器(实例化的时候)已经可以通过XML得知启用基于注解的Bean装配(依赖注入)了,那究竟用哪个注解呢?答案就是@Autowired注解。

因为在我们的例子中只有ServiceA和ServiceB这两个组件才需要装配,下面就只列出这两个组件的代码。

ServiceA.java

package test.service;

import org.springframework.beans.factory.annotation.Autowired;

import test.repository.RepositoryA;

public class ServiceA {

	private ServiceB serviceB;
	private RepositoryA repositoryA;
	
	@Autowired
	public ServiceA (ServiceB serviceB, RepositoryA repositoryA) {
		this.serviceB = serviceB;
		this.repositoryA = repositoryA;
	}
	
	public void doWork() {
		System.out.println("ServiceA doWork start");
		serviceB.doWork();
		repositoryA.save();
		System.out.println("ServiceA doWork end");
	}
}

可以看到,与之前的代码唯一不同的就是在构造方法的前面添加了@Autowired注解。

ServiceB.java

package test.service;

import org.springframework.beans.factory.annotation.Autowired;
import test.repository.RepositoryB;

public class ServiceB {

	@Autowired
	private RepositoryB repositoyB;
	
	public void doWork() {
		System.out.println("ServiceB doWork start");
		repositoyB.update();
		System.out.println("ServiceB doWork end");
	}
}

这里,与之前的代码有比较大的不同,但是更简洁了,原因就是:

  • 去掉了属性repositoyB的getter和setter方法;
  • 属性repositoyB的前面添加了@Autowired注解。

验证

现在,大家可以运行一下工程进行验证一下是否正确。

可以看到,使用基于注解来进行自动装配(依赖注入)比基于XML的要简洁很多,但只能使用在自己开发的组件当中,因为你没法为第三方组件添加注解,这时候只能使用XML来装配了。

基本等价的注解:@Resource注解

@Resource注解与@Autowired注解基本等价,它是JSR-250规范中定义的,全限定名是javax.annotation.Resource

它也可以用在类的属性和方法上,不过它是默认是按照Bean名称来装配的,@Autowired是按照类型来装配的。

这里不再详细讨论。

基本等价的注解:@Inject注解

@Inject注解与@Autowired注解基本等价,它是JSR-330规范中定义的,全限定名是javax.inject.Inject

它也可以用在类的属性和方法上,跟@Autowired一样是按照类型来装配的。

如果需要按照Bean名称来装配,@Autowired需要跟@Qualifier一起使用,@Inject需要跟@Named一起使用。

这里不再详细讨论。

再谈注解的本质

从这个例子我们也可以再次看到注解的本质实际上就是标记、元数据,它的三要素是:

  • 定义:@Autowired注解显然是由Spring框架为我们定义的,名称起的也是相当有含义的,就是自动装配的意思,表示使用的地方要执行自动装配。
  • 解析处理:@Autowired的解析和处理是由Spring框架中的AutowiredAnnotationBeanPostProcessor这个Bean后处理器来执行的;
  • 使用:就是我们来使用了。

总结

  • @Autowired注解可以修饰类的属性、构造方法、setter方法和其他方法;
  • @Autowired注解默认按照Bean的类型来自动装配;
  • @Autowired注解基本等价的有JSR规范定义的@Resource注解和@Inject注解。

Bean的实例化和自动装配(依赖注入)还有很多细节需要讨论,这里涉及到依赖关系的解析过程,以后慢慢细说,比如:

  • 一些原子类型、字符串等字面值常量的注入;
  • 一些集合类型的注入;
  • null和空字符串的注入;
  • XML中使用p命名空间和c命名空间来注入;
  • 使用外部配置文件(比如properties文件和yml文件)中的值来注入;
  • 内部Bean的注入;
  • 复合属性的注入;
  • 指定Bean不尽心自动装配;
  • 自动装配的匹配,可以通过类型、名称、参数位置索引等;
  • 泛型的自动装配;
  • 没有符合条件的Bean怎么处理注入的;
  • 有多个符合条件的Bean时怎么选择一个注入(使用primary和qualify);
  • 依赖的解析过程,循环依赖的问题;
  • Bean实例化的时机(强制在另一个Bean之后实例化、延迟实例化);
  • XML和自动装配重复的问题;
  • 高级一点的:工厂方法生成Bean,内部类的Bean,Lookup方法注入,方法替代,Bean定义的继承等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值