1 概述
- Spring属性注入使用注解的目的是简化属性注入的配置,提高开发效率和代码可读性。使用注解可以将属性注入的信息直接写在类的成员变量、setter方法或构造函数上,从而避免了繁琐的XML配置,并且可以使代码更加简洁、清晰。同时,注解可以帮助开发人员更好地理解对象之间的依赖关系,增强代码的可读性。
在使用注解进行属性注入时,通常使用的注解包括:
- @Autowired:自动注入Bean对象,可以根据类型或名称进行注入。
- @Value:注入配置文件中的属性值。
- @Resource:根据名称注入Bean对象。
- @Inject:与@Autowired类似,可以自动注入Bean对象。
- @Qualifier:与@Autowired配合使用,指定Bean对象的名称。
通过使用这些注解,开发人员可以更加便捷地进行属性注入,避免了冗长的XML配置,提高了开发效率和代码质量。同时,注解的使用也使得代码更加易于理解和维护。
2 @Value
@Value是Spring框架中的一个注解,用于将属性值注入到组件中。
通常情况下,我们需要将一些配置信息注入到应用程序中,这时可以使用@Value注解来完成这个任务。@Value注解可以将属性值注入到组件的构造方法、成员变量、Setter方法中,例如:
lessCopy code
@Component
public class MyComponent {
@Value("${app.name}")
private String appName;
private int appVersion;
@Autowired
public MyComponent(@Value("${app.version}") int appVersion) {
this.appVersion = appVersion;
}
public void setAppName(@Value("${app.name}") String appName) {
this.appName = appName;
}
// ...
}
在上面的代码中,我们使用@Value注解将应用程序的名称和版本号注入到MyComponent组件中。其中,{app.version}是配置文件中的属性占位符,Spring会自动将这些占位符替换为对应的属性值。
需要注意的是,@Value注解可以用于注入任意类型的属性值,包括字符串、数值、布尔值、数组、集合等。如果需要将属性值注入到集合中,可以使用SpEL表达式来表示集合的值,例如:
typescriptCopy code
@Value("#{${app.properties}}")
private Map<String, String> appProperties;
在上面的代码中,我们使用SpEL表达式将应用程序的属性列表注入到一个Map集合中。
3 @Autowired和@Qualifier
@Autowired是Spring框架中的一个注解,用于自动装配组件之间的依赖关系。默认根据类型自动注入,如果需要按照名称注入,需要@Qualifier注解配合使用。
在一个Spring应用程序中,通常会有很多组件之间存在依赖关系,这时就需要使用@Autowired注解来自动装配这些组件。@Autowired注解可以用于构造方法、成员变量、Setter方法上,例如:
typescriptCopy code
@Component
public class MyComponent {
private MyDependency myDependency;
@Autowired
public MyComponent(MyDependency myDependency) {
this.myDependency = myDependency;
}
@Autowired
public void setMyDependency(MyDependency myDependency) {
this.myDependency = myDependency;
}
@Autowired
public void init(MyDependency myDependency) {
this.myDependency = myDependency;
}
// ...
}
在上面的代码中,我们使用@Autowired注解将MyDependency组件注入到MyComponent组件中。@Autowired注解会自动扫描应用程序上下文中所有的组件,并根据类型和名称来自动装配依赖关系。如果存在多个同类型的组件,可以使用@Qualifier注解指定要装配的组件名称,例如:
kotlinCopy code
@Component
public class MyComponent {
private MyDependency myDependency;
@Autowired
public MyComponent(@Qualifier("myDependency2") MyDependency myDependency) {
this.myDependency = myDependency;
}
// ...
}
在上面的代码中,我们使用@Qualifier注解指定要装配的组件名称为myDependency2,这样就可以避免由于同类型组件的存在而导致的装配错误。
需要注意的是,@Autowired注解可以用于构造方法、Setter方法、成员变量上,但通常情况下,最好将@Autowired注解放在构造方法上,这样可以保证组件依赖关系的完整性,也可以避免在Setter方法或成员变量上出现多次装配的情况。
4 @Resource
@Resource注解是JavaEE中的注解,Spring框架也支持这个注解用于Bean的属性注入。
@Resource注解可以用来注入一个Bean或者其他的资源到一个Bean的属性中,具体使用方式如下:
- 通过名称注入
可以使用@Resource注解并指定名称来注入一个Bean,例如:
kotlinCopy code
@Component
public class MyComponent {
@Resource(name = "myDependency")
private MyDependency myDependency;
// ...
}
在上面的代码中,我们使用@Resource注解将名称为"myDependency"的Bean注入到myDependency属性中。
- 通过类型注入
如果只有一个Bean类型与被注入属性类型匹配,可以省略@Resource注解中的name属性,例如:
kotlinCopy code
@Component
public class MyComponent {
@Resource
private MyDependency myDependency;
// ...
}
在上面的代码中,Spring会自动查找与myDependency属性类型匹配的Bean进行注入。
- 通过限定符注入
如果存在多个同类型的Bean,可以使用@Resource注解的name属性和Qualifier注解的value属性来指定要注入的Bean,例如:
lessCopy code
@Component
public class MyComponent {
@Resource(name = "myDependency2")
@Qualifier("myQualifier")
private MyDependency myDependency;
// ...
}
在上面的代码中,我们使用@Resource注解指定要注入的Bean的名称为"myDependency2",并使用@Qualifier注解指定要注入的Bean的限定符为"myQualifier"。
总之,@Resource注解可以用于Bean的属性注入,可以根据具体的场景和需求来选择合适的注入方式。需要注意的是,@Resource注解需要配合JavaEE容器一起使用,不建议在纯Spring应用中使用。
5 自动注入执行
AutowiredAnnotationBeanPostProcessor是一个BeanPostProcessor,用于在Bean实例化之后、初始化之前,自动注入Bean的依赖关系。它会扫描容器中所有的Bean,检查其中是否存在@Autowired、@Resource或@Inject注解,并自动注入它们所依赖的其他Bean。
具体来说,AutowiredAnnotationBeanPostProcessor类会在Bean实例化后,执行postProcessProperties()方法,用于自动注入Bean的依赖关系。在该方法中,会遍历Bean的所有属性,检查其中是否存在@Autowired、@Resource或@Inject注解,并将其依赖的Bean注入到当前的Bean中。
当@Autowired注解出现在成员变量上时,AutowiredAnnotationBeanPostProcessor类会调用resolveAutowiredFieldValue()方法,使用类型匹配的方式来注入Bean的依赖关系。当@Autowired注解出现在setter方法上时,AutowiredAnnotationBeanPostProcessor类会调用resolveAutowiredMethod()方法,使用方法匹配的方式来注入Bean的依赖关系。
除了自动注入Bean的依赖关系,AutowiredAnnotationBeanPostProcessor类还提供了一些配置选项,如required属性、autowiredAnnotationType属性等,用于控制自动注入的行为。
总之,AutowiredAnnotationBeanPostProcessor类是Spring中常用的依赖注入的实现方式之一,它能够自动注入Bean的依赖关系,并提高代码的灵活性和可维护性。
我来看下AutowiredAnnotationBeanPostProcessor#postProcessProperties()方法,继承自InstantiationAwareBeanPostProcessor,之前我们在 ,==0105bean实例化-Bean生命周期详解-spring== 有介绍。源代码如下:
@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;
}
- findAutowiringMetadata():查找自动注入元数据
- metadata.inject():方法底层通过反射实现,这个暂时不详述,感兴趣自行查阅源码或者相关文档。
findAutowiringMetadata()代码如下:
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
- 先从缓存获取注入元数据,默认缓存没有,执行buildAutowiringMetadata()构建注入元数据。
buildAutowiringMetadata()方法源代码如下:
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
这代码有点“眼熟”,是地在我们前面==0108Bean销毁-Bean生命周期详解-spring==在处理@Predestroy和PostConstruct注解的时候,使用相同的处理逻辑:
- 分别遍历Class的字段和方法,检查是否有相关注解,加入集合构建元数据,放入缓存。
我们看下这里处理的注解类型,在构造方法中代码如下:
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// jakarta.inject API not available - simply skip.
}
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// javax.inject API not available - simply skip.
}
}
- 我们常用的有@Value和@Autowired