Spring5源碼解析-@Autowired

你有沒有思考過Spring中的@Autowired註解?一般用於方便依賴注入,而隱藏在這個過程以後的機制究竟是怎樣,將在本篇中進行講述。

@Autowired所具備的功能

@Autowired是一個用來執行依賴注入的註解。每當一個Spring管理的bean發現有這個註解時候,它會直接注入相應的另外一個Spring管理的bean。java

該註解能夠在不一樣的層次上應用:git

  • 類字段:Spring將經過掃描自定義的packages(例如在咱們所註解的controllers)或經過在配置文件中直接查找bean。
  • 方法:使用@Autowired註解的每一個方法都要用到依賴注入。但要注意的是,方法簽名中呈現的全部對象都必須是Spring所管理的bean。若是你有一個方法,好比setTest(Article article, NoSpringArticle noSpringArt) ,其中只有一個參數 (Article article)是由Spring管理的,那麼就將拋出一個org.springframework.beans.factory.BeanCreationException異常。這是因爲Spring容器裏並無指定的一個或多個參數所指向的bean,因此也就沒法解析它們。完整的異常跟蹤以下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.krams.tutorial.controller.TestController.ix(com.mysite.controller.IndexController,com.mysite.nospring.data.Article); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mysite.nospring.data.Article] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
複製代碼
  • 構造函數:@Autowired的工做方式和方法相同。

對象注入須要遵循一些規則。一個bean能夠按照下面的方式注入: github

  • 名稱:bean解析是經過bean名稱(看後面的例子)。
  • 類型:解析過程基於bean的類型。

在某些狀況下,@Autowired應該經過@Qualifier註解協做注入。例以下面幾個是相同類型的bean:spring

<bean name="comment1" class="com.waitingforcode.Comment">
    <property name="text" value="Content of the 1st comment" />
</bean>
 
<bean name="comment2" class="com.waitingforcode.Comment">
    <property name="text" value="Content of the 2nd comment" />
</bean>
複製代碼

上面這種狀況,假如只是一個簡單的@Autowired,Spring根本不知道你要注入哪一個bean。這就是爲何咱們要使用@Qualifier(value =「beanName」)這個註解。在咱們的例子中,要從 com.waitingforcode.Comment這個類型的bean中區分comment1,comment2,咱們能夠寫下面的代碼:bootstrap

@Qualifier(value="comment1")
@Autowired
private Comment firstComment;
 
@Qualifier(value="comment2")
@Autowired
private Comment secondComment;
複製代碼

在Spring中如何使用@Autowired

正如前面部分所看到的,咱們知道了在Spring中實現@Autowired的不一樣方法。在這一部分中,咱們將使用XML配置的方式激活@Autowired註解來自動注入。而後,咱們將編寫一個簡單的類並配置一些bean。最後,咱們將分別在另外兩個類中使用它們:由@Controller註解的控件和不禁Spring所管理的類。(爲何用XML配置來作例子,我以爲這樣更直觀,其實XML和使用註解沒多少區別,都是往容器裏添加一些bean和組織下彼此之間的依賴而已,沒必要要非要拘泥於一種形式,哪一種順手用哪一種,不過Springboot自定義的這些仍是推薦使用註解了)markdown

咱們從啓動註解的自動注入開始:app

<context:annotation-config />
複製代碼

你必須將上面這個放在應用程序上下文配置中。它可使在遇到@Autowired註解時啓用依賴注入。ide

如今,咱們來編寫和配置咱們的bean:函數

// beans first
public class Comment {
 
    private String content;
     
    public void setContent(String content) {
        this.content = content;
    }
     
    public String getContent() {
        return this.content;
    }
     
}
 
// sample controller
@Controller
public class TestController {
     
    @Qualifier(value="comment1")
    @Autowired
    private Comment firstComment;
     
    @Qualifier(value="comment2")
    @Autowired
    private Comment secondComment;
     
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String test() {
        System.out.println("1st comment text: "+firstComment.getText());
        System.out.println("2nd comment text: "+secondComment.getText());
        return "test";
    }
 
}
 
// no-Spring managed class
public class TestNoSpring {
 
    @Autowired
    private Comment comment;
     
     
    public void testComment(String content) {
        if (comment == null) {
            System.out.println("Comment's instance wasn't autowired because this class is not Spring-managed bean");
        } else {
            comment.setContent(content);
            System.out.println("Comment's content: "+comment.getContent());
        }
    }
     
}
複製代碼

XML配置(在前面部分已經看到過):oop

<bean name="comment1" class="com.specimen.exchanger.Comment">
    <property name="content" value="Content of the 1st comment" />
</bean>
 
<bean name="comment2" class="com.specimen.exchanger.Comment">
    <property name="content" value="Content of the 2nd comment" />
</bean>
複製代碼

如今,咱們打開http://localhost:8080/test來運行TestController。如預期的那樣,TestController的註解字段正確地自動注入,而TestNoSpring的註解字段並無注入進去:

1st comment text: Content of the 1st comment
2nd comment text: Content of the 2nd comment
Comment's instance wasn't autowired because this class is not Spring-managed bean
複製代碼

哪裏不對 ?TestNoSpring類不禁Spring所管理。這就是爲何Spring不能注入Comment實例的依賴。咱們將在下一部分中解釋這個概念。

@Autowired註解背後的工做原理?

在討論代碼細節以前,咱們再來了解下基礎知識。Spring管理可用於整個應用程序的Java對象bean。他們所在的Spring容器,被稱爲應用程序上下文。這意味着咱們不須要處理他們的生命週期(初始化,銷燬)。該任務由此容器來完成。另外,該上下文具備入口點,在Web應用程序中,是dispatcher servlet。容器(也就是該上下文)會在它那裏被啓動而且全部的bean都會被注入。

說的再清楚點,請看<context:annotation-config />的定義:

<xsd:element name="annotation-config">
		<xsd:annotation>
			<xsd:documentation><![CDATA[
	Activates various annotations to be detected in bean classes: Spring's @Required and
	@Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),
	JAX-WS's @WebServiceRef (if available), EJB 3's @EJB (if available), and JPA's
	@PersistenceContext and @PersistenceUnit (if available). Alternatively, you may
	choose to activate the individual BeanPostProcessors for those annotations.
	Note: This tag does not activate processing of Spring's @Transactional or EJB 3's
	@TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
	tag for that purpose.
	See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext
	for information on code-based alternatives to bootstrapping annotation-driven support.
			]]></xsd:documentation>
		</xsd:annotation>
	</xsd:element>
複製代碼

能夠看出 : 類內部的註解,如:@Autowired、@Value、@Required、@Resource以及EJB和WebSerivce相關的註解,是容器對Bean對象實例化和依賴注入時,經過容器中註冊的Bean後置處理器處理這些註解的。

因此配置了上面這個配置(<context:component-scan>假若有配置這個,那麼就能夠省略<context:annotation-config />)後,將隱式地向Spring容器註冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor以及這4個專門用於處理註解的Bean後置處理器。

當 Spring 容器啓動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中全部 Bean,當發現 Bean 中擁有@Autowired 註解時就找到和其匹配(默認按類型匹配)的 Bean並注入到對應的地方中去。 源碼分析以下:

經過org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor能夠實現依賴自動注入。經過這個類來處理@Autowired和@Value這倆Spring註解。它也能夠管理JSR-303的@Inject註解(若是可用的話)。在AutowiredAnnotationBeanPostProcessor構造函數中定義要處理的註解:

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
		implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
		...
	/**
 * Create a new AutowiredAnnotationBeanPostProcessor
 * for Spring's standard {@link Autowired} annotation.
 * <p>Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
 */
	@SuppressWarnings("unchecked")
	public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}
	...
	}
複製代碼

以後,有幾種方法來對@Autowired註解進行處理。

第一個,private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz)解析等待自動注入類的全部屬性。它經過分析全部字段和方法並初始化org.springframework.beans.factory.annotation.InjectionMetadata類的實例來實現。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
		Class<?> targetClass = clazz;
		do {
			final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
			//分析全部字段
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
              //findAutowiredAnnotation(field)此方法後面會解釋
				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));
				}
			});
			//分析全部方法
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				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 only 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);
		//返回一個InjectionMetadata初始化的對象實例
		return new InjectionMetadata(clazz, elements);
	}
...
  /**
 * 'Native' processing method for direct calls with an arbitrary target instance,
 * resolving all of its fields and methods which are annotated with {@code @Autowired}.
 * @param bean the target instance to process
 * @throws BeanCreationException if autowiring failed
 */
	public void processInjection(Object bean) throws BeanCreationException {
		Class<?> clazz = bean.getClass();
		InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
		try {
			metadata.inject(bean, null, null);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					"Injection of autowired dependencies failed for class [" + clazz + "]", ex);
		}
	}
複製代碼

InjectionMetadata類包含要注入的元素的列表。注入是經過Java的API Reflection (Field set(Object obj, Object value) 或Method invoke(Object obj,Object ... args)方法完成的。此過程直接在AutowiredAnnotationBeanPostProcessor的方法中調用public void processInjection(Object bean) throws BeanCreationException。它將全部可注入的bean檢索爲InjectionMetadata實例,並調用它們的inject()方法。

public class InjectionMetadata {
  ...
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			boolean debug = logger.isDebugEnabled();
			for (InjectedElement element : elementsToIterate) {
				if (debug) {
					logger.debug("Processing injected element of bean '" + beanName + "': " + element);
				}
              	//看下面靜態內部類的方法
				element.inject(target, beanName, pvs);
			}
		}
	}
  ...
    public static abstract class InjectedElement {
		protected final Member member;
		protected final boolean isField;
      ...
        /**
 * Either this or {@link #getResourceToInject} needs to be overridden.
 */
		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 {
				if (checkPropertySkipping(pvs)) {
					return;
				}
				try {
                  	//具體的注入看此處咯
					Method method = (Method) this.member;
					ReflectionUtils.makeAccessible(method);
					method.invoke(target, getResourceToInject(target, requestingBeanName));
				}
				catch (InvocationTargetException ex) {
					throw ex.getTargetException();
				}
			}
		}
      ...
    }
}
複製代碼

AutowiredAnnotationBeanPostProcessor類中的另外一個重要方法是private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao)。它經過分析屬於一個字段或一個方法的全部註解來查找@Autowired註解。若是未找到@Autowired註解,則返回null,字段或方法也就視爲不可注入。

@Nullable
	private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
		if (ao.getAnnotations().length > 0) {
			for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
				AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
				if (attributes != null) {
					return attributes;
				}
			}
		}
		return null;
	}
複製代碼

在上面的文章中,咱們看到了Spring中自動注入過程。經過整篇文章能夠看到,這種依賴注入是一種便捷易操做方式(能夠在字段以及方法上完成),也促使咱們逐漸在拋棄XML配置文件。還加強了代碼的可讀性。

原文: Spring5源碼解析-@Autowired
相關文章
相關標籤/搜索