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的是不需要这些的
SpringAop注解实现日志的存储的更多相关文章
- SpringAOP+注解实现简单的日志管理
今天在再次深入学习SpringAOP之后想着基于注解的AOP实现日志功能,在面试过程中我们也经常会被问到:假如项目已经上线,如何增加一套日志功能?我们会说使用AOP,AOP也符合开闭原则:对代码的修改 ...
- 【Spring】每个程序员都使用Spring(四)——Aop+自定义注解做日志拦截
一.前言 上一篇博客向大家介绍了Aop的概念,对切面=切点+通知 .连接点.织入.目标对象.代理(jdk动态代理和CGLIB代理)有所了解了.理论很强,实用就在这篇博客介绍. 这篇博客中,小编向大家介 ...
- Spring2.5那些事之基于AOP的方法级注解式日志配置
在日常开发中经常需要在代码中加入一些记录用户操作日志的log语句,比如谁在什么时间做了什么操作,等等. 把这些对于开发人员开说无关痛痒的代码写死在业务方法中实在不是一件很舒服的事情,于是AOP应运而生 ...
- SpringAop切面实现日志记录
SpringAop切面实现日志记录代码实现:https://www.cnblogs.com/wenjunwei/p/9639909.html 问题记录 1.signature.getMethod(). ...
- 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存
示例代码下载 : http://download.csdn.net/detail/han1202012/8638801; 一. 崩溃日志本地存储 1. 保存原理解析 崩溃信息本地保存步骤 : -- 1 ...
- 大数据学习——有两个海量日志文件存储在hdfs
有两个海量日志文件存储在hdfs上, 其中登陆日志格式:user,ip,time,oper(枚举值:1为上线,2为下线):访问之日格式为:ip,time,url,假设登陆日志中上下线信息完整,切同一上 ...
- 运用Spring Aop,一个注解实现日志记录
运用Spring Aop,一个注解实现日志记录 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到 ...
- springAOP注解方式实现日志操作
通过自定义注解调用方法执行日志存储: package com.zktx.platform.log2; import java.lang.reflect.Method; import java.util ...
- 利用Spring AOP自定义注解解决日志和签名校验
转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...
随机推荐
- mina statemachine解读(一)
statemachine(状态机)在维护多状态数据时有非常好的作用,现在github上star排名最前的是squirrel-foundation以及spring-statemachine,而min ...
- mysql数据库 表 导入导出
1.导出表结构 mysqldump --no-data -h192.168.222.11 -uroot -proot --databases db01 db02 db30>file.sql 2. ...
- nodejs的package.json依赖dependencies中 ^ 和 ~ 的区别
nodejs的package.json定义了一个模块,包括其依赖关系的一个简单的JSON文件,该文件可以包含多个不同的指令来告诉Node包管理器如何处理模块. dependencies则表示此模块依赖 ...
- cadence学习二----->Allegro基本概念
Class与Subclass 同一根线在不同的Subclass里的含义不一样,下面介绍常用Class和Subclass的含义 1.Etch 包括TOP和BOTTOM,用于走线和覆铜 2.Package ...
- 【MySQL】MySQL内连接,左连接,右连接查询
概念 INNER JOIN(内连接):获取两个表中字段匹配关系的记录.也就是只会返回共有的内容. LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录. RIGHT JOIN(右 ...
- hostapd、/dev/random、/dev/urandom
在使用hostapd做软ap时,出现了random熵不够的问题,导致节点连接不上这个ap. 下面先解释一下/dev/random和/dev/urandom 先让我们从一个工程中遇到的实际问题开始,先上 ...
- 安装kafka过程及出现的问题解决
第一步:下载kafka安装包 下载地址:http://kafka.apache.org/downloads 解压 到/usr/local 目录 tar -zxvf kafka_2.12-2.2.0 第 ...
- 2-2、安装Filebeat
安装filebeat 第1步:安装Filebeat 开始之前:如果尚未安装Elastic Stack,请立即执行此操作. 请参阅Getting started with the Elastic Sta ...
- java----代码打包
打包 文件生成在out目录下 D:\IDEA代码\out\artifacts\IDEA_jar 注意打包好像只能打包src下面的代码 不在src目录下的一些文件,自己文件添加到打包好的目录下 可以选择 ...
- web.xml 简记
web.xml (tomcat启动时读取的配置文件) 首页配置 <welcome-file-list>:index.jsp servlet配置(<servlet>和<se ...