场景:通过http调用第三方接口进行日志记录,第三方接口可能返回成功、可能返回失败(指调用成功,接口响应体为失败),也可能超时,甚至当不是期望的url时我们不进行日志记录。
1.依赖
<!-- aop 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- http依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- java 工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
2.Htttutils编写
@Component
public class HttpUtils {
// HTTP 前缀
private static final String HTTP_PREFIX = "http://";
public WebClient getWebClient(String ip, int port) {
return WebClient.builder()
.baseUrl(HTTP_PREFIX + ip + StrUtil.COLON + port)
.defaultHeaders(heads -> {
heads.setExpires(1000);
heads.setContentType(MediaType.APPLICATION_JSON_UTF8);
}).build();
}
public <T> T get(String ip, int port, String uri, Class<T> clazz) {
WebClient webClient = getWebClient(ip, port);
return webClient.get().uri(uri).retrieve().bodyToMono(clazz)
.doOnError(ex -> {
})
.doOnSuccess(s -> {
})
.block();
}
}
httpUtils上加@Component是将该实例放入Spring容器,为了给AOP拦截该实例的方法
3.AOP编写
@Component
@Aspect
public class LogAspect {
// @Pointcut("")
// public void logPoint() {
// }
private static final Map<Integer,String> statusMap = new HashMap<>();
private static final List<String> urlList = new ArrayList<>();
static {
statusMap.put(200,"ok");
statusMap.put(500,"error");
urlList.add("/lundy/logs/communicationLogs/findCommunicationLog?id=");
urlList.add("/lundy/logs/communicationLogs/retryCommunication?id=");
}
@Around("execution(* com.suzhouheart.utils.HttpUtils.get(..))")
public Object aroundAop(ProceedingJoinPoint pj) {
LinkedHashMap proceed = null;
String str = "no_todo";
Object[] args = pj.getArgs();
try {
proceed = (LinkedHashMap)pj.proceed();
//httpUtils.get()的第三个参数为url
if (urlList.contains(args[2])){
str = statusMap.getOrDefault(proceed.get("status"),"error");
}
} catch (Throwable e) {
str = "error";
}
LogHandle logHandle = UrlHandleFactory.getInvokeStrategy(str);
logHandle.handle(args);
return proceed;
}
}
urlList中存放需要进行日志记录的url,当url不在这个集合里时,不进行日志记录。statusMap为状态转换map,根据http的响应状态,进行不同的处理。
4.策略模式编写
4.1编写策略接口
public interface LogHandle extends InitializingBean {
void handle(Object[] args);
}
接口继承InitializingBean,其实现类要重写afterPropertiesSet()方法,该方法在加载这个bean的时候会调用,通过该方法可以将每个策略实现注册到工厂中。
4.2编写策略实现
@Component
public class SuccessHandle implements LogHandle{
@Override
public void handle(Object[] args) {
System.out.println("成功");
for (Object arg : args) {
System.out.println(arg);
}
}
@Override
public void afterPropertiesSet() throws Exception {
UrlHandleFactory.register("ok", this);
}
}
@Component
public class ErrorHandle implements LogHandle{
@Override
public void handle(Object[] args) {
System.out.println("失败");
for (Object arg : args) {
System.out.println(arg);
}
}
@Override
public void afterPropertiesSet() throws Exception {
UrlHandleFactory.register("error", this);
}
}
@Component
public class NoToDoHandle implements LogHandle{
@Override
public void handle(Object[] args) {
System.out.println("不记录日志");
}
@Override
public void afterPropertiesSet() throws Exception {
UrlHandleFactory.register("no_todo",this);
}
}
5.编写工厂模式
public class UrlHandleFactory {
private static Map<String ,LogHandle> strategyMap = new HashMap<>();
public static LogHandle getInvokeStrategy(String name){
return strategyMap.get(name);
}
public static void register(String name, LogHandle logHandle){
if (StringUtils.isEmpty(name) || null == logHandle){
return;
}
strategyMap.put(name, logHandle);
}
}