YMNNALFT:Spring *Utils 類別

工程 | Josh Long | 2021 年 1 月 27 日 | ...

歡迎來到您可能不需要另一個程式庫 (YMNNALFT) 的另一個專欄!自 2016 年以來,我花了很多時間在 我的 Spring Tips 影片中,闡明(或試圖闡明!)Spring 生態系統中一些更龐大的機會。然而,今天,我以不同的精神來到您面前,希望專注於那些做很棒事情的小而隱藏的瑰寶,它們可能會讓您免於額外的第三方依賴及其隱含的複雜性。

我們都經歷過。您想要一些日常的字串操作例程,因此您將其提取到一個單獨的抽象類別中,並將其公開為 static 方法。然後,有一個用於構建 java.util.Collection<T> 的工廠方法,因此您將其提取到一個單獨的類別中,並將其公開為 static 方法。最終,您會在程式碼庫中散布這些東西的集合,並且它們之間幾乎沒有凝聚力。畢竟,沒有太多內容,對吧?這些本質上只是全域函數,而不是有狀態物件上的方法本身

並不是說建立自己的靜態方法本質上是錯誤的,只要您以某些約定來處理它們即可。例如,我們通常使用帶有靜態方法的抽象類別。

一切都始於如此無害。當然,確實如此。您知道這個週期。首先,一些方法位於公司範圍內 artifact 儲存庫的 jar 中的一個孤立類別中。然後有複數形式的類別。現在套件已經進入畫面。套件很麻煩!您將開始意識到最好對組織進行一些思考,否則事情會很快失控。您開始接受 pull request。在某個時候,會發生重大變更,開發人員會帶著乾草叉在您門外排隊。您想知道為什麼每個人都一直吃東西並在工作中劃破您的輪胎。太過分了!因此,您讓公司對您新生的模組進行開源。現在是世界的問題了!就像電子雞和真人實境節目一樣,沒有盡頭。而那個廣闊的世界?嗯,那個廣闊的世界喜歡在點發佈版本中進行重大變更,並且他們會因為這些變更而愛你

也許吧。

還有另一種方法。

市面上有許多品質不一的第三方實用程式庫:GS Collections、Apache Commons、Guava 等。這裡不乏選擇。您是否知道 Spring 在框架本身中提供了幾個實用類別,可能會讓您免於依賴?我並不是說它們會做您可能從傑出競爭對手那裡獲得的一切,但您可能會感到驚訝!此範例將研究其中的一些實用類別,但在類別路徑上還有許多其他類別。您通常可以透過進入 IDE 並在類別搜尋中搜尋 *Utils 或在檔案搜尋中搜尋 *Utils.java 來找到它們。讓我們看看其中的一些。

  • 這些中的許多預設都包含在每個 Spring Boot 專案中,透過對 Spring Initializr 的以下傳遞性(或預設)依賴 - org.springframework.boot : spring-boot-starter

這是程式碼

package bootiful.utils;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jmx.support.JmxUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.*;

import javax.annotation.PostConstruct;
import java.beans.PropertyDescriptor;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;

@Log4j2
@SpringBootApplication
public class BootifulApplication {

	@Data
	@AllArgsConstructor
	@Component
	public static class DemoClass {

		@PostConstruct
		public void begin() {
			log.info("begin()");
		}

		private final List<Map<String, Object>> list = new ArrayList<>();

	}

	@Bean
	ApplicationListener<ApplicationReadyEvent> ready(DemoClass demo) {
		return event -> {

			Assert.notNull(demo.getList(), "the list can't be null");

			beansUtils(demo);
			classUtils();
			systemPropertyUtils();
			fileCopyUtils();
			aop(demo);
			reflection();
			ensure();
			collections();
			serialize();

		};
	}

	private void ensure() {
		int counter = 2;
		Assert.state(counter == 2, () -> "the counter should be 2 or more. Was " + counter);
		Assert.hasText("Hello, world!", () -> "this string should be a non-null, non-empty String");
	}

	private void reflection() {

		ReflectionUtils.doWithFields(DemoClass.class, field -> log.info("field = " + field.toString()));
		ReflectionUtils.doWithMethods(DemoClass.class, method -> log.info("method = " + method.toString()));

		Field list = ReflectionUtils.findField(DemoClass.class, "list");
		log.info(Objects.requireNonNull(list).toString());

		ResolvableType rt = ResolvableType.forField(list);
		log.info(rt.toString());
	}

	private void aop(DemoClass demoClass) {
		Class<?> targetClass = AopUtils.getTargetClass(demoClass);
		log.info("Class<?> is " + targetClass);
		log.info("is AOP proxy? " + AopUtils.isAopProxy(demoClass));
		log.info("is CGlib proxy? " + AopUtils.isCglibProxy(demoClass));
	}

	private void collections() {
		Collection<String> names = Arrays.asList("Tammie", "Kimly", "Josh");
		boolean contains = CollectionUtils.containsAny(names, Arrays.asList("Josh"));
		Assert.state(contains, () -> "one or more of the names in " + names.toString() + " should be present");
	}

	private void serialize() {
		Customer in = new Customer(593232329, "Josh");
		byte[] bytes = SerializationUtils.serialize(in);
		Customer out = (Customer) SerializationUtils.deserialize(bytes);
		Assert.state(out.getId() == in.getId() && out.getName().equals(in.getName()),
				() -> "the " + Customer.class.getName() + " did not serialize correctlyy");
	}

	private void fileCopyUtils() {
		Resource cpr = new ClassPathResource("/application.properties");
		try (Reader r = new InputStreamReader(cpr.getInputStream())) {
			String contents = FileCopyUtils.copyToString(r);
			log.info("application.properties contents: " + contents);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void systemPropertyUtils() {
		String resolvedText = SystemPropertyUtils.resolvePlaceholders("my home directory is ${user.home}");
		log.info("resolved text: " + resolvedText);
	}

	private void classUtils() {
		Constructor<DemoClass> demoClassConstructor = ClassUtils.getConstructorIfAvailable(DemoClass.class);
		log.info("demoClassConstructor: " + demoClassConstructor);
		try {
			DemoClass demoClass = demoClassConstructor.newInstance();
			log.info("newInstance'd demoClass: " + demoClass);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void beansUtils(DemoClass demo) {
		PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(demo.getClass());
		for (PropertyDescriptor pd : descriptors) {
			log.info("pd: " + pd.getName());
		}
	}

	public static void main(String[] args) {
		SpringApplication.run(BootifulApplication.class, args);
	}

}

@Data
class Customer implements Serializable {

	static final long serialVersionUID = 1L;

	private int id;

	private String name;

	public Customer(int id, String name) {
		this.id = id;
		this.name = name;
	}

}

此範例介紹了 Spring 生態系統中各種 Utils 類別實作的自助餐。它著眼於

  • BeanUtils - 用於處理 JavaBeans 的有用函數
  • ClassUtils - 用於以反射方式詢問類型問題的有用函數
  • SystemPropertyUtils - 用於處理 System 屬性的有用函數
  • FileCopyUtils - 用於複製 InputStreamOutputStream 實作的有用函數
  • AopUtils - 用於處理 Spring 的 AOP 代理的有用函數
  • ReflectionUtils - 用於廣泛處理反射的有用函數
  • Assert - 有助於設計合約樣式斷言的有用函數
  • CollectionUtils - 用於處理各種 Java java.util.Collection 類型的有用函數
  • SerializeUtils - 用於處理 Java 序列化的有用函數

有很多令人興奮的東西,這個密集的範例甚至沒有開始觸及表面!

您喜歡這種一目了然的方法嗎?您學到什麼了嗎?一如既往,我很想收到您的來信,所以請在 Twitter 上發聲 (@starbuxman) !我將會帶著另一個YMNNALFT專欄回來,所以一定要錯過!

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部