Spring依赖注入之@Resource注解
@Resource注解
类似spring中的@Autowired注解,这里也会有一个后置处理器。
对于CommonAnnotationBeanPostProcessor来说,首先在类加载的时候
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);
static {
webServiceRefClass = loadAnnotationType("javax.xml.ws.WebServiceRef");
ejbClass = loadAnnotationType("javax.ejb.EJB");
resourceAnnotationTypes.add(Resource.class);
if (webServiceRefClass != null) {
resourceAnnotationTypes.add(webServiceRefClass);
}
if (ejbClass != null) {
resourceAnnotationTypes.add(ejbClass);
}
}
就已经开始往里面来添加一个注解@Resource。
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;
}
寻找注入点
看看spring是如何来处理@Resource的。
metadata = buildResourceMetadata(clazz);
@Autowired和@Resource区别
和@Autowired逻辑类似,但是有一个区别:
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
如果类中的属性是static关键字并且添加了@Resource注解,那么这里直接报错。
而对于@Autowired来说,是直接返回null的。
ResourceElement构造函数
最终发现,找到了在方法上和属性上添加了@Resource注解了之后,形成了一个对象ResourceElement。
看看构造方法:
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
Resource resource = ae.getAnnotation(Resource.class);
// @Resource中的name和type属性
String resourceName = resource.name();
Class<?> resourceType = resource.type();
// 是否是使用默认的名字
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName = this.member.getName();
// set方法来做处理
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());
}
构造中主要是用@Resource中的name和type属性来做一个判断,以及是否有@Lazy注解去生成代理对象等等。
注入
那么直接看下怎么进行注入的即可。
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
那么直接正常不会加@Lazy,那么直接看下getResource方法:
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
...........
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
再来到:
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;
}
那么直接看核心方法:
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);
}
这里就是@Resource注解的核心:先根据名字,然后根据类型。
那么原理是什么呢?
重点是:fallbackToDefaultTypeMatch=TRUE,表示的是如果根据名字从factory中查找得到bean没有找到的时候,需要类型来查找。
因为在判断factory.containsBean(name),是否有这个名字的bean的时候,如果没有找到,下面会根据类型查找。
如果包含了,那么直接根据名字找到对应的bean来进行赋值。通俗易懂
从理论中来,到实践中去,最终回归理论