重識Spring的BeanPostProcessor處理器
BeanPostProcessor
是Spring用來實作快速拓展最為核心的接口,現在常說的AOP就是通過實作BeanPostProcessor接口來進行拓展。
下面就先看下BeanPostProcessor到底長什麼樣?🧐
BeanPostProcessor接口
public interface BeanPostProcessor {
//在bean init之前執行
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在bean執行完init方法之後執行
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
BeanPostProcessor中就兩個方法,postProcessBeforeInitialization和postProcessAfterInitialization,分别會在Spring執行invokeInitMethods方法(包含了afterPropertiesSet和自定義init方法)前後進行執行。
這裡要特别注意BeanPostProcessor中方法的最後一個單詞是Initialization,這個很容易和postProcessBeforeInstantiation/postProcessAfterInstantiation方法混淆,兩者的調用時機是不一樣的,Initialization是在初始化前後,而Instantiation是在執行個體化前後。
下面介紹下BeanPostProcessor用途比較重要的兩個實作:CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor。
Spring會在添加<context:component-scan > XML配置以及@ComponentScan()注解時建立預設的CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor處理器。
CommonAnnotationBeanPostProcessor
最上層實作就是
BeanPostProcessor
接口。
CommonAnnotationBeanPostProcessor
支援常見的Java注解,特别是JSR-250注解,是以大部分的注解都會關于“資源”的建構、銷毀和使用。
因為實作了InitDestroyAnnotationBeanPostProcessor接口,是以支援
@PostConstruct
,
@PreDestroy
關于對象初始化和銷毀的注解。
支援注解如下:
- @Resource
- @PostConstruct
- @PreDestroy
- @WebServiceRef
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 在bean執行個體化之前調用
*/
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
/**
* 在bean執行個體化之後調用
*/
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
/**
* 當使用注解的時候,通過這個方法來完成屬性的注入
*/
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
/**
* 屬性注入後執行的方法,在5.1版本被廢棄
*/
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
}
如何解析
CommonAnnotationBeanPostProcessor
類中有如下方法會在Bean初始化後執行,用來填充BeanDefinition中的初始化方法以及@Resource對應的字段定義;
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 處理@PostConstruct和@PreDestroy注解
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 處理@Resouce注解對應的屬性以及方法
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
postProcessMergedBeanDefinition
實際調用的是父類
InitDestroyAnnotationBeanPostProcessor
中的方法;
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 擷取生命周期方法
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
// 驗證相關方法
metadata.checkConfigMembers(beanDefinition);
}
findLifecycleMetadata方法中會調用**buildLifecycleMetadata()**方法來建構對應生命周期方法的LifecycleElement(這裡主要是@PostConstruct或者@PreDestroy注解對應的方法)。
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
return this.emptyLifecycleMetadata;
}
// @PostConstruct方法
List<LifecycleElement> initMethods = new ArrayList<>();
// @PreDestroy方法
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
// 周遊目前類以及父類所有方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 目前方法的注解中包含initAnnotationType注解時(@PostConstruct)
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
// 如果有,把它封裝成LifecycleElement對象,存儲起來
LifecycleElement element = new LifecycleElement(method);
// 将建立好的元素添加到集合中
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
// 目前方法的注解中包含destroyAnnotationType注解(PreDestroy)
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
// 如果有,把它封裝成LifecycleElement對象,存儲起來
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
// 擷取父類class對象
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
buildLifecycleMetadata
方法會封裝類中對應生命周期方法的中繼資料,通過
getDeclaredMethods()
擷取類中的所有方法,然後判斷是否使用了**@PostConstruct或者@PreDestroy**注解,是就會以
LifecycleMetadata
的形式放入到
lifecycleMetadataCache
中,并且一層一層擷取父類相關内容。
而
findResourceMetadata
方法同樣會調用
buildResourceMetadata
方法來建構@Resource注解對應的屬性或方法的ResourceElement。
也就是存在如下資料存放方式:
InjectionMetadata -> Collection injectedElements -> ResourceElement
LifecycleMetadata -> Collection initMethods / destroyMethods -> LifecycleElement
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
// 判斷目前的類是否是Resource或javax.xml.ws.WebServiceRef類
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<>();
// @Resource屬性注入
// 查詢是否有webService,ejb,Resource的屬性注解,但是不支援靜态屬性
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else 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");
}
//如果不想注入某一類型對象 可以将其加入ignoredResourceTypes中
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
//字段會封裝到ResourceElement
currElements.add(new ResourceElement(field, field, null));
}
}
});
// @Resource方法注入
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
//如果重寫了父類的方法,則使用子類的
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
// 靜态字段不支援
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else 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);
}
buildResourceMetadata
方法和
buildLifecycleMetadata
實作基本一樣,隻不過是加了字段的周遊以及最後生成的中繼資料不一樣罷了。
PS:
這裡擷取類字段和方法的函數,Spring也是進行了充分的優化;
private static Field[] getDeclaredFields(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
Field[] result = declaredFieldsCache.get(clazz);
if (result == null) {
try {
// 擷取clazz所有定義的屬性
result = clazz.getDeclaredFields();
// EMPTY_FIELD_ARRAY和result在沒有字段情況下都是空數組,是以用一個固定的空數組來表示,減少新對象的記憶體占用
declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
// 傳回clazz的屬性數組
return result;
}
首先,Spring将所有對應的方法或字段都放入到了Map緩存中。
此外,下面這行代碼;
declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
在緩存中還沒有對應類的字段或方法時,通過Class類的
getDeclaredFields
方法先擷取到,然後放入map集合中,主要是這裡會對擷取的數組長度做個判斷,當為0時,實際放入的是EMPTY_FIELD_ARRAY對象,而EMPTY_FIELD_ARRAY定義如下;
EMPTY_FIELD_ARRAY實際就是一個長度為0的數組,實際上EMPTY_FIELD_ARRAY和類字段或方法不存在時代表的含義是相同的,而getDeclaredFields和getDeclaredMethods方法每次都會傳回一個新數組,是以Spring利用EMPTY_FIELD_ARRAY作了代替,來讓result數組較早的回收,就是為了減少記憶體的消耗。
AutowiredAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor從類名的定義上就能看出,該處理器主要适合**@Autowired**注解相關的。該處理器和CommonAnnotationBeanPostProcessor處理器執行的時間都是一樣的,隻是會後于CommonAnnotationBeanPostProcessor處理器執行。
這裡隻介紹相關的幾個重要方法。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 周遊類中的每個屬性,判斷屬性是否包含指定的屬性(通過 findAutowiredAnnotation 方法)
// 如果存在則儲存,這裡注意,屬性儲存的類型是 AutowiredFieldElement
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
//Autowired注解不支援靜态方法
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
//檢視是否是required的
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 周遊類中的每個方法,判斷屬性是否包含指定的屬性(通過 findAutowiredAnnotation 方法)
// 如果存在則儲存,這裡注意,方法儲存的類型是 AutowiredMethodElement
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// 如果方法沒有入參,輸出日志,不做任何處理
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// AutowiredMethodElement裡封裝了一個PropertyDescriptor(比字段多了一個參數)
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 父類的都放在第一位,是以父類是最先完成依賴注入的
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// InjectionMetadata就是對clazz和elements的一個包裝而已
return InjectionMetadata.forElements(elements, clazz);
}
@Autowired處理器和@Resource處理器相比,方法類似,都是擷取到所有的字段或者方法,然後周遊進行相應的判斷處理。@Autowired處理器相對而言,包裝的類型發生了變化(AutowiredMethodElement,AutowiredMethodElement),并且,在@Autowired下,所有對象的注入都是從父類開始的,也就是說在同注解的屬性注入時,父類的屬性注入會先于子類的屬性注入。
兩個處理器在populateBean對象注入的時候,執行的邏輯都是如下所示:
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 這裡擷取到的是之前解析儲存的Metadata
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;
}
findResourceMetadata
從之前解析類時所儲存在BeanPostProcessor中的
injectionMetadataCache
Map集合中擷取到InjectionMetadata對象,然後執行inject方法,循環周遊注入。
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()) {
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
拓展:BeanPostProcessor執行順序
- 實作了PriorityOrdered接口 > Ordered接口 > 兩者都沒實作;
- 實作了MergedBeanDefinitionPostProcessor一定最後執行;
- 相同情況下,還是按照PriorityOrdered > Ordered相對順序。
相同情況下比較如下:
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
// 判斷o1是否實作了PriorityOrdered接口
boolean p1 = (o1 instanceof PriorityOrdered);
// 判斷o2是否實作了PriorityOrdered接口
boolean p2 = (o2 instanceof PriorityOrdered);
// 如果o1實作了PriorityOrdered接口,o2沒有,則o1排前面
if (p1 && !p2) {
return -1;
}
// 如果o2實作了PriorityOrdered接口,而o1沒有,o2排前面
else if (p2 && !p1) {
return 1;
}
// 如果o1和o2都實作或者都沒有實作PriorityOrdered接口
// 拿到o1的order值,如果沒有實作Ordered接口,值為Ordered.LOWEST_PRECEDENCE
int i1 = getOrder(o1, sourceProvider);
// 拿到o2的order值,如果沒有實作Ordered接口,值為Ordered.LOWEST_PRECEDENCE
int i2 = getOrder(o2, sourceProvider);
// 通過order值排序(order值越小,優先級越高)
return Integer.compare(i1, i2);
}