@Resource注解标注的属性默认按照ByName进行注入,由J2EE提供
如果我们想按照ByType注入,代码要这样写:
public class LaController {
//按类型注入
@Resource(type=LaService.class)
private LaService laService;
}
复制代码
如果LaService接口存在两个实现类,且两个实现类都会被spring扫描到,在注入的时候就会报错:nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.netty.use.nettyuse.service.LaService' available: expected single matching bean but found 2: la2ServiceImpl,laServiceImpl
type也可以是实现类class,例如:
public class LaController {
//按类型注入
@Resource(type=La2ServiceImpl.class)
private LaService laService;
}
复制代码
这样就会注入La2ServiceImpl实例,就不会出现上面的报错了。
代码定位到
AbstractAutowireCapableBeanFactory类的populateBean方法
//是否有实例化相关的BeanPostProcessor
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
if (hasInstAwareBpps || needsDepCheck) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//调用处理属性值的方法
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
复制代码
InstantiationAwareBeanPostProcessor有一个实现类叫做CommonAnnotationBeanPostProcessor,这个类会对@Resource标注的属性进行赋值。
1.找到所有的@Resource标注的属性
利用java反射,找到类中所有的Fields,然后判断属性上是否标记了@Resource注解
if(field.isAnnotationPresent(Resource.class))
复制代码
如果条件成立,就会构建一个ResourceElement出来。我们来看下ResourceElement类
private class ResourceElement extends LookupElement {
private final boolean lazyLookup;
//构造函数
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
Resource resource = ae.getAnnotation(Resource.class);
//属性名字
String resourceName = resource.name();
//属性的类型
Class> resourceType = resource.type();
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName = this.member.getName();
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
resourceName = Introspector.decapitalize(resourceName.substring(3));
}
}
else if (embeddedValueResolver != null) {
resourceName = embeddedValueResolver.resolveStringValue(resourceName);
}
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.name = (resourceName != null ? resourceName : "");
this.lookupType = resourceType;
String lookupValue = resource.lookup();
this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
Lazy lazy = ae.getAnnotation(Lazy.class);
this.lazyLookup = (lazy != null && lazy.value());
}
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
}
复制代码
最后会将这个类中所有标注有@Resource注解的属性构造出来的ResourceElement都放到LinkedList中。最终会包装出一个注入元数据(InjectionMetadata)对象出来。
2.@Resource属性值注入
既然我们拿到了所有@Resource属性,那么循环对这些属性注入就可以了。
属性注入核心原理=反射+getBean方法。
getBean方法获取Bean对象,然后调用Field的set方法,对属性进行赋值。我们看下代码片段:
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}