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) {}
相關文章
相關標籤/搜索