zoukankan      html  css  js  c++  java
  • SpringAop注解实现日志的存储

    一.介绍

    1.AOP的作用

      在OOP中,正是这种分散在各处且与对象核心功能无关的代码(横切代码)的存在,使得模块复用难度增加。AOP则将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

    2.DI 和 IOC 概念

      依赖注入或控制反转的定义中,调用者不负责被调用者的实例创建工作,该工作由Spring框架中的容器来负责,它通过开发者的配置来判断实例类型,创建后再注入调用者。由于Spring容器负责被调用者实例,实例创建后又负责将该实例注入调用者,因此称为依赖注入。而被调用者的实例创建工作不再由调用者来创建而是由Spring来创建,控制权由应用代码转移到了外部容器,控制权发生了反转,因此称为控制反转。

    3.BeanFactory与ApplicationContext

      ApplicationContext是BeanFactory的子接口,也被称为应用上下文。BeanFactory提供了Spring的配置框架和基本功能,ApplicationContext则添加了更多企业级功能(如国际化的支持),他另一重要优势在于当ApplicationContext容器初始化完成后,容器中所有的 singleton Bean 也都被实例化了,也就是说当你需要使用singleton Bean 是,在应用中无需等待就可以用,而其他BeanFactory接口的实现类,则会延迟到调用 getBean()方法时构造,ApplicationContext的初始化时间会稍长些,调用getBean()是由于Bean已经构造完毕,速度会更快。因此大部分系统都使用ApplicationContext,而只在资源较少的情况下,才考虑使用BeanFactory。

    4.AOP的实现策略

    (1)Java SE动态代理:
        使用动态代理可以为一个或多个接口在运行期动态生成实现对象,生成的对象中实现接口的方法时可以添加增强代码,从而实现AOP。缺点是只能针对接口进行代理,另外由于动态代理是通过反射实现的,有时可能要考虑反射调用的开销。
    (2)字节码生成(CGLib 动态代理)
        动态字节码生成技术是指在运行时动态生成指定类的一个子类对象,并覆盖其中特定方法,覆盖方法时可以添加增强代码,从而实现AOP。其常用工具是cglib。
    (3)定制的类加载器
        当需要对类的所有对象都添加增强,动态代理和字节码生成本质上都需要动态构造代理对象,即最终被增强的对象是由AOP框架生成,不是开发者new出来的。解决的办法就是实现自定义的类加载器,在一个类被加载时对其进行增强。JBoss就是采用这种方式实现AOP功能。
    (4)代码生成
        利用工具在已有代码基础上生成新的代码,其中可以添加任何横切代码来实现AOP。
    (5)语言扩展
        可以对构造方法和属性的赋值操作进行增强,AspectJ是采用这种方式实现AOP的一个常见Java语言扩展。

    二.使用

    1.mysql数据库日志

    最后一列为异常原因

    2.数据库表

    DROP TABLE IF EXISTS `sys_log`;
    CREATE TABLE `sys_log` (
      `id` int(255) NOT NULL AUTO_INCREMENT,
      `user_id` int(255) DEFAULT NULL COMMENT '操作人id',
      `user_name` varchar(255) DEFAULT NULL COMMENT '操作人name',
      `peration` varchar(255) DEFAULT NULL COMMENT '操作内容',
      `method` varchar(255) DEFAULT NULL COMMENT '请求路径',
      `params` varchar(255) DEFAULT NULL COMMENT '请求参数',
      `ip` varchar(255) DEFAULT NULL COMMENT '请求人ip',
      `create_date` datetime DEFAULT NULL COMMENT '请求时间',
      `operate_result` varchar(255) DEFAULT NULL COMMENT '请求结果',
      `abnormity` text COMMENT '异常原因',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8;

    3.Maven文件

    <!-- Spring Aop 架包 -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>1.9.1</version>
            </dependency>
    
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
    
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib-nodep</artifactId>
                <version>2.1_3</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/fr.opensagres/org.apache.struts2.views.xdocreport -->
            <dependency>
                <groupId>fr.opensagres</groupId>
                <artifactId>org.apache.struts2.views.xdocreport</artifactId>
                <version>0.9.1</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-core</artifactId>
                <version>5.1.2.RELEASE</version>
            </dependency>

    4.日志实现类

    import java.util.Date;
    
    public class SysLog {
        private Integer id;
    
        private String registerName;
    
        private Integer userId;
    
        private String userName;
    
        private String peration;
    
        private String method;
    
        private String params;
    
        private String ip;
    
        private Date createDate;
    
        private String operateResult;
    
        private String abnormity;
    get和set    
    }

    日志Mapper和Service就不贴了,Mapper逆向工程生成的 Service就一个insert方法

    5.自定义注解

    package com.mz.monotoring.Util.Log;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 自定义日志注解
     *
     * @author sjl
     * @date 2019-04-09 11:03
     */
    @Target(ElementType.METHOD) // 方法注解
    @Retention(RetentionPolicy.RUNTIME) // 运行时可见
    public @interface LogAnno {
        String operateType();// 记录操作功能
    }

    6.切面工具类

    package com.mz.monotoring.Util.Log;
    
    import com.mz.monotoring.Domain.SysLog;
    import com.mz.monotoring.Model.BaseReturnModel;
    import com.mz.monotoring.Service.SysLogService;
    import com.mz.monotoring.Util.Logs.Utils.IpAdrressUtil;
    import com.mz.monotoring.Util.Logs.Utils.JacksonUtil;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    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.util.Date;
    import java.util.Map;
    
    
    /**
     * 切面工具类
     *
     * @author sjl
     * @date 2019-04-09 11:07
     */
    @Component
    @Aspect
    public class LogAopAspect {
    
        /**
         * 日志Service
         */
        @Autowired
        private SysLogService sysLogService;
    
        BaseReturnModel model = new BaseReturnModel();
    
        /**
         * 环绕通知记录日志通过注解匹配到需要增加日志功能的方法
         *
         * @param
         * @return
         * @throws Throwable
         */
        @Around("@annotation(com.mz.monotoring.Util.Log.LogAnno)")
        public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
            //保存日志
            SysLog sysLog = new SysLog();
            //从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入点所在的方法
            Method method = signature.getMethod();
    
    
            //获取操作
            LogAnno operation = method.getAnnotation(LogAnno.class);
            if (operation != null) {
                String value = operation.operateType();
                //保存获取的操作
                sysLog.setPeration(value);
            }
    
            //获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
    
            //获取请求的方法名
            String methodName = method.getName();
            sysLog.setMethod(className + "." + methodName);
    
            //请求的参数
            Object[] args = joinPoint.getArgs();
            //获取请求参数中携带的用户id或username存入日志库中
            String s = JacksonUtil.obj2json(args);
            String[] split = s.split("\[");
            String[] split1 = split[1].split("\]");
            Map<String, Object> map = JacksonUtil.json2map(split1[0]);
            if (map.get("id") != null) {
                sysLog.setUserId(Integer.parseInt(map.get("id").toString()));
            } else if (map.get("registerName") != null) {
                sysLog.setUserName(map.get("registerName").toString());
            }
            //将参数所在的数组转换成json
            String params = null;
            try {
                params = JacksonUtil.obj2json(args);
            } catch (Exception e) {
                e.printStackTrace();
            }
            sysLog.setParams(params);
    
            //请求的时间
            sysLog.setCreateDate(new Date());
    
    //         获取用户名
    //         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    //         if (!(authentication instanceof AnonymousAuthenticationToken)) {
    //         sysLog.setUsername(authentication.getName());
    //         }
    
            Object proceed = null;
            try {
                proceed = joinPoint.proceed();
                sysLog.setOperateResult("请求正常");
            } catch (Throwable e) {
                e.printStackTrace();
                model.setCode(500);
                model.setMess("请求失败");
                sysLog.setOperateResult("请求失败");
                sysLog.setAbnormity(e.toString());
            }
    
            //获取用户ip地址
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                    .getRequest();
            sysLog.setIp(IpAdrressUtil.getIpAdrress(request));
    
    
            //调用service保存SysLog实体类到数据库
            sysLogService.insertSelective(sysLog);
    
            //proceed不为空时被注解的方法运行正常 else抛异常
            if (proceed == null) {
                return model;
            } else {
                return proceed;
            }
        }
    }

    7.获取ip的工具类

    package com.mz.monotoring.Util.Logs.Utils;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 获取用户真实的ip地址
     *
     * @author sjl
     * @date 2019-04-09 16:09
     */
    public class IpAdrressUtil {
        public static String getIpAdrress(HttpServletRequest request) {
            String ip = null;
    
            //X-Forwarded-For:Squid 服务代理
            String ipAddresses = request.getHeader("X-Forwarded-For");
            String unknown = "unknown";
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //Proxy-Client-IP:apache 服务代理
                ipAddresses = request.getHeader("Proxy-Client-IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //WL-Proxy-Client-IP:weblogic 服务代理
                ipAddresses = request.getHeader("WL-Proxy-Client-IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //HTTP_CLIENT_IP:有些代理服务器
                ipAddresses = request.getHeader("HTTP_CLIENT_IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //X-Real-IP:nginx服务代理
                ipAddresses = request.getHeader("X-Real-IP");
            }
    
            //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
            if (ipAddresses != null && ipAddresses.length() != 0) {
                ip = ipAddresses.split(",")[0];
            }
    
            //还是不能获取到,最后再通过request.getRemoteAddr();获取
            if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                ip = request.getRemoteAddr();
            }
            return ip;
        }
    }

    8.数组转Json   Json转JavaBean   Json转Map工具类

    package com.mz.monotoring.Util.Logs.Utils;
    
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.util.Map;
    
    /**
     * @author sjl
     * @date 2019-04-09 16:09
     */
    public class JacksonUtil {
        private final static ObjectMapper objectMapper = new ObjectMapper();
    
        private JacksonUtil() {
    
        }
    
        public static ObjectMapper getInstance() {
            return objectMapper;
        }
    
        /**
         * javaBean、列表数组转换为json字符串
         */
        public static String obj2json(Object obj) throws Exception {
            return objectMapper.writeValueAsString(obj);
        }
    
        /**
         * json 转JavaBean
         */
    
        public static <T> T json2pojo(String jsonString, Class<T> clazz) throws Exception {
            objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            return objectMapper.readValue(jsonString, clazz);
        }
    
        /**
         * json字符串转换为map
         */
        public static <T> Map<String, Object> json2map(String jsonString) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            return mapper.readValue(jsonString, Map.class);
        }
    }

    9.方法上使用  只需在方法添加自定义好的注解就好了

    package com.mz.monotoring.Service.Impl;
    
    import com.mz.monotoring.Dao.UserBeanMapper;
    import com.mz.monotoring.Domain.UserBean;
    import com.mz.monotoring.Model.BaseReturnModel;
    import com.mz.monotoring.Service.UserBeanService;
    import com.mz.monotoring.Util.Log.LogAnno;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * @author sjl
     * @date 2019-04-09 11:45
     */
    @Service
    public class UserBenaServiceImpl implements UserBeanService {
    
        @Autowired
        private UserBeanMapper userBeanMapper;
    
        BaseReturnModel model = new BaseReturnModel();
    
        @LogAnno(operateType = "添加一条用户信息")
        @Override
        public BaseReturnModel insertSelective(UserBean record)throws Exception {
            int i = userBeanMapper.insertSelective(record);
    
            if (i > 0) {
                model.setMess("成功");
                model.setCode(200);
            } else {
                model.setCode(500);
                model.setMess("失败");
            }
            return model;
        }
    }

     10 在spring中加入配置文件

    <context:component-scan base-package="com.qd.util.Log"/>
        <!-- 激活自动代理功能 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    
        <!-- 用户服务对象 -->
        <bean id="SysLogService" class="com.qd.service.SysLogService" abstract="true"/>

    这个在spring2.0的时候是需要的, 不然进不去切面类    我用4.0的是不需要这些的

  • 相关阅读:
    记录@DeleteMapping注解的使用
    记录一次linux设置frp开机自启动功能实现
    mongo对用户简单操作
    Jedis分布式锁实际应用笔记
    如何在R语言中建立六边形矩阵热图heatmap可视化
    R语言复杂网络分析:聚类(社区检测)和可视化
    R语言泊松Poisson回归模型预测人口死亡率和期望寿命
    R语言混合正态分布极大似然估计和EM算法
    R语言使用自组织映射神经网络(SOM)进行客户细分
    R语言时间序列TAR阈值自回归模型
  • 原文地址:https://www.cnblogs.com/shijl/p/10685755.html
Copyright © 2011-2022 走看看