@Resource注解,默认基于ByName注入模型实现的注入,来看看具体实现,入口在CommonAnnotationBeanPostProcessor
的postProcessProperties
先看看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;
}
- 找到注入点
findResourceMetadata
- 完成注入
inject
查看寻找注入点的接口InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
private InjectionMetadata findResourceMetadata(String beanName, final 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);
//判断是否为空或者为Class对象
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 = buildResourceMetadata(clazz);
//存入缓存
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
//-----------------------------------------------------------------
public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
return (metadata == null || metadata.targetClass != clazz);
}
查看真正的查询接口metadata = buildResourceMetadata(clazz);
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
//属性
//ResourceElement 注入点
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));
}
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));
}
}
});
//方法
//ResourceElement 注入点
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));
}
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));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
查询也是类似于@AutoWried满分为两种一种基于属性doWithLocalFields,一种基于方法doWithLocalMethods,都会进行各种注解的判断,同时基于方法时还会判断参数的个数是否大于1,我们只需要关注一点在@Resource条件下,构建注入点,也就是new ResourceElement(method, bridgedMethod, pd)
进入ResourceElement看看具体实现
private class ResourceElement extends LookupElement {
private final boolean lazyLookup;
//查看注入方法,inject,没有就查看父类
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();
//如果@Resource注解中没有指定name,那么久使用默认生成的名字
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());
}
接下来看看注入的逻辑inject
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
// 如果是属性,则反射赋值
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
//获取资源注入
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
// 检查当前的属性是不是通过by_name和by_type来注入的
if (checkPropertySkipping(pvs)) {
return;
}
try {
// 如果是方法,则通过方法赋值
// 这里的方法并不是一定要setXX方法
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
- 如果是属性,则反射赋值
- 如果是方法,则通过方法赋值,这里的方法并不是一定要setXX方法
主要关注点落在了getResourceToInject(target, requestingBeanName)
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
然后查看getResource(this, requestingBeanName)
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");
}
//最后还是走到了autowireResource
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
一个特定的bd : private transient BeanFactory jndiFactory = new SimpleJndiBeanFactory();先通过getBean获取Bean,调用autowireResource完成注入
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
try {
if (isSingleton(name)) {
//单例获取,默认
return doGetSingleton(name, requiredType);
}
else {
return lookup(name, requiredType);
}
}
catch (NameNotFoundException ex) {
throw new NoSuchBeanDefinitionException(name, "not found in JNDI environment");
}
catch (TypeMismatchNamingException ex) {
throw new BeanNotOfRequiredTypeException(name, ex.getRequiredType(), ex.getActualType());
}
catch (NamingException ex) {
throw new BeanDefinitionStoreException("JNDI environment", name, "JNDI lookup failed", ex);
}
}
总结
对于@Resource:
- 如果@Resource注解中指定了name属性,那么则只会根据name属性的值去找bean,如果找不到则报错
- 如果@Resource注解没有指定name属性,那么会先判断当前注入点名字(属性名字或方法参数名字)是不是存在Bean,如果存在,则直接根据注入点名字取获取bean,如果不存在,则会走@Autowired注解的逻辑,会根据注入点类型去找Bean