java 使用spring aop/mybatis記錄操作日誌到數據庫

  1. maven依賴
    需要spring aop, 由於要用到數據庫,使用mybatis-plus生成crud代碼,將mybatis-plus依賴也加上
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>		
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.0.6</version>
		</dependency>
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.28</version>
			<scope>test</scope>
		</dependency>		
  1. 數據庫建表語句
DROP TABLE IF EXISTS `operation_log`;
CREATE TABLE `operation_log` (
  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '操作日誌id',
  `username` VARCHAR(20) DEFAULT NULL COMMENT '操作人',
  `module` VARCHAR(30) DEFAULT NULL COMMENT '執行模塊',
  `methods` VARCHAR(50) DEFAULT NULL COMMENT '執行方法',
  `content` VARCHAR(255) DEFAULT NULL COMMENT '操作內容',
  `actionurl` VARCHAR(50) DEFAULT NULL COMMENT '請求路徑',
  `ip` VARCHAR(50) DEFAULT NULL COMMENT 'IP地址',
  `date` DATETIME DEFAULT NULL COMMENT '操作時間',
  `commite` TINYINT(2) DEFAULT NULL COMMENT '執行描述(1:執行成功、2:執行失敗)',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
  1. 使用mybatis-plus創建crud
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 演示例子,執行 main 方法控制檯輸入模塊表名回車自動生成對應項目目錄中
public class DevCodeGenerator {

    /**
     * <p>
     * 讀取控制檯內容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("請輸入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }

    public static void main(String[] args) {
        String module = scanner("模塊名");
        String table = scanner("表名");

        generate(module, table);
    }

    private static void generate(String module, String table) {
        // 代碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("360os");
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);

        // 數據源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(module);

        pc.setParent("com.test");
        mpg.setPackageInfo(pc);

        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出文件名
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        // templateConfig.setEntity();
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
        strategy.setEntityLombokModel(false);
        strategy.setRestControllerStyle(true);
//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        strategy.setInclude(table);
//        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
//        strategy.setTablePrefix("ope");
//        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}
  1. spring 切面


package com.test.config;

import com.google.gson.Gson;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@Aspect
@Component
public class LogAopAction {
    // 注入service,用來將日誌信息保存在數據庫
    @Autowired
    private IOperationLogService logService;

    private Gson gson = new Gson();

    // 配置接入點,即爲所要記錄的action操作目錄
    @Pointcut("@annotation(com.test.config.OptionalLog)")
    private void controllerAspect() {
    }

    @Around("controllerAspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //日誌實體對象
        OperationLog logBo = new OperationLog();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        // 從session獲取用戶名
        String username = (String) request.getSession().getAttribute("user");
        logBo.setUsername(username);
        // 獲取系統當前時間
        logBo.setDate(new Date());

        // 獲取訪問真實IP    
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根據網卡取本機配置的IP  
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //對於通過多個代理的情況,第一個IP爲客戶端真實IP,多個IP按照','分割  
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }

        logBo.setIp(ipAddress);

        // 攔截的實體類,就是當前正在執行的controller
        Object target = pjp.getTarget();
        // 攔截的方法名稱。當前正在執行的方法
        String methodName = pjp.getSignature().getName();
        // 攔截的方法參數
        Object[] args = pjp.getArgs();

        //獲取請求路徑
        String actionUrl = request.getRequestURI();

        // 攔截的放參數類型
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("該註解只能用於方法");
        }
        msig = (MethodSignature) sig;
        Class[] parameterTypes = msig.getMethod().getParameterTypes();

        Object object = null;
        // 獲得被攔截的方法
        Method method = null;


        try {
            method = target.getClass().getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e1) {
            e1.printStackTrace();
        } catch (SecurityException e1) {
            e1.printStackTrace();
        }
        if (null != method) {
            // 獲取方法(此爲自定義註解) 
            OptionalLog op = method.getAnnotation(OptionalLog.class);

            // 獲取註解的modules 設爲操作模塊
            logBo.setModule(op.module());
            // 獲取註解的methods 設爲執行方法
            logBo.setMethods(op.methods());
            // 將上面獲取到的請求路徑 設爲請求路徑
            logBo.setActionurl(actionUrl);
            try {
                object = pjp.proceed();
                //接受客戶端的數據
                Map<String, String[]> map = request.getParameterMap();
                // 解決獲取參數亂碼
                Map<String, String[]> newmap = new HashMap<String, String[]>();
                for (Map.Entry<String, String[]> entry : map.entrySet()) {
                    String name = entry.getKey();
                    String[] values = entry.getValue();

                    if (values == null) {
                        newmap.put(name, new String[]{});
                        continue;
                    }
                    String[] newvalues = new String[values.length];
                    for (int i = 0; i < values.length; i++) {
                        String value = values[i];
                        value = new String(value.getBytes("iso8859-1"), request.getCharacterEncoding());
                        newvalues[i] = value; //解決亂碼後封裝到Map中
                    }

                    newmap.put(name, newvalues);

                }

                logBo.setContent(gson.toJson(newmap));
                //1爲執行成功
                logBo.setCommite(1);
                // 添加到數據庫
                logService.save(logBo);
            } catch (Throwable e) {

                //接受客戶端的數據
                Map<String, String[]> map = request.getParameterMap();
                // 解決獲取參數亂碼
                Map<String, String[]> newmap = new HashMap<String, String[]>();
                for (Map.Entry<String, String[]> entry : map.entrySet()) {
                    String name = entry.getKey();
                    String[] values = entry.getValue();

                    if (values == null) {
                        newmap.put(name, new String[]{});
                        continue;
                    }
                    String[] newvalues = new String[values.length];
                    for (int i = 0; i < values.length; i++) {
                        String value = values[i];
                        value = new String(value.getBytes("iso8859-1"), request.getCharacterEncoding());
                        newvalues[i] = value; //解決亂碼後封裝到Map中
                    }

                    newmap.put(name, newvalues);

                }
                //MapperUtil.toJsonStr爲自定義的轉換工具類
                logBo.setContent(gson.toJson(newmap));
                //2爲執行失敗
                logBo.setCommite(2);
                //添加到數據庫
                logService.save(logBo);
            }
        }
        return object;
    }

}
  1. 操作日誌註解
import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented 
public @interface OptionalLog {
    String module()  default "";  
    String methods()  default ""; 
}
  1. 用法示例
    @OptionalLog(module = "數據庫模塊",methods = "查詢列表數據")
    @PostMapping("/selectList")
    public BaseRsp selectList(@RequestParam("sql") String sql){}
  1. 日誌效果
    日誌效果
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章