spring依赖注入——注解注入
之前分析spring的依赖注入时,主要分析的是xml配置方式。但是在实际项目中,我们其实用的更多的是注解方式。这一篇博客会分析下spring是如何处理这种注解注入的。(主要分析最常使用的@Autowired 和 @Resource注解)
注解注入的开启 annotation-config
SpringBoot方式暂且不管,正常来说我们要想启用注解注入都需要有这样一个配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
</beans>
那么我们就从annotation-config的解析开始
从代码很容易看出,annotation-config的解析是在org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser类里。
PostProcessor的注册
对于annotation-config的处理,依然只关心最核心的部分。跟着源码走,发现一部分非常核心的逻辑是在AnnotationConfigUtils里。在处理annotation-config时候,spring配置了各种PostProcessor类
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
....
populateBean
我们在分析populateBean时候,只是曾跳过一段关于关于PostProcessor的回调方法。而Spring正是通过PostProcessor来实现注解方式的依赖注入的。
同样,这里把PostProcessor这一段单独贴出来
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
PropertyValues pvs = mbd.getPropertyValues();
if (bw == null) {
if (!pvs.isEmpty()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
//... 省略AutowiredMode等
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
//就是在这里处理注解形式的依赖注入的
if (hasInstAwareBpps || needsDepCheck) {
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);
}
}
applyPropertyValues(beanName, mbd, bw, pvs);
}
@Autowired处理 AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues
从annotatation-config的处理我们知道了,spring其实配置了很多的PostProcessor。这里我只分析我们最常用的一个AutowiredAnnotationBeanPostProcessor
这个类对@Autowired注解进行了处理
AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues
随后我们就来看看,究竟这个AutowiredAnnotationBeanPostProcessor是如何对这些依赖注入使用的注解进行处理的
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
这里的代码很简单,找到InjectionMetadata
调用InjectionMetadata进行注入
那么我们就要先弄清楚,这个InjectionMetadata究竟是什么?
Internal class for managing injection metadata.
Not intended for direct use in applications.
注释里说是一个用于管理注入的元数据
的管理类,到这里我们还是不清楚它是干啥用的。
所以继续深入分析InjectionMetadata是如何构建的。findAutowiringMetadata这个方法大概就是,如果有的话,就从缓存获取,如果没有的话,就build一个。所以我们直接看build的逻辑就好了
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
Class<?> targetClass = clazz;
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements =
new LinkedList<>();
//找到所有的标注了 @Autowired @Value 等注解的field,封装成一个AutowiredFieldElement 添加到currElements里
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
}
});
//找到所有的标注了 @Autowired @Value 等注解的method,封装成一个AutowiredFieldElement 添加到currElements里
ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with parameters: " + method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
//封装找到了的method和field,放到InjectionMetadata
return new InjectionMetadata(clazz, elements);
}
这里的逻辑也很简单
1)找到所要注入的类,标记有 @Autowired @Value等注解的 field和method 添加到一个list中
2)以list和这个class封装成一个InjectionMetadata
3)注意这里的while (targetClass != null && targetClass != Object.class);
除了扫描自己类的@Autowired等注解,还会扫描父类的注解,这也解释了,为什么继承了抽象类的时候,可以同时继承其 @Autowired等注解
下面的例子简单说明了父类注入的场景
抽象类的注入
@Component
public class BaseDao {
//各种数据库操作方式...
}
//抽象类,只是注入了dao
public abstract class BaseService {
@Autowired
protected BaseDao dao;
public BaseDao getDao() {
return dao;
}
public void setDao(BaseDao dao) {
this.dao = dao;
}
}
StudentService 继承了BaseService,这样就可以直接在自己的方法里使用dao
public class StudentService extends BaseService {
}
xml配置,xml只需要配置Service和dao,就会自动将dao注入到StudentService中
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--<context:component-scan base-package="com.hdj.learn.spring.annotation"/>-->
<bean id="dao" class="com.hdj.learn.spring.annotation.abs.BaseDao">
</bean>
<bean id="studentService" class="com.hdj.learn.spring.annotation.abs.StudentService"/>
<context:annotation-config/>
</beans>
@Autowired的继承正是通过上文分析的,一个简单的do while就实现了。
回到 InjectionMetadata
经过上文的分析,我们就大概知道了所谓的InjectionMetadata究竟是个什么。它存储了某个类,以及这个类里需要被依赖注入的element(标注了@Autowired,@Value等注解的方法或者成员变量)
分析到这里后,随后的就是InjectionMetadata.inject方法。
大概就是遍历之前的List<InjectedElement> 然后调用 InjectedElement.inject方法
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//找到要注入的值
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
特别熟悉的代码,大体和之前通过xml依赖注入的代码一样。找到要注入的对象,注入进去。
总结
这篇博客分析了spring对于注解标记的变量,是如何进行注入了。主要分析了我们最常用的 @Autowired 、@Value 注解。依赖注入分析到这里,基本上就能大致理解spring依赖注入的步骤。当然还是有种无法统一的感觉,下一篇博客会试着从上层到下分析spring依赖注入究竟是如何设计的,以及从spring的依赖注入中我们能学到什么。