配置(这个埋点的事务到底怎么弄,一直都没有配成功):
<!-- execution(* com.sf.pmp.http.controller.AgntSignUpController.*(..))" --> <!-- 第一个*表示任意返回值;表示这个类;第二个*表示这个类下面的任意方法; (..)表示任意类型和参数--> <!-- 只要切点满足上面的条件,就会触发切面aspect(类),以及那个方法method(配置在通知那里) --> <bean id="agntLogHandler" class="com.sf.pmp.agnt.aop.AgntLogHandler"/> <!-- (1)你想进行切面的是哪个类呢 --> <aop:config proxy-target-class="true"> <!-- <aop:advisor advice-ref="txAdvice" pointcut="(execution(* com.sf.pmp.*.service.*.*(..)) or execution(* com.sf.pmp.*.controller..*.*(..)))" /> --> <!-- <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.sf.pmp.*.*.*.*(..))" /> --> <!-- <aop:advisor advice-ref="txAdvice" pointcut-ref="agntLogPointcut" /> --> <!-- aspect:切面是通知和切点的集合,通知和切点共同定义了切面的全部功能——它是什么,在何时何处完成其功能。 --> <aop:aspect ref="agntLogHandler"> <!-- ref:指向bean标签中的id,因为id代表了哪个类了嘛 --> <aop:pointcut id="agntLogPointcut" expression="execution(* com.sf.pmp.*.*.*.*(..)) and @annotation(agntLog)"/> <!-- 切入点,哪里切入,哪里触发执行 --> <aop:around pointcut-ref="agntLogPointcut" method="doAudit"/> </aop:aspect> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="check*" read-only="true"/> <tx:method name="is*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="*" isolation="DEFAULT" rollback-for="java.lang.Exception"/> </tx:attributes> </tx:advice>
应用:
@RequestMapping("list") @Action(description="查看佣金基数配置表分页列表") @AgntLog(moudel="代理招投标管理-基础配置",operateMenu="佣金基数配置",description="查询") // 在这里哦 public ModelAndView list(HttpServletRequest request,HttpServletResponse response) throws Exception { List<AgntCommissionBase> list=agntCommissionBaseService.getAll(new QueryFilter(request,"agntCommissionBaseItem")); ModelAndView mv=this.getAutoView().addObject("agntCommissionBaseList",list); return mv; }
切面以及切点:
public class AgntLogHandler { private Log logger = LogFactory.getLog(AgntLogHandler.class); @Resource private AgntLogSwitchService agntLogSwitchService; private static AgntWorkQueue wq = new AgntWorkQueue(10); /** * 执行日志系统记录 * @param point 横切点 * @param action 注解 * @throws Throwable */ public Object doAudit(ProceedingJoinPoint point,AgntLog agntLog) throws Throwable { // 入口就是在这里哦 //如果方法上没有注解@Action,返回 if (agntLog == null) { return point.proceed(); } //执行对象是否有异常 Throwable throwable = null; //执行结果 String result = "SUCCESS"; //执行对象返回值 Object returnVal = null; String optErrorMsg = null; try { returnVal = point.proceed(); } catch (Throwable t) { throwable = t; optErrorMsg = ExceptionUtils.getStackTrace(throwable); result = "ECXEPTION"; } //日志记录 doLog(point,agntLog, result,returnVal,optErrorMsg); //如果执行对象有异常,则抛出原有异常 if(null != throwable) { throw throwable; } return returnVal; } /** * @param point 横切点 * @param annotation 注解 * @param async 是否异步 * @param rtnValue 返回值 */ private void doLog(ProceedingJoinPoint point, AgntLog annotation, String result, Object rtnValue,String optErrorMsg){ //类、类Action Class<?> targetClass = point.getTarget().getClass(); // (java.lang.Class<T>) class com.sf.pmp.agnt.controller.conf.AgntCommissionBaseController try { //归属模块(菜单) String modelType = annotation.operateMenu(); // 佣金基数配置(跟注释那里是一样的,注意留意注释) //模块日志开关(关闭则返回) if(!isOwnerModelLogOpen(modelType)){ return; } //执行的方法 // point.getTarget().getClass().getName():(java.lang.String) com.sf.pmp.agnt.controller.conf.AgntCommissionBaseController // point.getSignature().getName() : (java.lang.String) list // exeMethod:com.sf.pmp.agnt.controller.conf.AgntCommissionBaseController.list( StringBuffer exeMethod = new StringBuffer(targetClass.getName()).append(".").append(point.getSignature().getName()).append("("); Object[] methodArgs = point.getArgs(); if(methodArgs != null && methodArgs.length > 0) { for(Object obj : methodArgs) { exeMethod.append(obj == null?"null":obj.getClass().getName()).append(","); } exeMethod.delete(exeMethod.length()-1, exeMethod.length()); } exeMethod.append(")"); // 日志信息封装 AgntOptLog agntOptLog = new AgntOptLog(); agntOptLog.setOptId(UniqueIdUtil.genId()); agntOptLog.setOptModule(annotation.moudel());//归属模块 agntOptLog.setOptType(annotation.operateMenu());;//操作(类型)菜单 agntOptLog.setOptSource(annotation.optSource()); agntOptLog.setOptTime(new Date());//操作时间 agntOptLog.setStatus(result);//操作结果 agntOptLog.setOptErrorMsg(optErrorMsg); agntOptLog.setOptDesc(annotation.description());//描述 SysUser curUser = ContextUtil.getCurrentUser(); if (curUser != null) { agntOptLog.setOptAccount(curUser.getAccount()); agntOptLog.setOptName(curUser.getFullname()); agntOptLog.setOrgid(ContextUtil.getCurrentOrgId());//操作用户归属组织ID } HttpServletRequest request = RequestUtil.getHttpServletRequest(); if (request != null) { String fromIp=RequestUtil.getIpAddr(request); agntOptLog.setOptFromIp(fromIp); agntOptLog.setRequestUrl(request.getRequestURI()); } AgntLogHolder logHolder = new AgntLogHolder(); logHolder.setAgntOptLog(agntOptLog); doLogAsync(logHolder); } catch (Exception ex) { logger.error(ex.getMessage(), ex); } finally { SysAuditThreadLocalHolder.clearDetail(); // 看不懂,暂时先不管 SysAuditThreadLocalHolder.clearParameters();// 看不懂,暂时先不管 SysAuditThreadLocalHolder.clearResult();// 看不懂,暂时先不管 SysAuditThreadLocalHolder.clearShouldLog();// 看不懂,暂时先不管 } } private void doLogAsync(AgntLogHolder holder){ AgntLogExecutor logExecutor = new AgntLogExecutor(); logExecutor.setLogHolders(holder); wq.execute(logExecutor); // 队列的执行方法 } /** * 判断模块的日志开关是否打开 * @param ownermodel 模块 * @return */ synchronized private boolean isOwnerModelLogOpen(String ownermodel){ int status = 0; List<Map<String, Object>> agntLogSwitchs = agntLogSwitchService.getAllCache(); for(Map<String,Object> map : agntLogSwitchs){ if(map.containsKey(ownermodel)){ status = (Integer) map.get(ownermodel); } } return status == 1; } }
执行记录日志的任务作业
class AgntLogExecutor implements Runnable{ private Log logger = LogFactory.getLog(AgntLogExecutor.class); private AgntLogHolder agntLogHolder; private AgntOptLogDao agntOptLogDao; public void setLogHolders(AgntLogHolder agntLogHolder) { this.agntLogHolder = agntLogHolder; this.agntOptLogDao = (AgntOptLogDao) AppUtil.getBean(AgntOptLogDao.class); } private void doLog() throws TemplateException, IOException{ AgntOptLog agntOptLog = agntLogHolder.getAgntOptLog(); String uriString = agntOptLog.getRequestUrl(); uriString=uriString.toUpperCase(); agntOptLogDao.insertAgntOptLog(agntOptLog);; } @Override public void run() { try { doLog(); } catch (Exception e) { logger.error(e.getMessage(), e); } } }
作业队列
public class AgntWorkQueue{ private final int nThreads; private final PoolWorker[] threads; LinkedList<Runnable> queue; public AgntWorkQueue(int nThreads){ this.nThreads=nThreads; queue = new LinkedList<Runnable>(); threads = new PoolWorker[nThreads]; for(int i=0;i<this.nThreads;i++){ threads[i] = new PoolWorker(); threads[i].start(); } } public void execute(Runnable r){ synchronized (queue) { queue.addLast(r); queue.notify(); } } private class PoolWorker extends Thread{ private Log logger = LogFactory.getLog(PoolWorker.class); public void run(){ Runnable r; while(true){ synchronized (queue) { while(queue.isEmpty()){ try { queue.wait(); } catch (InterruptedException e) { logger.error(e.getMessage(), e); } } r=(Runnable)queue.removeFirst(); } try{ r.run(); }catch (Exception e) { logger.error(e.getMessage(), e); } } } } }
自定义日志注解:
@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface AgntLog { /** * 操作菜单 * @return */ public String operateMenu() default ""; /** * 方法描述 * @return */ public String description() default "no description"; /** * 归属模块 * @return */ public String moudel() default "" ; /** * 日志类型 * @return */ public String exectype() default "操作日志"; /** * 详细信息 * @return */ public String detail() default ""; /** * 操作来源 * @return */ public String optSource() default "PC"; }
END;