Spring註解式開發(四):Spring底層對BeanPostProcessor的應用

引言

上篇文章介紹了BeanPostProcessor的原理,這周加了一週的班,週末靜下心來繼續spring註解式開發的學習。

Spring提供了哪些BeanPostProcessor

spring爲我們提供了很多BeanPostProcessor,使用開發工具可以看到下圖,我們就挑幾個介紹一下
在這裏插入圖片描述
這只是其中的一部分

1.ApplicationContextAwareProcessor的使用

ApplicationContextAwareProcessor是一個Spring內部工具,它實現了接口BeanPostProcessor,用於向實現瞭如下某種Aware接口的bean設置ApplicationContext中相應的屬性:
通過ApplicationContextAwareProcessor源碼中postProcessBeforeInitialization的實現可以看出只要我們的bean組件實現了以下接口就會爲這個bean設置ApplicationContext中相應的屬性:

  • EnvironmentAware 用於讀取配置文件中的信息
  • EmbeddedValueResolverAware 獲取Spring容器加載的 properties文件屬性值
  • ResourceLoaderAware 獲取資源加載器,獲得外部資源文件
  • ApplicationEventPublisherAware ApplicationEventPublisherAware事件發佈詳解
  • MessageSourceAware Spring的MessageSource的作用
  • ApplicationContextAware 獲取IOC容器
    那麼如果我們的bean實現了相應的Aware接口,就會去執行invokeAwareInterfaces爲bean設置ApplicationContext中相應的屬性
class ApplicationContextAwareProcessor implements BeanPostProcessor {

	private final ConfigurableApplicationContext applicationContext;

	private final StringValueResolver embeddedValueResolver;


	/**
	 * Create a new ApplicationContextAwareProcessor for the given context.
	 */
	public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
	}

	@Override
	public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
		AccessControlContext acc = null;
		if (System.getSecurityManager() != null &&
				(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
						bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
						bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run() {
					invokeAwareInterfaces(bean);
					return null;
				}
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
	}

	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		return bean;
	}
}

那麼我就舉一個實現了ResourceLoaderAware接口的bean做一個測試

ResourceLoaderAware可以用來獲取資源加載器,獲得外部資源文件

//配置類
@Configuration
public class AwareConfig {
    @Bean
    public MyAwareBean myAwareBean(){
        return new MyAwareBean();
    }
}
//自定義組件實現ResourceLoaderAware
public class MyAwareBean implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }
}
//測試
@Test
    public void TestAwareProcessor(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AwareConfig.class);
        MyAwareBean bean = applicationContext.getBean(MyAwareBean.class);
        ResourceLoader resourceLoader = bean.getResourceLoader();
        System.out.println("resourceLoader:"+resourceLoader);
        Resource resource=resourceLoader.getResource("classpath:aware-test.txt");
        System.out.println("ResourceLoader加載的文件內容是:");
        String line=null;
        BufferedReader reader=new BufferedReader(new InputStreamReader(resource.getInputStream()));
        while((line=reader.readLine())!=null){
            System.out.println(line);
        }
        reader.close();
    }

測試結果(文字是aware-test.txt中的類容)

resourceLoader:org.springframework.context.annotation.AnnotationConfigApplicationContext@4c15e7fd: startup date [Sat Jan 12 15:34:33 CST 2019]; root of context hierarchy
ResourceLoader加載的文件內容是:
    致橡樹
我如果愛你——
絕不學攀援的凌霄花,借你的高枝炫耀自己;
絕不學癡情的鳥兒,爲綠蔭重複單調的歌曲;
也不止象泉源,常年送來清涼的慰籍;也不止象險峯,
增加你的高度,襯托你的威儀。
甚至日光,甚至春雨,
不,這些都還不夠,我必須是你近旁的一株木棉,
作爲樹的形象和你站在一起。
根,相握在地下;
葉,相觸在雲裏。
每一陣風吹過,我們都互相致意,
但沒有人,聽懂我們的言語。
你有你的銅枝鐵幹,
象刀象劍也象戟;
我有我紅碩的花朵,
象沉重的嘆息,
又象英勇的火炬,
我們分擔寒潮風雷霹靂;我們共享霧靄流嵐虹霓;
彷彿永遠分離,卻又終身相依。
這纔是偉大的愛情,堅貞就在這裏――
愛,不僅愛你偉岸的身軀,
也愛你堅持的位置,足下的土地

其他的就不用舉例了,用法基本都是類似的,主要要了解不同的Aware可以提供什麼功能以及這些功能在開發中的用處,比如這個讀取外部資源的就可以使用配置文件來給屬性賦值,這樣就可以做動態的配置

AutowiredAnnotationBeanPostProcessor介紹

這是一個解析bean中@Autowire註解的類,這個類實現了BeanPostProcessor接口
接下來看一下實現源碼

@Override
public PropertyValues postProcessPropertyValues(
		PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
	//獲取Autowire元數據
	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
	try {
		//將獲取的元素注入到bean中
		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;
}

上面的代碼我們先來看findAutowiringMetadata方法,其實這個方法我們可以不用關心,如何獲取Autowire元數可以看看,其中的關鍵代碼就是InjectionMetadata metadata =this.injectionMetadataCache.get(cacheKey);這句話。從這裏我們知道他是從this.injectionMetadataCache,這是一個Map,key爲beanName,value爲注入元數據InjectionMetadata。即直接從這個Map中獲取,那麼接下來我們就要知道這個注入信息是什麼時候放入到這個緩存Map上的。從findAutowiringMetadata代碼上,我們看到this.injectionMetadataCache.put(cacheKey, metadata);這段代碼,這代碼就是把注入信息放到this.injectionMetadataCache上。那麼,從這裏我們可以猜測,在Bean實例化過程中findAutowiringMetadata()這個方法肯定被調用了多次。

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
	// 獲取緩存的的key
	String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// 根據緩存的key獲取緩存中的metadata,此處我們在開發中也可以借鑑,使用hashMap做緩存來加速計算
	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);
				}
				try {
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
				catch (NoClassDefFoundError err) {
					throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
							"] for autowiring metadata: could not find class that it depends on", err);
				}
			}
		}
	}
	return metadata;
}

當獲取到元數據之後就開調用inject方法注入

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
	Collection<InjectedElement> elementsToIterate =
			(this.checkedElements != null ? this.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);
			}
			//開始向bean中注入element
			element.inject(target, beanName, pvs);
		}
	}
}

接下來看一下AutowiredFieldElement.inject(target, beanName, pvs);

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<String>(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;
					}
				}
			}
			//當獲取的值不是null時就開始注入
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

到此@Autowire註解大致原理就講完了,其實其中還有很多細節,比如這個註解的的屬性是什麼時機注入到bean中的,後續再補充吧

結束語

下篇文章將介紹屬性賦值和自動裝配

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章