# 基于注解的容器配置(常用注解)
# @Autowired
如何正确使用,请参考官方文档 (opens new window), 本篇主要讲它的实现原理。
@Autowired
的源码如下:
// org.springframework.beans.factory.annotation.Autowired.java
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
从源码我们知道,@Autowired
注解可以被标注在构造函数、属性、setter方法或配置方法上,用于实现依赖自动注入。
@Autowired
注解的作用是由AutowiredAnnotationBeanPostProcessor实现的,它实现了MergedBeanDefinitionPostProcessor
接口中的postProcessMergedBeanDefinition
方法,
@Autowired
注解正是通过这个方法实现注入类型的预解析,将需要依赖注入的属性信息封装到InjectionMetadata
类中,InjectionMetadata
类中包含了哪些需要注入的元素及元素要注入到哪个目标类中。
# AutowiredAnnotationBeanPostProcessor 源码解析
postProcessMergedBeanDefinition
方法的源码如下:
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
2
3
4
5
findAutowiringMetadata
方法源码如下:
// AutowiredAnnotationBeanPostProcessor.java
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
buildAutowiringMetadata
方法的源码如下:
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes 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;
}
AnnotationAttributes 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 new InjectionMetadata(clazz, elements);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
为什么 @Autowired 不支持静态变量、静态方法?
首先我们知道静态变量它是属于类的,而非属于实例对象的属性;同样的静态方法也是属于类的,普通方法(实例方法)才属于实例对象。
然而Spring
容器管理的都是实例对象,包括它的@Autowired
依赖注入的均是容器内的对象实例,所以对于static
成员是不能直接使用@Autowired
注入的。
上面的InjectedElement
有两个子类,分别是AutowiredFieldElement
和AutowiredMethodElement
。
AutowiredFieldElement
用于对标注在属性上的注入,AutowiredMethodElement
用于对标注在方法上的注入。
两种方式的注入过程都差不多,根据需要注入的元素的描述信息,按类型查找需要的依赖值,如果依赖没有实例化则先实例化依赖,然后使用反射进行赋值。
# @Resource
如何正确使用,请参考官方文档 (opens new window), 本篇主要讲它的实现原理。
Spring
不但支持自己定义的@Autowired
注解,还支持几个由JSR-250
规范定义的注解,它们分别是@Resource
@PostConstruct
@PreDestroy
。
@Resource
的作用相当于@Autowired
,只不过@Autowired
按类型自动注入,而@Resource
默认按byName
自动注入。
@Resource
有两个属性是比较重要的,分是name
和type
,Spring
将@Resource
注解的name
属性解析为bean
的名字,而type
属性则解析为bean
的类型。
所以如果使用name
属性,则使用byName
的自动注入策略,而使用type
属性时则使用byType
自动注入策略。如果既不指定name
也不指定type
属性,这时将通过反射机制使用byName
自动注入策略。
@Resource
装配顺序:
- 如果同时指定了
name
和type
,则从Spring
上下文中找到唯一匹配的bean
进行装配,找不到则抛出异常; - 如果指定了
name
,则从上下文中查找名称(id)匹配的bean
进行装配,找不到则抛出异常; - 如果指定了
type
,则从上下文中找到类型匹配的唯一bean
进行装配,找不到或者找到多个,都会抛出异常; - 如果既没有指定
name
,又没有指定type
,则自动按照byName
方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@Resource
的源码如下:
// javax.annotation.Resource.java
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
String lookup() default "";
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# @Value
@Value
通常用于注入外部属性。
如何正确使用,请参考官方文档 (opens new window)。
@Value
的源码如下:
// org.springframework.beans.factory.annotation.Value
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
/**
* The actual value expression: for example {@code #{systemProperties.myProp}}.
*/
String value();
}
2
3
4
5
6
7
8
9
10
11
12
13
# CrossOrigin
//实现跨域注解
//origin="*"代表所有域名都可访问
//maxAge飞行前响应的缓存持续时间的最大年龄,简单来说就是Cookie的有效期 单位为秒
//若maxAge是负数,则代表为临时Cookie,不会被持久化,Cookie信息保存在浏览器内存中,浏览器关闭Cookie就消失
@CrossOrigin(origins = "*",maxAge = 3600)
2
3
4
5
# 为什么Spring不推荐使用 @Autowired 注解
- 不建议直接在字段上进行依赖注入,Spring 开发团队建议:在Java Bean 中永远使用构造函数进行依赖注入;
- 编译过程不会报错,但是运行之后可能报NPE异常;