本文內容如有錯誤、不足之處,歡迎技術愛好者們一同探討,在本文下面討論區留言,感謝。
文章目錄
簡述
Processor 中文翻譯:處理器、加工機,這裏 Processor 只是一種實現類描述其功能具有處理能力。
在 Spring 中,有兩個核心 Processor 接口:BeanPostProcessor 和 BeanFactoryPostProcessor ;
在 Spring Boot 中, 有個核心 Processor 接口: EnvironmentPostProcessor;
通過閱讀 Spring 源碼可以學習到許多設計和類命名上的知識,不但可以幫助學習 Spring 框架,而且也可以幫助學習優秀編碼習慣。
自定義Processor類
在實際開發過程中,並沒有約定的一種 Processor 必然要實現的模式,它只是表示這個類的功能是有做處理一些其他功能。下面舉個例子說明:
SmsProcessor.java
@Component(value = "smsProcessor")
public class SmsProcessor {
@Autowired
private JmsMessagingTemplate jmsTemplate;
@Autowired
@Qualifier("verCodeService")
private SmsSender smsSender;
public void sendSmsToQueue(Destination destination, final String message){
jmsTemplate.convertAndSend(destination, message);
}
@JmsListener(destination="sms.queue")
public void doSendSmsMessage(String text){
JSONObject jsonObject = JSON.parseObject(text);
smsSender.sendSms(jsonObject.getString("mobile") , jsonObject.getString("tplId"),jsonObject.getString("vercode"));
}
}
用來處理消息的處理類,主要功能是發送消息到隊列,和從隊列獲取消息進行處理功能。
原理
其實,Processor 沒有什麼原理,主要原理就是這個翻譯上:處理機、加工機。
下面介紹一下 Spring 和 Spring Boot 中的核心 Processor 類的原理,幫助理解 Spring 的設計思路。
Spring 中的 Processor
Spring 設計時考慮的設計模式的開閉原則,因此提供了大量的擴展接口,其中就有 BeanPostProcessor 和 BeanFactoryPostProcessor 。
作用:
- BeanPostProcessor : 對容器中的 Bean 進行後處理,增強 Bean 的功能
- BeanFactoryPostProcessor :對 Spring 容器本身進行後處理,增強容器的功能
區別:
- BeanPostProcessor 執行順序在 BeanFactoryPostProcessor 之後
- BeanPostProcessor 是 Spring 容器加載 Bean 的定義文件並且實例化 Bean 之後,在容器執行 InitializingBean 之前執行的。
- BeanFactoryPostProcessor 是 Spring 容器加載 Bean 的定義文件之後,在執行 Bean 實例化之前執行的。
BeanFactoryPostProcessor 流程圖如下:
BeanPostProcessor 流程圖如下
BeanPostProcessor 接口源碼
public interface BeanPostProcessor {
/**
* Bean 初始化執行之前
* @param bean 處理的bean實例
* @param beanName bean的名稱
* @return Object bean的實例
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Bean 初始化執行之後
* @param bean 處理的bean實例
* @param beanName bean的名稱
* @return Object bean的實例
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
在每個 bean 加載過程中,spring 會調用所有實現 BeanPostProcessor 接口的類方法,一般實現的邏輯都是:
- 判斷 bean 的類型,篩選要處理的 bean
- 針對選出的 bean 執行相應邏輯
每個實現 BeanPostProcessor 接口的類的調用順序是通過實現 Ordered 來定義先後順序:
Ordered 接口源碼:
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
在 Spring 框架中,實現的不是 Ordered 接口,而是 PriorityOrdered 接口:
public interface PriorityOrdered extends Ordered {
}
PriorityOrdered 除了繼承 Ordered 接口外,沒有做其他處理操作,這裏是一個設計理念,通過類名來構造方便理解和維護的代碼。
BeanFactoryPostProcessor 接口源碼
public interface BeanFactoryPostProcessor {
/**
* 標準初始化後,修改應用上下文的內部beanFactory,根據需要進行修改,對容器進行處理。
* 修改 bean 的配置元信息
* @param beanFactory 應用上下文 bean 工廠
* @throws org.springframework.beans.BeansException Beans 異常
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
實現 BeanFactoryPostProcessor 接口的類,邏輯和 BeanPostProcessor 相似,同時也是需要實現 PriorityOrdered 接口。
Spring 實現類
實現 BeanFactoryPostProcessor
序號 | 類名 | 作用 |
---|---|---|
0 | PropertyPlaceholderConfigurer | 屬性佔位符配置器 |
1 | PropertyOverrideConfigurer | 重寫佔位符配置器 |
2 | CustomAutowireConfigurer | 自定義自動裝配的配置器 |
3 | AutowiredAnnotationBeanPostProcessor | 自動裝配的配置器 |
4 | CustomScopeConfigurer | 自定義作用域的配置器 |
實現 BeanPostProcessor
序號 | 類名 | 作用 |
---|---|---|
0 | CommonAnnotationBeanPostProcessor | 支持@Resource註解的注入 |
1 | RequiredAnnotationBeanPostProcessor | 支持@Required註解的注入 |
2 | AutowiredAnnotationBeanPostProcessor | 支持@Autowired註解的注入 |
3 | PersistenceAnnotationBeanPostProcessor | 支持@PersistenceUnit和@PersistenceContext註解的注入 |
4 | ApplicationContextAwareProcessor | 爲bean注入ApplicationContext等容器對象 |
Spring Boot 中的 Processor
使用這個進行配置文件的集中管理,而不需要每個項目都去配置配置文件,自定義屬性加載和轉換到 Environment 中,然後訪問這些屬性,這些配置是系統級別的配置,相當於容器的基礎使用配置。
實現 EnvironmentPostProcessor, 自定義環境後處理類,意味着在將各種環境屬性暴露給容器中的bean之前對其進行操作,例如:增加一個環境屬性表示,需要特定的類(用戶Bean)是否進行特定的工作(打印出用戶Bean信息)。
執行時間:在 Spring 上下文構建之前執行。
EnvironmentPostProcessor 接口源碼
public interface EnvironmentPostProcessor {
/**
* 處理 environment
* @param environment 需要處理的容器環境 environment
* @param application 環境所屬的應用程序
*/
void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application);
}
Spring Boot 實現類
實現 EnvironmentPostProcessor
序號 | 類名 | 作用 |
---|---|---|
0 | CloudFoundryVcapEnvironmentPostProcessor | 雲構建環境處理 |
1 | ConfigFileApplicationListener | 支持 配置文件監聽 |
2 | SpringApplicationJsonEnvironmentPostProcessor | 支持 spring 應用上下文 JSON環境 |
注意:
- 實現 EnvironmentPostProcessor 接口的同時需要實現 Ordered 排序接口,也可以實現 PriorityOrdered 接口。
- EnvironmentPostProcessor 的實現類必須要在 META-INF/spring.factories 文件中去註冊,並且註冊的是全類名。
例子
下面的例子首先給出 Spring 框架是如何實現,然後給出自定義實現。
Spring 的例子
BeanFactoryPostProcessor
CustomScopeConfigurer.java 源碼分析
public class CustomScopeConfigurer implements BeanFactoryPostProcessor, BeanClassLoaderAware, Ordered {
// 保存 scope 和 scope 對象,scope 是控制 bean 作用域的屬性
@Nullable
private Map<String, Object> scopes;
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.scopes != null) {
// 迭代作用域Map
this.scopes.forEach((scopeKey, value) -> {
// 對象類型爲 Scope 對象
if (value instanceof Scope) {
// 註冊作用域
beanFactory.registerScope(scopeKey, (Scope)value);
} else {
Class scopeClass;
// 對象類型是類類型
if (value instanceof Class) {
scopeClass = (Class)value;
// 校驗是否爲 作用域類型
Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
// 註冊作用域
beanFactory.registerScope(scopeKey, (Scope)BeanUtils.instantiateClass(scopeClass));
} else {
// 對象類型不屬於字符串,拋出非法論點異常
if (!(value instanceof String)) {
throw new IllegalArgumentException("Mapped value [" + value + "] for scope key [" + scopeKey + "] is not an instance of required type [" + Scope.class.getName() + "] or a corresponding Class or String value indicating a Scope implementation");
}
// 類加載對應的作用域類
scopeClass = ClassUtils.resolveClassName((String)value, this.beanClassLoader);
Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
// 註冊作用域
beanFactory.registerScope(scopeKey, (Scope)BeanUtils.instantiateClass(scopeClass));
}
}
});
}
}
}
自定義 BeanFactoryPostProcessor 類
MyBeanFactoryBean.java
public class MyBeanFactoryBean implements InitializingBean {
private String initEcho;
private String processorEcho;
public String getProcessorEcho() {
return processorEcho;
}
public void setProcessorEcho(String processorEcho) {
this.processorEcho = processorEcho;
}
public String getInitEcho() {
return initEcho;
}
public void setInitEcho(String initEcho) {
this.initEcho = initEcho;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("調用afterPropertiesSet方法");
this.initEcho = "在初始化方法中修改之後的信息";
}
}
MyBeanFactoryPostProcessor.java
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("調用MyBeanFactoryPostProcessor的postProcessBeanFactory");
BeanDefinition bd = configurableListableBeanFactory.getBeanDefinition("myBeanFactoryBean");
MutablePropertyValues pv = bd.getPropertyValues();
pv.addPropertyValue("processorEcho", "在BeanFactoryPostProcessor中添加之後的信息");
}
}
測試方式,放到啓動類裏面進行注入和獲取:
@SpringBootApplication
public class ProcessorApplication {
public static void main(String[] args) {
//啓動WEB項目
SpringApplication application = new SpringApplication(ProcessorApplication.class);
ConfigurableApplicationContext context = application.run(args);
MyBeanFactoryBean bean = (MyBeanFactoryBean) context.getBean("myBeanFactoryBean");
System.out.println("===============下面輸出結果============");
System.out.println("初始化信息:" + bean.getInitEcho());
System.out.println("後處理信息:" + bean.getProcessorEcho());
}
}
BeanPostProcessor
ApplicationContextAwareProcessor.java 源碼分析
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
// 判斷bean 是否爲感知類,同時獲取 訪問控制上下文
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(() -> {
this.invokeAwareInterfaces(bean);
return null;
}, acc);
} else {
this.invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
自定義 BeanPostProcessor 類
MyBeanBean.java
@Component
public class MyBeanBean{
}
MyBeanPostProcessor.java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor,PriorityOrdered {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyBeanBean) {
System.out.println("MyBeanBean,對象" + beanName + "調用初始化方法之前的數據: " + bean.toString());
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyBeanBean) {
System.out.println("MyBeanBean,對象" + beanName + "調用初始化方法之後的數據:" + bean.toString());
}
return bean;
}
@Override
public int getOrder() {
return 10;
}
}
執行相同的啓動方式:
Spring Boot 的例子
EnvironmentPostProcessor
SpringApplicationJsonEnvironmentPostProcessor.java 源碼分析
public class SpringApplicationJsonEnvironmentPostProcessor
implements EnvironmentPostProcessor, Ordered {
public static final String SPRING_APPLICATION_JSON_PROPERTY = "spring.application.json";
public static final String SPRING_APPLICATION_JSON_ENVIRONMENT_VARIABLE = "SPRING_APPLICATION_JSON";
private static final String SERVLET_ENVIRONMENT_CLASS = "org.springframework.web."
+ "context.support.StandardServletEnvironment";
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
private int order = DEFAULT_ORDER;
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull)
.findFirst().ifPresent((v) -> processJson(environment, v));
}
private void processJson(ConfigurableEnvironment environment,
JsonPropertyValue propertyValue) {
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(propertyValue.getJson());
if (!map.isEmpty()) {
addJsonPropertySource(environment,
new JsonPropertySource(propertyValue, flatten(map)));
}
}
...
}
實現EnvironmentPostProcessor接口。
使用它來讀取幾個環境變量:
calculation_mode=GROSS
gross_calculation_tax_rate=0.15
使用後處理器以特定於應用程序的方式來公開這些內容,在這種情況下,使用自定義前綴:
com.baeldung.environmentpostprocessor.calculation.mode=GROSS
com.baeldung.environmentpostprocessor.gross.calculation.tax.rate=0.15
然後,將新屬性添加到Environment中:
@Order(Ordered.LOWEST_PRECEDENCE)
public class PriceCalculationEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(PriceCalculationEnvironmentPostProcessor.class);
// 測試用例環境變量名稱前綴
private static final String PREFIX = "com.baeldung.environmentpostprocessor.";
// 計算模式後綴
private static final String CALCUATION_MODE = "calculation_mode";
// 計算比率後綴
private static final String GROSS_CALCULATION_TAX_RATE = "gross_calculation_tax_rate";
// 計算模式默認值
private static final String CALCUATION_MODE_DEFAULT_VALUE = "NET";
// 計算比率默認值
private static final double GROSS_CALCULATION_TAX_RATE_DEFAULT_VALUE = 0;
// 後綴集合
List<String> names = Arrays.asList(CALCUATION_MODE, GROSS_CALCULATION_TAX_RATE);
// 默認值Map
private static Map<String, Object> defaults = new LinkedHashMap<>();
static {
defaults.put(CALCUATION_MODE, CALCUATION_MODE_DEFAULT_VALUE);
defaults.put(GROSS_CALCULATION_TAX_RATE, GROSS_CALCULATION_TAX_RATE_DEFAULT_VALUE);
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 獲取系統中的配置信息,相當於 system.getProperty()
// 這裏調用的是 org.springframework.core.env.StandardEnvironment
// 中的 SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = “systemEnvironment”
PropertySource<?> system = environment.getPropertySources()
.get(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
Map<String, Object> prefixed = new LinkedHashMap<>();
if (!hasOurPriceProperties(system)) {
// 內部代碼
logger.warn("System environment variables [calculation_mode,gross_calculation_tax_rate] not detected, fallback to default value [calcuation_mode={},gross_calcuation_tax_rate={}]", CALCUATION_MODE_DEFAULT_VALUE,
GROSS_CALCULATION_TAX_RATE_DEFAULT_VALUE);
prefixed = names.stream()
.collect(Collectors.toMap(this::rename, this::getDefaultValue));
// 將獲取到的自定義配置信息放到環境變量中
environment.getPropertySources()
.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource("prefixer", prefixed));
return;
}
prefixed = names.stream()
.collect(Collectors.toMap(this::rename, system::getProperty));
environment.getPropertySources()
.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource("prefixer", prefixed));
}
要在 Spring Boot 引導過程中調用實現,需要在 META-INF/spring.factories 中註冊該類:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.baeldung.environmentpostprocessor.PriceCalculationEnvironmentPostProcessor
綜上所述,EnvironmentPostProcessor 實現可以從不同位置加載各種格式的任意文件。此外,可以進行所需的任何轉換,以使屬性在環境中隨時可用,以備後用。
結論
通過本文內容,認識到了 Processor 只是一個處理稱呼,可以手動實現任何對象的處理邏輯,在 Spring 框架中有兩個核心的 Processor 類,當然還有其他作用的其他 Processor,介紹了這兩個 Processor 具體做了哪些處理幫助 Spring 框架更好工作,通過 Spring 框架如何實現,以及如何進行自定義實現來理解 Spring 的設計理念和原理。介紹了 Spring Boot框架中約定大於配置的設計理念核心處理類 EnvironmentPostProcessor 的作用是用來擴展自定義的系統參數來擴展應用。希望通過本文內容可以幫助開發人員更好的理解 Spring 的設計思路。
參考資料
Spring的後置處理器到底是怎麼回事?
Spring後處理器
Spring的BeanFactoryPostProcessor和BeanPostProcessor
spring–BeanPostProcesstor
A Post-Processor for Spring Boot(Spring Boot的後處理器)
Spring BeanPostProcessor Example(pring BeanPostProcessor示例)
EnvironmentPostProcessor in Spring Boot(Spring Boot中的EnvironmentPostProcessor)