spring拦截方法的配置和实现

最近项目要求,对于某个某些方法执行异步任务,就是说,对于那些接口(调用时间长,没必要立刻得到接口执行结果的接口)执行异步调用,这样主线程就会执行的时间就很短了,我的实现呢是用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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页