前面文章@Autowired与@Resource区别主要对这两个注解的差异及使用方式进行了一个总结,本文将从源码分析被@Autowired、@Value和@Resource这三个注解修饰的属性和方法是如何被spring扫描到的。
其实负责扫描这三个注解的类是spring中的一种后置处理器BeanPostProcessor。其中,AutowiredAnnotationBeanPostProcessor负责扫描@Autowired和@Value这两个注解;CommonAnnotationBeanPostProcessor负责扫描@Resource这个注解。
平时开发中,我们通常将@Autowired、@Value和@Resource这三个注解添加到属性上,实际上这三个注解也可以应用在方法上,spring同样可以将它们扫描到。
下面将对核心源码进行简单分析:
@Autowired和@Value
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#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 -> {
// 判断属性上是否有注解@Autowired或@Value
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
// 属性是否是静态属性,如果是静态属性,则不会注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 推断该属性是否是必须的,@Value的required永远为true
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;
}
// 查找方法上是否有@Autowired或@Value注解
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 如果方法是静态方法时,无法注入
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// @Autowired和@Value应该应用于有参数的方法上
if (method.getParameterCount() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("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);
// 获取当前bean的父类,如果父类不是Object,继续循环,判断父类中是否添加了@Autowired、@Value属性
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
上面核心代码的主要逻辑如下:
(1)@Autowired和@Value这两个注解加在属性上:加在属性字段上,该字段不能被static修饰,否则无法注入,显示默认值null;
(2)@Autowired和@Value这两个注解加在方法上:加在方法上,方法应该包含参数,并且该方法不能被static修饰,否则无法注入,显示默认值null。
(3)如果该类拥有父类,递归查找父类添加了这两个注解的属性和方法,执行同样的扫描逻辑。
@Autowired和@Value代码测试:
@Service
public class InstanceA implements InstanceAPI{
// 正常注入
@Autowired
private InstanceB instanceB;
// 不报错:为null
@Autowired
private static InstanceC instanceC;
private InstanceD instanceD;
// 正常注入
@Autowired
public void instanceD(InstanceD instanceD){
this.instanceD = instanceD;
}
private InstanceE instanceE;
// 不报错:instanceE为null
@Autowired
public static void instanceE(InstanceE instanceE){
instanceE = instanceE;
}
// 正常注入
@Value("1")
private Integer numValueA;
// 不报错:为null
@Value("2")
private static Integer numValueB;
// 正常注入
private Integer numValueC;
@Value("3")
public void setNumC(Integer numC){
this.numValueC = numC;
}
// 不报错:为null
private static Integer numValueD;
@Value("4")
public static void setNumD(Integer numD){
numValueD = numD;
}
}
@Autowired和@Value代码测试结果
@Resource
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata()
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
// 判断属性上是否有注解@Resource
else if (field.isAnnotationPresent(Resource.class)) {
// 判断属性是否是静态
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
// 判断属性是否被忽略注入
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
// 判断方法上是否有注解@Resource
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
// 被@Resouce修饰的方法不能是静态的
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
// 被@Resouce修饰的方法只能有1个参数
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
// 判断方法传递进来的bean是否被忽略注入
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
上面核心代码的主要逻辑如下:
(1)@Resource注解加在属性上:注解被加在属性字段上时,该属性字段不能被static修饰,否则直接报错;
(2)@Resource注解加在方法上:注解被加在方法上时,该方法必须只有1个参数,并且该方法不能被static修饰,否则直接报错。
(3)如果该类拥有父类,递归查找父类添加了这个注解的属性和方法,执行同样的扫描逻辑。
@Resource代码测试:
@Service
public class InstanceA implements InstanceAPI{
// 正常注入
@Resource
private InstanceB instanceB;
// 报错:@Resource不能修饰静态属性
@Resource
private static InstanceC instanceC;
private InstanceD instanceD;
// 正常注入
@Resource
public void instanceD(InstanceD instanceD){
this.instanceD = instanceD;
}
private InstanceE instanceE;
// 报错:@Resource不能修饰静态方法
@Resource
public static void instanceE(InstanceE instanceE){
instanceE = instanceE;
}
}