首頁 > 軟體

Spring BeanPostProcessor原始碼範例解析

2023-01-16 14:00:36

正文

BeanPostProcessor對於Spring開發者來說一定不陌生,平時開發過程中如果涉及對中介軟體做增強處理或者需要對某個實際Bean做初始化前的處理,一般都可以使用該介面來實現。

對於自己來說自己在對接RocketMQ、RabbitMQ過程中需要實現一個快速接入SpringBoot的中間外掛,最快速的就是實現該介面對中介軟體中的Bean做增強處理,能夠快速接入SpringBoot。

1. BeanPostProcessor介紹

BeanPostProcessor是一個介面,該介面提供給開發者使用,開發者可以實現該介面,然後在實現類中對想要處理的類做增強處理。

該介面中提供了兩個基礎方法:

  • postProcessBeforeInitialization:Bean初始化前對Bean的增強處理
  • postProcessAfterInitialization:Bean初始化後對Bean的增強處理
public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

下面舉一個最簡單的例子來看一下BeanPostProcessor的使用。

2. BeanPostProcessor的使用

@Service
public class UserService {
    private String name = "陳tom";
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
@Component
public class MyPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            UserService userService = (UserService) bean;
            userService.setName("陳湯姆");
            userService.setAge(21);
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
@SpringBootApplication
public class BootApplication {
    @Resource
    private DefaultListableBeanFactory defaultListableBeanFactory;
    public static void main(String[] args) {
        SpringApplication.run(BootApplication.class,args);
    }
    @PostConstruct
    public void init() {
        UserService userService = defaultListableBeanFactory.getBean("userService", UserService.class);
        System.out.println("測試結果:姓名:"+userService.getName()+";年齡:"+userService.getAge());
    }
}

以上的實現中做了最簡單的實現,通過MyPostProcessor實現BeanPostProcessor介面,然後在前置增強處理中加入對Bean的修改。

然後通過PostConstruct在Bean初始化後做Bean的查詢,得到通過BeanPostProcessor操作的結果。

所以BeanPostProcessor在使用上來說很簡單,對於開發者來說它很方便的提供了對於Bean物件的修改操作,提高了Spring的擴充套件性和靈活性。

3. BeanPostProcessor的作用

說完了最基礎的BeanPostProcessor的使用,通過以上的實現來梳理下BeanPostProcessor的作用。

  • 對Bean的修改操作,通過實現該介面可以很快的實現對Bean的操作
  • 提供前置和後置兩個處理,提供開發者可以在初始化前做處理以及初始化後做處理
  • 對於Spring框架的靈活性,使用Spring框架也可以很方便的做自己的邏輯處理

自己對於BeanPostProcessor最大的感受就是太靈活了,開發者只要實現該介面,然後重寫其中的方法就可以實現自己的邏輯。這樣也保證了Spring容器對於開發者來說並不是一個黑盒,開發者可以通過Spring提供的鑰匙讓Spring容器暴露在開發者眼中,想要做什麼處理都可以根據提供的鑰匙實現。(這裡其實還有一個配套的BeanFactoryPostProcessor,面試的時候也經常被問到BeanPostProcessor和BeanFactoryPostProcessor的區別,有興趣的可以自己瞭解下,區別只是最終服務的物件不一樣)

這裡不得不說Spring YYDS!!!

4. BeanPostProcessor註冊

Q:既然開發者通過自己的實現類實現了BeanPostProcessor,那麼Spring又是如何將開發者做的實現類加入到Spring中並且執行開發者重寫的前置和後置增強邏輯呢?

這裡就要提到Spring對於BeanPostProcessor的設計,在Spring中初始化了一個CopyOnWriteArrayList作為儲存所有實現BeanPostProcessor的列表。

從實現上可以看到該列表是一個寫複製的列表,通俗點講就是向該列表中新增元素時,不直接往當前容器新增,而是先將當前容器進行Copy,複製出一個新的容器,然後新的容器裡新增元素,新增完元素之後,再將原容器的參照指向新的容器。這裡不展開說只要知道就是一個列表就可以。

Spring向該容器中新增的邏輯原始碼如下:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	//儲存BeanPostProcessor列表
	private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
	@Override
	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
		Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
		// Remove from old position, if any
		this.beanPostProcessors.remove(beanPostProcessor);
		// Track whether it is instantiation/destruction aware
		if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
			this.hasInstantiationAwareBeanPostProcessors = true;
		}
		if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
			this.hasDestructionAwareBeanPostProcessors = true;
		}
		// 將BeanPostProcessor加入beanPostProcessors列表中
		this.beanPostProcessors.add(beanPostProcessor);
	}
}

這裡展示的只是最終寫入的邏輯,那麼從源頭到寫入BeanPostProcessor的邏輯是怎麼樣呢?

以ClassPatchXmlApplicationContext為例:

  • org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext
  • org.springframework.context.support.AbstractApplicationContext#refresh
  • org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
  • org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanPostProcessor>)
  • org.springframework.beans.factory.support.AbstractBeanFactory#addBeanPostProcessor

時序圖如下:

5. BeanPostProcessor呼叫

說完如何註冊BeanPostProcessor,最後來說如何將註冊到beanPostProcessors的實現類進行呼叫。

前一篇聊Spring容器時也提到了Spring在初始化Bean時會執行BeanPostProcessor的增強處理,具體的呼叫原始碼如下:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
	//初始化Bean
	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			//呼叫BeanPostProcesso#postProcessBeforeInitialization:前置處理
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
		try {
			//執行初始化方法
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			//呼叫呼叫BeanPostProcesso#postProcessAfterInitialization:後置處理
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}
	//BeanPostProcessor前置邏輯處理
	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//執行實現BeanPostProcessor的實現類的前置增強邏輯
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
	//BeanPostProcessor後置邏輯處理
	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//執行實現BeanPostProcessor的實現類的後置增強邏輯
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
}

從以上原始碼中可以看到在初始化Bean時呼叫前置和後置增強,並且這裡也做了明確的邊界,前置增強處理一定是在invokeInitMethods之前,後置增強一定是在invokeInitMethods之後。這樣就保證了BeanPostProcessor的執行順序,也可以讓開發者在使用過程中只要掌握Bean的生命週期,那麼就可以自主的對Bean進行增強處理。

6. 總結

以上是對Spring中BeanPostProcessor的原始碼梳理,對BeanPostProcessor的介紹到使用、作用、註冊實現和呼叫實現,完成了對BeanPostProcessor的原始碼分析。

對於文中提到的BeanFactoryPostProcessor有興趣的可以從原始碼按照這個過程梳理一下,其實跟BeanPostProcessor作用是相同的。

以上就是Spring BeanPostProcessor原始碼範例解析的詳細內容,更多關於Spring BeanPostProcessor的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com