使用Spring Rest Tempalte, https://spring.io/guides/gs/consuming-rest/前端
RestTemplate restTemplate = new RestTemplate();
爲了使得獲取和注入token機制與業務功能代碼解耦,須要在resttemplate注入自定義interceptor https://www.tutorialspoint.com/spring_boot/spring_boot_interceptor.htmjava
interceptor file:spring
public class DemoInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution){ //get token here. String tokenValue = String.format("%s %s", "Authorization", "XXXXXXXXXXX"); request.getHeaders().add("Authorization", tokenValue); } }
resttemplate add interceptor後端
DemoInterceptor ris = new DemoInterceptor(); restTemplate.setInterceptors(Arrays.asList({ris});
把cert文件生成jks證書, 生成方式參考:api
openssl pkcs12 -export -in server.crt -inkey server.key -out server.pkcs12 keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server-keystore.jks -srcstoretype pkcs12
新建ssl request builder緩存
public class SslRequestFactoryBuilder { private static Logger logger = LoggerFactory.getLogger(SslRequestFactoryBuilder.class); public ClientHttpRequestFactory build(SslOption sslOption) { HttpClientBuilder httpClientBuilder = HttpClients.custom(); if (sslOption != null && sslOption.getEnable() != null && sslOption.getEnable()) { logger.info("ssl connection is being enabled"); SSLContext sslContext = getSslContext(sslOption); httpClientBuilder.setSSLContext(sslContext); } else { logger.info("ssl connection not active, use http instead"); } CloseableHttpClient client = httpClientBuilder.build(); ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client); ClientHttpRequestFactory bufferRequestFactory = new BufferingClientHttpRequestFactory(requestFactory); return bufferRequestFactory; } private SSLContext getSslContext(SslOption sslOption) { SSLContext sslContext; try { sslContext = SSLContextBuilder .create() .loadKeyMaterial(ResourceUtils.getFile(sslOption.getKeyStore()), sslOption.getKeyStorePassword().toCharArray(), sslOption.getKeyPass().toCharArray()) .loadTrustMaterial(ResourceUtils.getFile(sslOption.getTrustStore()), sslOption.getTrustStorePassword().toCharArray()) .build(); } catch (Exception e) { logger.error("ssl restTemplate initialize failed!"); throw new RuntimeException("ssl restTemplate initialize failed!", e); } return sslContext; } }
resttemplate add interceptorspringboot
ClientHttpRequestFactory requestFactory = builder.buildPool(sslOption)
restTemplate.setRequestFactory(requestFactory);
添加spring retry依賴 https://www.baeldung.com/spring-retry, enable retrying架構
@Retryable( value = {RetryException.class}, maxAttemptsExpression = "2", backoff = @Backoff(5000)) public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { ClientHttpResponse response = execution.execute(request, body); // if error, throw exception throw new RetryException("retry"); return response; }
添加 Dozer 轉換框架, http://dozer.sourceforge.net/app
compile('net.sf.dozer:dozer:5.4.0')
框架
定義class A 和 B, A爲數據DTO, B爲領域模型
class A{ @mapping("at1") private String attr1; @mapping("at2") private String attr2; private String attr3; ... } class B{ private String at1; private String at2; ... }
源數據轉爲目標數據
Mapper mapper = new DozerBeanMapper(); B b = mapper.map(a, B.class)
採用 Spring Cache + ehcache方案,https://www.baeldung.com/spring-boot-ehcache
添加項目依賴,spring boot enable cache
compile('javax.cache:cache-api') compile('org.ehcache:ehcache:3.6.1') compile('org.springframework.boot:spring-boot-starter-cache')
新增cache配置
@Configuration public class ProfileCacheConfig implements JCacheManagerCustomizer { private static Logger logger = LoggerFactory.getLogger(ProfileCacheConfig.class); @Autowired private ProfileProperties profileProperties; @Override public void customize(CacheManager cacheManager) { CacheProperty cacheProperty = profileProperties.getCache(); cacheManager.createCache("CACHE_NAME", new MutableConfiguration<>() .setExpiryPolicyFactory(ModifiedExpiryPolicy.factoryOf(new Duration(TimeUnit.MINUTES, cacheProperty.getProfileExpireMinutes()))) .setStoreByValue(false)); } }
在方法上面添加cacheable註解,啓用緩存
@Cacheable(value = "CACHE_NAME", key = "#param") public void method(string param){ ****** }
採用MDC方案,將生成的x-trace-Id注入到request,response的header裏面
攔截每一個前端發送給每一個後端的請求, 繼承HandlerInterceptorAdapter,生成x-trace-Id, 注入到 MDC context裏面
public class XTraceInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String xTraceId = generateTraceId(); MDC.put("x-trace-Id", xTraceId); response.setHeader("x-trace-Id", xTraceId); return true; } private String generateTraceId() { String randomId = UUID.randomUUID().toString(); String xTraceId = String.format("%s-%s", "demo", randomId); return xTraceId; } }
拓展WebMvcConfigurationSupport,將XTraceInterceptor注入
public class AppConfig extends WebMvcConfigurationSupport { @Override protected void addInterceptors(InterceptorRegistry registry) { XTraceInterceptor xtrace = new XTraceInterceptor(); registry.addInterceptor(loggerInterceptor); super.addInterceptors(registry); } }
restTemplate調用服務的時候,新建interceptor,在取出來注入到request header當中
public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { String xTraceId = MDC.get(LoggerAttributeKeys.X_TRACE_ID); if (xTraceId != null) { request.getHeaders().add(LoggerAttributeKeys.X_TRACE_ID, xTraceId); } ClientHttpResponse response = execution.execute(request, body); return response; } }