Spring Processor 是什麼?

本文內容如有錯誤、不足之處,歡迎技術愛好者們一同探討,在本文下面討論區留言,感謝。

簡述

Processor 中文翻譯:處理器、加工機,這裏 Processor 只是一種實現類描述其功能具有處理能力。

Spring 中,有兩個核心 Processor 接口:BeanPostProcessorBeanFactoryPostProcessor ;
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 沒有什麼原理,主要原理就是這個翻譯上:處理機、加工機。
下面介紹一下 SpringSpring Boot 中的核心 Processor 類的原理,幫助理解 Spring 的設計思路。

Spring 中的 Processor

Spring 設計時考慮的設計模式的開閉原則,因此提供了大量的擴展接口,其中就有 BeanPostProcessorBeanFactoryPostProcessor

作用:

  • BeanPostProcessor : 對容器中的 Bean 進行後處理,增強 Bean 的功能
  • BeanFactoryPostProcessor :對 Spring 容器本身進行後處理,增強容器的功能

區別:

  1. BeanPostProcessor 執行順序在 BeanFactoryPostProcessor 之後
  2. BeanPostProcessorSpring 容器加載 Bean 的定義文件並且實例化 Bean 之後,在容器執行 InitializingBean 之前執行的。
  3. BeanFactoryPostProcessorSpring 容器加載 Bean 的定義文件之後,在執行 Bean 實例化之前執行的。

BeanFactoryPostProcessor 流程圖如下:

Spring 容器加載 Bean 的定義文件
執行 BeanFactoryPostProcessor
執行 Bean 實例化

BeanPostProcessor 流程圖如下

Spring IOC 容器 實例化
調用 BeanPostProcessor#postProcessBeforeInitialization
InitializingBean 初始化
執行調用 BeanPostProcessor#postProcessAfterInitialization

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環境

注意

  1. 實現 EnvironmentPostProcessor 接口的同時需要實現 Ordered 排序接口,也可以實現 PriorityOrdered 接口。
  2. 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

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