最近项目要求,对于某个某些方法执行异步任务,就是说,对于那些接口(调用时间长,没必要立刻得到接口执行结果的接口)执行异步调用,这样主线程就会执行的时间就很短了,我的实现呢是用spring环绕通知拦截所有的方法(这些方法先用spring aop进行切片)、
配置如下:
<!-- 方法拦截器 -->
<!-- <bean id="methodInterceptor" class="com.paic.icore.aops.common.biz.util.MethodInterceptor"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.paic.icore.aops.*.service.impl.*.*(..))" id="servicePointCut"/>
<aop:aspect id="loggeraspect" ref="methodInterceptor">
<aop:around method="logger" pointcut-ref="servicePointCut"/>
</aop:aspect>
</aop:config> -->
相应的拦截器的代码为:
package com.paic.icore.aops.common.biz.util;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import com.paic.icore.aops.common.jedis.message.MsgRedisPoolUtils;
public class MethodInterceptor {
@Autowired
private AopsLogger logger;
@Autowired
private MsgRedisPoolUtils msgRedis;
public Object logger(ProceedingJoinPoint pjp) throws Throwable {
Object obj = null;
MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();
Method method = joinPointObject.getMethod();
boolean flag = method.isAnnotationPresent(AsynchTask.class) ;
if(flag){
//这里要判断是否是异步线程调用,还是程序调用
String flagValue = method.getName()+JSONArray.fromObject(pjp.getArgs()).toString();
String isDo = msgRedis.get(flagValue);
if(null==isDo || "".equals(isDo))//表示是程序调用
{
// 获取目标对象类名
String clazzName = pjp.getTarget().getClass().getName();
// 获取方法名
String methodName = pjp.getSignature().getName();
logger.trace("================拦截到" + clazzName + "的" + methodName + "方法");
// 获取执行方法的参数
Object[] args = pjp.getArgs();
StringBuffer params =new StringBuffer();//参数的数组
Object[] paramsType = method.getParameterTypes();
for(int i=0;i<paramsType.length;i++)
{
String type = (paramsType[i].toString()).substring((paramsType[i].toString()).lastIndexOf(".")+1);
params.append(type+"|"+args[i]);
logger.trace(i+"=================params.length()="+params.length());
if(i!=(paramsType.length-1)){
params.append("#");
}
System.out.println("***********" + args[i]);
logger.trace("====方法"+methodName+"的参数为:"+args[i]);
}
long taskId = System.currentTimeMillis();
msgRedis.saveStrToListJedis("asynchTask", String.valueOf(taskId));
Map<String,String> map =new HashMap<String, String>();
AsynchTask methodInterceptor = method.getAnnotation(AsynchTask.class);
String interfaceName = methodInterceptor.value();
map.put("className", clazzName);
map.put("methodName", methodName);
map.put("params", params.toString());
map.put("interfaceName", interfaceName);//这里把那个方法所在的接口别名拿到
msgRedis.saveHashMapToJedis(String.valueOf(taskId), map, -1);
obj = true;
}
else if(null!=isDo)//表示是异步任务调用
{
obj = pjp.proceed();//放方法过去
}
}
else//没有加那个拦截方法的注解的
{
obj = pjp.proceed();//放方法过去
}
return obj;
}
}
把执行的方法放进缓存系统后,再合适的时机反射调用即可(这里用得是spring的task任务机制,1秒一次滴)
package com.paic.icore.aops.task.apptask.action;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import net.sf.json.JSONArray;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.paic.icore.aops.common.biz.util.AopsLogger;
import com.paic.icore.aops.common.biz.util.DateUtil;
import com.paic.icore.aops.common.jedis.message.MsgRedisPoolUtils;
import com.paic.icore.aops.points.service.IncomePointService;
import com.paic.pafa.app.lwc.core.context.support.PafaApplicationContext;
import com.paic.pafa.biz.AppContext;
import com.paic.pafa.web.BaseRest;
//测试异步调用程序
@Component
public class AsynchTaskAction extends BaseRest {
@Autowired
private AopsLogger logger;
//注入线程池
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private MsgRedisPoolUtils msgRedis;
@Autowired
private IncomePointService incomePointService;
@Scheduled(cron = "0/1 * * * * ?")
public void doAsynchTask(){
int count =taskExecutor.getActiveCount();
int maxpollsize = taskExecutor.getMaxPoolSize();
count =taskExecutor.getActiveCount();
logger.tasklog("调用接口:doTest:当前活动线程数:count="+count);
logger.tasklog("线程池允许的最大线程数:maxpollsize="+maxpollsize);
if(count<maxpollsize)
{
taskExecutor.execute(new Runnable() {
@Override
public void run() {
String taskId ="";
synchronized (this) {
taskId=msgRedis.rpopFromList("asynchTask");
}
if(taskId!=null && !"".equals(taskId))
{
logger.tasklog("从处理序列asynchTask抛出的taskId="+taskId);
//新建一个线程ID
long threadId = Thread.currentThread().getId();
//保存当前线程的ID
msgRedis.saveStrToStringJedis(String.valueOf(threadId), String.valueOf(threadId), 0);
Map<String, String> paramsMap = msgRedis.getHashMapFromJedis(taskId, "doEstablishAccount");
if(paramsMap!=null){
String className = paramsMap.get("className");
String methodName = paramsMap.get("methodName");
String params = paramsMap.get("params");
String interfaceName = paramsMap.get("interfaceName");
logger.tasklog("====调用类"+className);
logger.tasklog("====调用方法"+methodName);
logger.tasklog("=======相关参数"+params);
logger.tasklog("=======接口别名字"+interfaceName);
try {
//得到对象
Class c = Class.forName(className);
Object[] methodPramas;
Class[] parameterTypes =new Class[]{};
if(null==params || "".equals(params))//执行的方法没有参数
{
methodPramas = new Object[]{};
}
else
{
String[] paramStr = params.split("#");
logger.tasklog("===========参数paramStr的长度="+paramStr.length);
parameterTypes = new Class[paramStr.length];
methodPramas = new Object[paramStr.length];
for(int i=0;i<paramStr.length;i++)
{
String[] str = paramStr[i].split("\\|");
String type = str[0];
String param = str[1];
logger.tasklog("===========参数param"+param);
logger.tasklog("===========参数type"+type);
if(type.equals("String"))
{
methodPramas[i]=param;
parameterTypes[i] = String.class;
}
else if(type.equals("int"))
{
methodPramas[i]=Integer.parseInt(param);
parameterTypes[i] = int.class;
}
else if(type.equals("Date"))
{
Date date = DateUtil.formatStringToDate(param, "yyyy-MM-dd HH:mm:ss");
methodPramas[i]=date;
parameterTypes[i] = Date.class;
}
else if(type.equals("Long"))
{
methodPramas[i]=Long.parseLong(param);
parameterTypes[i] = Long.class;
}
}
}
logger.tasklog("#################参数parameterTypes"+parameterTypes);
//获取到方法对象,假设方法的参数是一个int,method名为setAge
Method interfaceMethod = c.getMethod(methodName, parameterTypes);
//执行方法
logger.tasklog("#################开始反射调用方法)))))))");
PafaApplicationContext context = (PafaApplicationContext) AppContext.getInstance().getApplicationContext();
BeanFactory factory = (BeanFactory) context;
Object obj = factory.getBean(interfaceName);
Method method = getImplMehodByInterMehtod(obj, interfaceMethod);
//开始往redis里面增加识别标示
String flag = method.getName()+JSONArray.fromObject(methodPramas).toString();
String isDo = msgRedis.get(flag);
if(null==isDo || "".equals(isDo))//异步第一次执行
{
msgRedis.saveStrToStringJedis(flag, String.valueOf(1), 0);
}
else if(null!=isDo && (Integer.parseInt(isDo)<6))//异步1~5次执行
{
msgRedis.saveStrToStringJedis(flag, String.valueOf(Integer.parseInt(isDo)+1), 0);
}
else
{
logger.tasklog("此异步任务已经执行了5次,将不再 执行,taskId="+taskId);
msgRedis.deleteJedisByKey(taskId, "doTest");//这里删掉hash里面的值
return;
}
Object resultObj = method.invoke(obj, methodPramas);
boolean invokeResult = (Boolean) resultObj;
if(invokeResult)
{
//成功了
msgRedis.deleteJedisByKey(taskId, "doAsynchTask");//这里删掉hash里面的值
}
else
{
//失败了(下次再来一次,直到成功)
msgRedis.saveStrToListJedis("asynchTask", taskId);
}
logger.tasklog("#########resultObj="+resultObj);
logger.tasklog("#########invokeResult="+invokeResult);
} catch (Exception e) {
logger.error("======反射调用"+className+"类的"+methodName+"异常,参数为"+params+"e="+e);
}
}
}
else
{
logger.tasklog("###处理序列asynchTask没有内容,本次操作停止...");
return;
}
}
});
logger.tasklog("AsynchTaskAction --> doTest");
}
}
//通过接口方法名,拿到实现类的方法
private Method getImplMehodByInterMehtod(Object obj,Method interfaceMethod ) throws SecurityException, NoSuchMethodException{
return obj.getClass().getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes());
}
}
其中方法上用到了一个注解,就是自定义注解
注解定义如下:
package com.paic.icore.aops.common.biz.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsynchTask {
//接口的实现类的别名
String value();
}
这个注解必须加在service所对应的接口方发上,就是说,拦截那个方法把这个注解加上即可
经测试功能ok