概述
@Resource这个注解在实现上很类似,但是于@Autowired在一般情况下是先byType,再byName,但是@Resource则相反,通知@Resource还可以单独制定注入的bean的名字,具体实现也是通过一个后置处理器进行实现的,这个后置处理是CommonAnnotationBeanPostProcessor
@Resource的自动注入
对于@Resource的自动注入而言,其注入的方法也是postProcessProperties这个方法
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
这个方法可以说是和@Autowired对应后置处理器的postProcessProperties一模一样了
首先是先找到所有的注入点InjectionMetadata,的inject方法
首先来看看@Resource是如何找注入点的
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
...
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));
}
}
...
其实就是简单的判断了一下这个属性上面有没有加了@Resource,如果加了这个注解,那么对于spring而言,这就是一个注入点,
同时构造一个注入点,这个注入点的实现类是ResourceElement,在这个注入点的构造里面,如果配置了name属性就会获取name的值,如果没有那么就会默认使用变量名作为name,对于方法而言,就是set方法之后的字符串
对于方法的注入点查找也是类似
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
这里于@Autowired还有一点不同的是,@Resource限制了方法的参数只能有一个,不然会抛出异常
ResourceElement里面重写了自己去实现注入的方法
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
这里lazylookup是配置的@Lazy注解的属性,从这里也可以看书@Reource是支持@Lazy的
所以默认这里是false
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
if (StringUtils.hasLength(element.mappedName)) {
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
if (this.alwaysUseJndiLookup) {
return this.jndiFactory.getBean(element.name, element.lookupType);
}
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the 'resourceFactory' property");
}
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
jndiFactory应该是跟jndi相关的工厂,这里不做探讨,从源码里面是可以看出,@Resource是支持JNDI工厂的,如果@Resource配置得了mappedName这个属性,那么就会去 jndiFactory 相关的工厂里面去找,如果说没有这个工厂,才会再spring 的工厂里面去找
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set<String> autowiredBeanNames;
String name = element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
else {
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}
同@Autowired一样,先取出属性描述对象,然后通过属性描述对象找到要注入的点,调用resolveDependency,但是这个方法是@Autowired的核心方法,我们都知道@Autowired是先byType,再byName,但是@Resource是先byName再ByType,但是在这里调用
resolveDependency方法,那么@Resource不就和@Autowired一样了
其实是的,在调用这个方法之前有一个判断 factory.containsBean(name) ,这个判断是当前spring容器里面有没有这个name的bean,这里就是byName的判断,如果说有,那么就直接调用resource = beanFactory.resolveBeanByName(name, descriptor);在这里面就是直接调用spring 的getBean方法,也就是说直接通过名字从spring里面拿到Bean直接返回,这就是byName的逻辑。
但是如果factory.containsBean(name)返回的false,也就是当前spring工厂里面没有这个名字的bean,就会通过resolveDependency方法找注入的对象,所以resolveDependency里面进行的是byType,再byName,但是name 再调用这个方法之前就已经判断是没有的,所以相当于这个ByName是没有效果的,所以给人了一种先ByName再ByType的假象
这里找到注入的bean后,会缓存起来,等待spring后续的生命周期继续注入