SpringAOP&&定時任務簡單實現接口訪問入表和定時任務接口重試
-
Spring aop
-
Spring 定時任務
-
代理模式深化
1.表設計
2.Aop主要代碼
@Aspect
@Component
public class AopUtils implements Ordered {
//當前
private static final Logger logger = LoggerFactory.getLogger(AopUtils.class);
public static String AOPLOG_NO_RETREN_VALUE = "NONE RETURN";
@Value("${接口地址.url}")
private String url;
@Autowired
private RequestInfoDao requestInfoDao;
/**
* API 接口日志訪問記錄
* 環繞通知實現
* @param jp
* @return
*/
@Around("execution(* com.*.*.modules.*.service.CommonService.*(..))")
public Object logApiMethod(ProceedingJoinPoint jp) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
Object rvt = null;
RequestInfo requestInfo = new RequestInfo();
//配合后面的定時任務重試接口機制
if(sra == null) {
try {
rvt = jp.proceed(jp.getArgs());
String params = "";
List<Object> filterArgs = Lists.newArrayList();
try {
if(jp.getArgs()!=null) {
Arrays.stream(jp.getArgs()).forEach(arg -> {
if (!(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse)) {
filterArgs.add(arg);
}
});
}
params = (filterArgs.size()==0)? "":JsonMapper.toJsonString(filterArgs);
}catch (Exception e) {
params = filterArgs+"";
}
String returnValue = "";
try {
returnValue = rvt==null ? "": JsonMapper.toJsonString(rvt);
}catch (Exception e) {
returnValue = "返回值過長不顯示";
}
requestInfo.setParam(params);
requestInfo.setReturnValue(returnValue);
requestInfo.setUpdateDate(new Date());
User user = new User("接口重試");
requestInfo.setUpdateBy(user);
requestInfo.setResult(true);
requestInfo.setErrorResson(null);
requestInfoDao.updateByParams(info);
return rvt;
} catch (Throwable var6) {
requestInfo.setResult(false);
requestInfo.setErrorResson("線上接口訪問異常:"+var6.getMessage());
logger.error("線上接口訪問異常:", var6);
}
}
HttpServletRequest request = sra.getRequest();
requestInfo.setResult(true);
try {
rvt = jp.proceed(jp.getArgs());
} catch (Throwable var6) {
requestInfo.setResult(false);
requestInfo.setErrorResson("線上接口訪問異常:"+var6.getMessage());
logger.error("線上接口訪問異常:", var6);
}
String methodName = jp.getSignature().getName();
//參數寫成一個集合數組,接口重試的時候需要轉換
String params = "";
List<Object> filterArgs = Lists.newArrayList();
try {
if(jp.getArgs()!=null) {
Arrays.stream(jp.getArgs()).forEach(arg -> {
if (!(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse)) {
filterArgs.add(arg);
}
});
}
params = (filterArgs.size()==0)? "":JsonMapper.toJsonString(filterArgs);
}catch (Exception e) {
params = filterArgs+"";
}
String returnValue = "";
if(isShowReturnValue(methodName)) {
try {
returnValue = rvt==null ? "": JsonMapper.toJsonString(rvt);
}catch (Exception e) {
returnValue = "返回值過長不顯示";
}
}else {
returnValue = "不顯示返回值";
}
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String requestURI = httpServletRequest.getRequestURI();
String beanName = jp.getThis().getClass().getSimpleName();
int num = beanName.indexOf("$$");
beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring( 1, num);
requestInfo.setId(IdGen.uuid());
requestInfo.setBeanName(beanName);
requestInfo.setMethod(methodName);
requestInfo.setParam(params);
requestInfo.setRequestUrl(url+requestURI);
requestInfo.setReturnValue(returnValue);
requestInfo.setCreateDate(new Date());
requestInfo.setUpdateDate(new Date());
//接口是否可重復
Method aopMethod = ((MethodSignature)jp.getSignature()).getMethod();
Method m = jp.getTarget().getClass().getDeclaredMethod(methodName, aopMethod.getParameterTypes());
InterfaceRepeatFlag interfaceRepeatFlag = m.getAnnotation(InterfaceRepeatFlag.class);
if (interfaceRepeatFlag == null){
requestInfo.setRepeatFlag(false);
}else {
requestInfo.setRepeatFlag(interfaceRepeatFlag.value());
}
User user = new User();
user.setId("接口調用");
requestInfo.setCreateBy(user);
requestInfo.setUpdateBy(user);
if(requestInfoDao != null) {
requestInfoDao.insert(requestInfo);
}
return rvt;
}
private boolean isShowReturnValue(String methodName) {
List<String> debugRoles = Arrays.asList(AOPLOG_NO_RETREN_VALUE.split(","));
for(String debugRole : debugRoles) {
if(!StringUtils.isBlank(methodName)
&& methodName.indexOf(debugRole) == 0){
return false;
}
}
return true;
}
@Override
public int getOrder() {
return 1001;
}
}
3.定時任務配置
<bean name="schedulerFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- 定義數據源 ,數據源里面需要Quzrtz集群所需要的表 -->
<property name="dataSource">
<ref bean="dataSource" />
</property>
<!-- 指定spring容器的key,如果不設定在job中的jobmap中是獲取不到spring容器的 -->
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<!-- 指定spring的配置相關信息 -->
<property name="configLocation" value="classpath:quartz.properties" />
<!-- 指定觸發器,可以指定多個 -->
<property name="triggers">
<list>
<ref bean="interfaceJobTrigger"/>
</list>
</property>
</bean>
<!-- 定義任務 -->
<bean id="interfaceJob" class="com.*.*.modules.quzrtz.service.interfaceJob"/>
<bean id="interfaceJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<!-- 上面的任務的代理類(自己實現) -->
<value>com.*.*.modules.quzrtz.scheduler.InterfaceJobSchedule</value>
</property>
<property name="description">
<value>定時任務接口重試</value>
</property>
<property name="jobDataAsMap">
<map>
<!--實際的任務BeanName,填上EventMonitorService的BeanName -->
<entry key="targetObject" value="interfaceJob" />
<!-- 執行Bean中的哪個方法 -->
<entry key="targetMethod" value="repeatInterface" />
</map>
</property>
</bean>
<bean id="interfaceJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<!-- 任務代理Bean name -->
<ref bean="interfaceJobDetail" />
</property>
<property name="description">
<!-- 任務代理Bean name -->
<value>定時任務接口重試</value>
</property>
<property name="cronExpression">
<!-- 配置表達式,這里表示每1分鍾執行一次 -->
<value>0 0/1 * * * ?</value>
</property>
</bean>
4.定時任務
@Transactional(readOnly = false)
public void repeatInterface() {
RequestInfo requestInfo = new RequestInfo();
requestInfo.setRepeatFlag(true);
requestInfo.setResult(false);
List<RequestInfo> requestInfoList = requestInfoDao.findAllList(requestInfo);
for (RequestInfo requestInfo : requestInfoList){
String paramStr = requestInfo.getParam();
String beanStr = requestInfo.getBeanName();
String methodStr = requestInfo.getMethod();
try {
Object obj = SpringContextHolder.getBean(beanStr);
Method m = null;
Method[] methods = obj.getClass().getMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
if (method.getName().equals(methodStr)) {
m = method;
break;
}
}
}
Class paramCls = m.getParameterTypes()[0];
List<Object> params = JSONArray.parseArray(paramStr);
MethodUtils.invokeExactMethod(obj, methodStr, JSON.parseObject(JSONObject.toJSONString(params.get(0)), paramCls));
}catch (Exception e){
e.printStackTrace();
logger.error("接口重試:失敗,失敗原因:"+e.getMessage());
continue;
}
logger.info("接口重試成功:類名"+beanStr+";方法:"+methodStr+";參數:"+paramStr+";結束重試");
}
}
5.定時任務調度器-定時任務代理
public class InterfaceJobSchedule extends QuartzJobBean {
private Logger logger = LoggerFactory.getLogger(InterfaceJobSchedule.class);
private String targetObject;
private String targetMethod;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
Object otargetObject = SpringContextHolder.getBean(targetObject);
Method m = null;
Method[] methods = otargetObject.getClass().getMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
if (method.getName().equals(targetMethod)) {
m = method;
break;
}
}
m.invoke(otargetObject, new Object[]{});
}
} catch (Exception e) {
logger.error("InterfaceJobSchedule Exception:", e);
}
}
/**
* @param targetObject the targetObject to set
*/
public void setTargetObject(String targetObject) {
this.targetObject = targetObject;
}
/**
* @param targetMethod the targetMethod to set
*/
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
}
6.接口重試注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InterfaceRepeatFlag {
public boolean value() default true;
}
7.學習總結
Spring AOP核心是代理模式,可見代理模式的拓展性有多強,有業務的代碼才會有收獲,試一試,收獲頗多