最近分享的一些源碼、框架設計的東西。我發現你們熱情不是特別高,想一想大多數應該仍是正兒八經寫代碼的居多;此次就分享一點接地氣的: SpringBoot
使用中的一些小技巧。java
算不上多高大上的東西,但都還挺有用。git
第一個是屏蔽外部依賴
,什麼意思呢?github
好比你們平常開發時候有沒有這樣的煩惱:spring
項目是基於 SpringCloud
或者是 dubbo
這樣的分佈式服務,你須要依賴許多基礎服務。數據庫
好比說某個訂單號的生成、獲取用戶信息等。springboot
因爲服務拆分,這些功能都是在其餘應用中以接口的形式提供,單測還好我還能夠利用 Mock
把它屏蔽掉。bash
但若是本身想把應用啓動起來同時把本身相關的代碼跑一遍呢?網絡
一般有幾種作法:app
看起來三種均可以,之前我也是這麼幹的。但仍是有幾個小問題:框架
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;
}
複製代碼
這是一個 SpringCloud 應用。
它依賴於 orderServiceClient
獲取一個訂單號。
其中的 orderServiceClient
就是一個外部 API,也是被 Spring 所管理。
下一步就是替換原有的 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
的更多使用技巧歡迎留言討論。
上文的一些實例代碼能夠在這裏找到:
歡迎關注公衆號一塊兒交流: