這篇文章帶給大家的內容是關於SpringBoot的實用技巧分享,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
前言
最近分享的一些原始碼、框架設計的東西。我發現大家熱情不是特別高,想想大多數應該還是正兒八經寫代碼的居多;這次就分享一點接地氣的: SpringBoot 使用中的一些小技巧。
不算是多高大上的東西,但都還蠻有用。
屏蔽外部依賴
第一個是屏蔽外部依賴,什麼意思呢?
例如大家日常開發時候有沒有這樣的煩惱:
專案是基於 SpringCloud 或是 dubbo 這樣的分散式服務,你需要依賴許多基礎服務。
比如說某個訂單號碼的產生、取得使用者資訊等。由於服務拆分,這些功能都是在其他應用中以介面的形式提供,單測還好我還可以利用 Mock 把它屏蔽掉。
但如果自己想把應用程式啟動起來同時把自己相關的程式碼跑一遍呢?
通常有幾種做法:
本機把所有的服務都啟動起來。
把註冊中心換成開發環境,依賴開發環境的服務。
直接把程式碼推送到開發環境自測。
看起來三種都可以,以前我也是這樣幹的。但還是有幾個小問題:
本地啟動有可能服務很多,全部起來電腦能不能撐住還兩說,萬一服務有問題就進行不下去了。
依賴開發環境的前提是網路打通,還有一個問題就是開發環境程式碼很不穩定很大可能會影響你的測試。
推送到開發環境應該是比較可靠的方案,但如果想除錯只有日誌大法,沒有本地 debug 的效率高效。
那要如何解決問題呢?既可以在本地調試也不用啟動其他服務。
其實也可以利用單測的做法,把其他外部依賴 Mock
掉就行了。
大致的流程分為以下幾步:
SpringBoot 啟動之後在Spring 中找出你需要屏蔽的那個API 的bean(通常情況下這個介面都是交給Spring 管理的)。
手動從 bean 容器中刪除該 bean。
重新建立一個該 API 的對象,只不過是透過 Mock 出來的。
再手動註冊進 bean 容器中。
以下面這段程式碼為例:
@Override public BaseResponse<ordernoresvo> getUserByHystrix(@RequestBody UserReqVO userReqVO) { OrderNoReqVO vo = new OrderNoReqVO(); vo.setAppId(123L); vo.setReqNo(userReqVO.getReqNo()); BaseResponse<ordernoresvo> orderNo = orderServiceClient.getOrderNo(vo); return orderNo; }</ordernoresvo></ordernoresvo>這是一個 SpringCloud 應用程式。
它依賴 orderServiceClient 取得一個訂單號碼。
其中的 orderServiceClient 就是一個外部 API,也是被 Spring 所管理。
取代原有的 Bean
下一步就是替換原有的 Bean。
@Component public class OrderMockServiceConfig implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(OrderMockServiceConfig.class); @Autowired private ApplicationContext applicationContext; @Value("${excute.env}") private String env; @Override public void run(String... strings) throws Exception { // 非本地环境不做处理 if ("dev".equals(env) || "test".equals(env) || "pro".equals(env)) { return; } DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); OrderServiceClient orderServiceClient = defaultListableBeanFactory.getBean(OrderServiceClient.class); logger.info("======orderServiceClient {}=====", orderServiceClient.getClass()); defaultListableBeanFactory.removeBeanDefinition(OrderServiceClient.class.getCanonicalName()); OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class, invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success")); defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi); logger.info("======mockOrderApi {}=====", mockOrderApi.getClass()); } }
其中實作了 CommandLineRunner 接口,可以在 Spring 容器初始化完成之後呼叫 run() 方法。
程式碼非常簡單,簡單來說先判斷下是什麼環境,畢竟除開本地環境其餘的都是需要真正呼叫遠端服務的。
之後就是要取得 bean 然後手動刪除掉。
關鍵的步驟:
OrderServiceClient mockOrderApi = PowerMockito.mock(OrderServiceClient.class, invocationOnMock -> BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success")); defaultListableBeanFactory.registerSingleton(OrderServiceClient.class.getCanonicalName(), mockOrderApi);
建立了一個新的 OrderServiceClient 物件並手動註冊進了 Spring 容器中。
第一段程式碼使用的是 PowerMockito.mock 的 API,他可以建立一個代理對象,讓所有呼叫 OrderServiceClient 的方法都會做預設的回傳。
BaseResponse.createSuccess(DateUtil.getLongTime() + "", "mock orderNo success"))
測試一下,當我們沒有替換時呼叫剛才那個介面且本機也沒有啟動OrderService
:
##因為沒有配置fallback 所以會報錯,表示找不到這個服務。 替換掉 bean 時:
再次要求沒有報錯,並且獲得了我們預設的回傳。
透過日誌也會發現
OrderServiceClient 最後已經被
Mock 代理程式了,並不會去調用真正的方法。
配置加密
下一個則是配置加密,這應該算是一個基本功能。 例如我們設定檔中的一些帳號和密碼,都應該是密文保存的。 因此這次使用了一個開源元件來實現加密與解密,並且對SpringBoot 非常友好只需要幾段程式碼即可完成。
首先根据加密密码将需要加密的配置加密为密文。
替换原本明文保存的配置。
再使用时进行解密。
使用该包也只需要引入一个依赖即可:
<dependency> <groupid>com.github.ulisesbocchio</groupid> <artifactid>jasypt-spring-boot-starter</artifactid> <version>1.14</version> </dependency>
同时写一个单测根据密码生成密文,密码也可保存在配置文件中:
jasypt.encryptor.password=123456
接着在单测中生成密文。
@Autowired private StringEncryptor encryptor; @Test public void getPass() { String name = encryptor.encrypt("userName"); String password = encryptor.encrypt("password"); System.out.println(name + "----------------"); System.out.println(password + "----------------"); }
之后只需要使用密文就行。
由于我这里是对数据库用户名和密码加密,所以还得有一个解密的过程。
利用 Spring Bean
的一个增强接口即可实现:
@Component public class DataSourceProcess implements BeanPostProcessor { @Autowired private StringEncryptor encryptor; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof DataSourceProperties){ DataSourceProperties dataSourceProperties = (DataSourceProperties) bean; dataSourceProperties.setUsername(encryptor.decrypt(dataSourceProperties.getUsername())) ; dataSourceProperties.setPassword(encryptor.decrypt(dataSourceProperties.getPassword())); return dataSourceProperties ; } return bean; } }
这样就可以在真正使用时还原为明文。
同时也可以在启动命令中配置刚才的密码:
java -Djasypt.encryptor.password=password -jar target/jasypt-spring-boot-demo-0.0.1-SNAPSHOT.jar
以上是SpringBoot的實用技巧分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!