spring AOP实现业务日志管理

咱们知道,在一个系统中,业务操做日志对咱们很重要。那么以往咱们更多的时候是写个方法,而后哪一个模块须要加入日志,就引入这个接口。可是这种其实冗余了不少代码。今天我简单给你们介绍下采用spring AOP来统一帮咱们处理业务操做日志。java

首先看下个人日志表的设计:web

DROP TABLE IF EXISTS `t_log`;
CREATE TABLE `t_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL COMMENT '用户名',
  `create_date` varchar(20) DEFAULT NULL COMMENT '发生日期',
  `module_name` varchar(2000) DEFAULT '' COMMENT '功能模块',
  `operation` varchar(255) DEFAULT NULL COMMENT '用户所作的操做',
  `work_time` int(11) DEFAULT NULL COMMENT '耗时',
  `oper_result` varchar(255) DEFAULT NULL COMMENT '操做结果',
  `user_ip` varchar(255) DEFAULT NULL COMMENT '用户ip',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

接下来,建立咱们的日志实体类:spring

 

public class Log implements Serializable {
    private Long id;//自增id
    private String userName;//用户名
    private String createdate;//日期
    private String moduleName;//模块内容
    private String operation;//操做(主要是"添加"、"修改"、"删除")
    private Long workTime;//耗时(s单位)
    private String operResult;//操做结果
    private String userIp;//用户ip

//此处省略getter,setter

日志dao:sql

@Repository("logDao")
public class LogDaoImpl extends BaseDao implements LogDao {
    @Override
    public Log insert(final Log log) {
        final String sql = "insert into t_log(username,create_date,module_name,operation,work_time,oper_result,user_ip) values(?,?,?,?,?,?,?)";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        super.getJdbcTemplate().update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement ps = connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
                ps.setObject(1, log.getUserName());
                ps.setObject(2, log.getCreatedate());
                ps.setObject(3, log.getModuleName());
                ps.setObject(4, log.getOperation());
                ps.setObject(5, log.getWorkTime());
                ps.setObject(6, log.getOperResult());
                ps.setObject(7, log.getUserIp());
                return ps;
            }
        }, keyHolder);
        log.setId(keyHolder.getKey().longValue());
        return log;
    }
}

日志service:编程

@Transactional(propagation = Propagation.REQUIRED)
@Service("logService")
public class LogServiceImpl implements LogService {
    @Autowired
    private LogDao logDao;

    @Override
    public Log insert(final Log log) {
        return logDao.insert(log);
    }
}

接下来,开始咱们的切面编程,首先建立aop包,并在包里面建立LogAspect切面类,因为这个只说Aop实现日志记录,并不会给你们讲解具体的aop知识,若是对aop不懂的同窗请自行查阅资料学习。session

我这里采用的通知类型为环绕通知。下面直接上代码:mvc

@Component
@Aspect
public class LogAspect {
    @Autowired
    private LogService logService;//日志记录Service


    /**
     * 方法切入点
     */
    @Pointcut("execution(* com.xwtec.manager.web.controller.*.*(..))")
    public void pointerCutMethod() {
    }

    /**
     * 环绕通知
     *
     * @param joinPoint
     * @param annotation
     * @return
     */
    @Around(value = "pointerCutMethod() && @annotation(annotation)")
    public Object doAround(ProceedingJoinPoint joinPoint, OperAnnotation annotation) {
        Log log = new Log();
        //经过注解获取当前属于那个模块
        log.setModuleName(annotation.moduleName());
        //经过注解获取当前的操做方式
        log.setOperation(annotation.option());
        log.setCreatedate(DateUtils.getCurdateStr("yyyy-MM-dd HH:mm:ss"));
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        Long beginTime = System.currentTimeMillis();
        if (ra != null) {
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = sra.getRequest();
            String ip = request.getRemoteHost();
            log.setUserIp(ip);
            // 从session中获取用户信息
            User loginUser = (User) request.getSession().getAttribute(ConstantParam.USER_TOKEN);
            if (loginUser != null) {
                log.setUserName(loginUser.getUserName());
            } else {
                if ("doLogin".equals(joinPoint.getSignature().getName())) {
                    log.setUserName(joinPoint.getArgs()[0].toString());
                }
            }
        }
        try {
            Object object = joinPoint.proceed();
            if (object != null) {
                if (object instanceof Map) {
                   if(MapUtils.getBoolean((Map)object, "status")){
                       log.setOperResult("成功");
                   }else {
                       log.setOperResult(MapUtils.getString((Map) object, "msg"));
                   }

                }
            }
            log.setWorkTime((System.currentTimeMillis() - beginTime) / 1000);
            logService.insert(log);
            return object;
        } catch (Throwable e) {
            log.setWorkTime((System.currentTimeMillis() - beginTime) / 1000);
            log.setOperResult("失败:" + e.getMessage());
            logService.insert(log);
            return null;
        }
    }
}

定义完切面后,咱们要在springmvc的配置文件中,指定代理app

<!-- 加入Aspectj配置 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

注意:该配置必须写在springmvc的配置文件,因为咱们如今的方法切入点是在controller层,若是你定义在spring的配置文件里面,不会起做用。这牵扯到父子容器的问题。spring默认的主配置文件为父容器,springmvc为子容器,子容器可使用父容器里面的配置信息,可是父容器却没法使用子容器的配置信息。ide

因为,日志记录的过程当中,咱们要记录该操做属于那个模块,而且当前的操做类型是什么,我这里直接采用注解来定义。学习

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OperAnnotation {
    //模块名
    String moduleName();

    //操做内容
    String option();
}

这样,当咱们在须要添加日志的controller方法上面只用加上简单的注解功能,在aop切面拦截的时候,就能够经过注解拿到属于那个模块,那个操做。

例如个人登陆controller:

@OperAnnotation(moduleName = "登陆系统",option = "登陆")
    @ResponseBody
    @RequestMapping(value = "doLogin", method = RequestMethod.POST)
    public Map doLogin(@RequestParam("username") String username, @RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response) {}
相关文章
相关标签/搜索