spring在早起的是時候是通過xml進行配置的bean的,但是發現所有的bean都放到xml中的時候,密密麻麻的xml配置非常混亂,乍眼一看一定很頭暈。之後,spring引入了註解,只是需要在類上加上註解就可以了,非常的方便,但是這些註解又是如何解析的呢?spring是如何做到如此的方便的呢?註解解析的位置不同,這裏只介紹@Controller
,@Service
,@Autowired
等註解的解析過程。
那麼什麼是
BeanDefinition
呢?BeanDefinition
可以看作是一個內部的配置文件,spring
通過註解或者xml等,把bean的信息存儲到BeanDefinition
中,包括:類名、scope、屬性、構造函數參數列表、依賴的bean、是否是單例類、是否是懶加載等等。
1. xml文件解析過程
首先是要配置包的路徑。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" 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.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
>
<!-- 配置要掃描的路徑-->
<context:component-scan base-package="com.controller"/>
</beans>
這段是說明spring要掃描com.controller
路徑下所有的類,然後把配置註解(spring管理的註解)類進行統一處理。
流程比較複雜,直接上圖。。
-
此以ClassPathXmlApplicationContext的構造方法爲入口,其實不管以哪爲入口,容器初始化的時候一定會走
refresh
方法,SpringMVC的初始化也是以refresh
來進行初始化。 -
obtainFreshBeanFactory方法中包括有ioc容器的定位,加載,註冊功能。spring掃描可進行管理的類,也是在這裏。
-
obtainFreshBeanFactory方法中,使用委派模式,使IOC的初始化在子類中進行實現。
-
接下來一致按照流程走,在
AbstractXmlApplicationContext
中的loadBeanDefinitions
方法中獲取Resource
對象。-
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 配置文件這裏根據路徑進行處理 String[] configLocations = getConfigLocations(); if (configLocations != null) { //有時候配置文件路徑要前綴有 classpath , // 從這裏進去 ,再進入((ResourcePatternResolver) resourceLoader).getResources(location); 方法 // 找到 PathMatchingResourcePatternResolver.getResources方法,即可看到對此前綴的處理。 reader.loadBeanDefinitions(configLocations); } }
-
-
xml文件加載完成之後,就要進行讀取了,
XmlBeanDefinitionReader.doLoadBeanDefinitions
方法獲取Document
對象,然後進行解析。
至此,xml讀取完成,接下來進行解析。
2. BeanDefinition解析
-
從
registerBeanDefinitions
方法進入之後,就是獲取doc的根節點進行遍歷。 -
接着向下走進入
parseBeanDefinitions
這個方法還是有點意思。如下-
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //Bean 定義的 Document 對象使用了 Spring 默認的 XML 命名空間 // 那麼啥是默認的呢? if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 這個主要是看xml是否又 http://www.springframework.org/schema/beans 這段,可以留意下,一般spring的xml都有這個鏈接 // 默認的是bean這個標籤 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { // 如果不是默認的,比如 xml 中有一段 <context:component-scan base-package="com.controller"/> ,則就走這段 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
就如上的xml爲例,
isDefaultNamespace
方法進行判斷是當前標籤是普通bean標籤。
-
-
如下,這裏通過uri獲取對應的命名空間處理器,然後進行解析
-
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // 根據命名空間,找到對應的處理器 // 這裏應該是通過策略模式吧。。 省去了if /else NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 進行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
-
順着流程向下走,進入
ComponentScanBeanDefinitionParser.parse
方法。-
public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. // 這裏麪包括 設置上要掃描的註解 @Component 等等 // 從ClassPathBeanDefinitionScanner類的構造方法進行設置註解的 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); // doscan 進行解析,掃描路徑下所有的類,判斷是否包括要過濾的註解 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); // BeanDefinition是獲取到了,但是類裏面的@AutoWrite呢? @Value註解呢? // 就是這裏進行處理的,這裏稍後說 registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
-
-
最後
isCandidateComponent
方法有點意思了,這裏判斷是否有交給spring管理的註解,如下:-
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // 一般情況下,此時includeFilters中有三個註解 @Component,@ManagedBean,@Named for (TypeFilter tf : this.includeFilters) { // 這裏判斷是否有註解 if (tf.match(metadataReader, getMetadataReaderFactory())) { // 這裏是是否跳過的,啥時候跳過呢? 爲什麼要跳過呢? 可以搜索下@Conditional 註解,在spring boot 使用的特別多。 // 參考 https://blog.csdn.net/qq_30285985/article/details/101637212 return isConditionMatch(metadataReader); } } return false; }
在ClassPathScanningCandidateComponentProvider.registerDefaultFilters進去,則看到includeFilters集合中如何進行設置值。
protected void registerDefaultFilters() { // 這裏加載到這個 集合中 一共有三個註解 @Component,@ManagedBean,@Named ,也就是說,只要有這三個註解的類都會被spring所管理。 // @Component 不用多說 // @ManagedBean 是JSF中用到的 // @Named 和@Commponent類似 說實話, 也沒有百度出啥區別,總之很少用。 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
但是到這裏有些疑問,這裏的註解,在平時開發的時候,只用到了
@Component
呀?那@Controller
,@Service
這些配置的類就不處理了麼?後來發現,其實@Controller
包含了@Component
註解@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component // 這裏包含了 public @interface Controller { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any (or empty String otherwise) */ @AliasFor(annotation = Component.class) String value() default ""; }
-
到此,spring掃描包,然後判斷包上是否設置了對應的註解,生成了BeanDefinition
,但是對應類下的屬性,還需要解析。
3. Autowired等註解解析
-
ComponentScanBeanDefinitionParserr.egisterComponents
方法是對@Autowired
等註解進行處理的。 -
進入
AnnotationConfigUtils.registerAnnotationConfigProcessors
方法,如下:-
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { //....省略前面代碼 if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { //這裏就是對Autowire註解進行處理的。 RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } //....省略後面代碼 return beanDefs; }
如上,剛剛開始看到,我也有些疑問,
AutowiredAnnotationBeanPostProcessor
這個類是幹啥的?看着好像是和@Autowired
註解有一定的關係。但是這裏又不是直接對@Autowired
註解做處理。這裏要對
InstantiationAwareBeanPostProcessor
接口和BeanPostProcessor
有一定的瞭解。不瞭解可以進去看下兩個接口的介紹。
-
-
瞭解了兩個接口的作用之後,就可以着重看下這兩個接口主要實現了什麼了。
public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader(); try { this.autowiredAnnotationTypes.add(cl.loadClass("javax.inject.Inject")); this.logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException var3) { ; } }
AutowiredAnnotationBeanPostProcessor在初始化的時候,就會加載
@Autowired
,@Value
,@Inject
這三個註解。 -
當bean設置值的時候,進行走如下方法:
-
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { //檢索出帶有註解的屬性,這裏的註解是初始化時加載的那三個註解 InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass()); try { // 進行注入 metadata.inject(bean, beanName, pvs); return pvs; } catch (Throwable var7) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7); } }
最後進入
AutowiredAnnotationBeanPostProcessor.inject
。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; // 判斷是否有緩存, // 就比如,有一個service,在多個地方進行自動注入的話,那麼第二次注入的話,就是走緩存了。 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); Assert.state(beanFactory != null, "No BeanFactory available"); 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) && 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); } } }
-
更多源碼中文註釋,有興趣的直接拉去代碼看把。