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首先 ...
随机推荐
- 【转】Python函数默认参数陷阱
[转]Python函数默认参数陷阱 阅读目录 可变对象与不可变对象 函数默认参数陷阱 默认参数原理 避免 修饰器方法 扩展 参考 请看如下一段程序: def extend_list(v, li=[]) ...
- 使用freemarker生成word文档处理表格数据
1.把需要从数据库取值的字段用${}括起来,如:${busDate};2.表格数据的循环需要加标签:<#list tbl3 as tbl3>......</#list>< ...
- 关于VC预定义常量_WIN32,WIN32,_WIN64等预定义宏的介绍(整理、转载)
参考帖子: (1)MSDN上专门讲预定义宏:https://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx (2)VS中属性页的配置介绍 ...
- 3D模型网站分享
http://www.cgjoy.com/work.php CG作品网 http://www.cgmxw.com/ CG模型王 http://www.cgmodel.cn/ CG模型网
- RSA加密解密算法
/** * RSA加密解密算法 * Class Rsa */ class Rsa { /** * 获取pem格式的公钥 * @param $public_key 公钥文件路径或者字符串 * @retu ...
- proxy ubunta
/etc/environment : Is the correct place to specify system-wide environment variables that should be ...
- Codeforces Round #352 (Div. 2) (A-D)
672A Summer Camp 题意: 1-n数字连成一个字符串, 给定n , 输出字符串的第n个字符.n 很小, 可以直接暴力. Code: #include <bits/stdc++.h& ...
- FATAL ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)”
https://stackoverflow.com/questions/29188789/eclipse-mac-os-x-debug-error-fatal-error-in-native-meth ...
- TFS2015创建项目
1,在TFS服务器上的团队项目集合中创建集合 2,创建集合完毕后,在VS2017中选择管理连接,创建对应的管理连接. 3,团队资源管理器中新建团队项目.后续就是下一步,下一步完成.帐号权限 ...
- Node.js 操作 OSX 系统麦克风、扬声器音量
最近几年 Electron 很火,公司也正好有个项目想做跨平台客户端,大家研究了一下就选择了 Electron,第一次做 js 的项目遇到了不少坑,不过也都一点点解决了. 因为项目中需要对用户录音,H ...