不会吧?还有人不知道spring可以依赖注入父类?

191 阅读2分钟

talk is cheap,show me the code!!!

实战

Person.java

@Component("person")
public class Person {
    public void say(){
        System.out.println("hello world");
    }
}

AbstractService.java

public abstract class AbstractService {
    @Resource
    protected Person person;
    public abstract void service();
}

ConcreteService.java

@Service
public class ConcreteService extends AbstractService{
    @Override
    public void service() {
        person.say();
    }
}

ProxyConfig

@Configuration
@ComponentScan("org.example.bean")
public class ProxyConfig {
}

App.java

public class App
{
    public static void main( String[] args ) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyConfig.class);
        AbstractService service = context.getBean(AbstractService.class);
        service.service();
        context.close();
    }
}

result: image.png

原理

在spring执行依赖注入的时候会执行 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean(该流程是在doCreateBean方法里)

image.png org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties 在postProcessProperties通过findResourceMetadata收集需要注入的字段、方法 然后通过inject进行注入

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;
}

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#findResourceMetadata

findResourceMetadata先从缓存获取。如果没有就执行buildResourceMetadata构建需要注入的字段、方法

private InjectionMetadata findResourceMetadata(String beanName, 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);
    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;
}

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata

buildResourceMetadata会解析当前类的字段、方法看看有没有符合要求的。

然后会解析父类的。最后几行!!!!

private InjectionMetadata buildResourceMetadata(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 < > ();
        ReflectionUtils.doWithLocalFields(targetClass, field - >
        {
            if(ejbClass != null && field.isAnnotationPresent(ejbClass))
            {
                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));
                }
            }
        });
        ReflectionUtils.doWithLocalMethods(targetClass, method - >
        {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod))
            {
                return;
            }
            if(method.equals(ClassUtils.getMostSpecificMethod(method, clazz)))
            {
                if(ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass))
                {
                    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);
}

Resource、Autowired都会解析父类