一.介绍

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注解实现日志的存储的更多相关文章

  1. SpringAOP+注解实现简单的日志管理

    今天在再次深入学习SpringAOP之后想着基于注解的AOP实现日志功能,在面试过程中我们也经常会被问到:假如项目已经上线,如何增加一套日志功能?我们会说使用AOP,AOP也符合开闭原则:对代码的修改 ...

  2. 【Spring】每个程序员都使用Spring(四)——Aop+自定义注解做日志拦截

    一.前言 上一篇博客向大家介绍了Aop的概念,对切面=切点+通知 .连接点.织入.目标对象.代理(jdk动态代理和CGLIB代理)有所了解了.理论很强,实用就在这篇博客介绍. 这篇博客中,小编向大家介 ...

  3. Spring2.5那些事之基于AOP的方法级注解式日志配置

    在日常开发中经常需要在代码中加入一些记录用户操作日志的log语句,比如谁在什么时间做了什么操作,等等. 把这些对于开发人员开说无关痛痒的代码写死在业务方法中实在不是一件很舒服的事情,于是AOP应运而生 ...

  4. SpringAop切面实现日志记录

    SpringAop切面实现日志记录代码实现:https://www.cnblogs.com/wenjunwei/p/9639909.html 问题记录 1.signature.getMethod(). ...

  5. 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存

    示例代码下载 : http://download.csdn.net/detail/han1202012/8638801; 一. 崩溃日志本地存储 1. 保存原理解析 崩溃信息本地保存步骤 : -- 1 ...

  6. 大数据学习——有两个海量日志文件存储在hdfs

    有两个海量日志文件存储在hdfs上, 其中登陆日志格式:user,ip,time,oper(枚举值:1为上线,2为下线):访问之日格式为:ip,time,url,假设登陆日志中上下线信息完整,切同一上 ...

  7. 运用Spring Aop,一个注解实现日志记录

    运用Spring Aop,一个注解实现日志记录 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到 ...

  8. springAOP注解方式实现日志操作

    通过自定义注解调用方法执行日志存储: package com.zktx.platform.log2; import java.lang.reflect.Method; import java.util ...

  9. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

随机推荐

  1. dash视频服务器本地搭建 (初探)

    2019-4-17 15:54:17 星期三 技术说明: dash: 将一个大视频分解成不同分辨率, 不同清晰度的小视频, 以及一个描述文件(后缀: mpd), 根据网络带宽自动调整视频流, 看起来更 ...

  2. php递归实现一维数组转为指定树状结构 --- 省市区处理

    ### 这两天脑壳痛,一时短路,想不到准备利用递归实现这个需求,最后还是要请教同事,回来自己在实现了一遍,并记录下来 ### 原数据: // { // 广东省: { // 广州市: [ // &quo ...

  3. 定时-thinkphp

    链接:https://pan.baidu.com/s/1wHayb9fYOiiMB3Scms0iRQ 提取码:51ex 复制这段内容后打开百度网盘手机App,操作更方便哦

  4. Python 爬虫-进阶开发之路

    第一篇:爬虫基本原理: HTTP, 爬虫基础 第二篇:环境安装与搭建: 第三篇:网页抓取:urllib,requests,aiohttp , selenium,  appium 第四篇:网页解析:re ...

  5. 先进过程控制之一:浅说APC

    先进过程控制(APC)技术作为在生产装置级的信息化应用,在优化装置的控制水平和提高生产过程的管理水平的同时,还为企业创造了可观的经济效益. 1.什么是APC 先进过程控制,简称APC,并不是什么新概念 ...

  6. Lesson 3-2 语句:循环语句

    3.2 循环语句 3.2.1 while 循环语句 --- while 语句包含:关键字while.条件.冒号.while子句(代码块). --- 执行while 循环,首先判断条件是否为真,如果为假 ...

  7. chrome启动参数之

    --remote-debugging-port Chrome 启动的时候,默认是关闭了调试端口的,如果要对一个目标 Chrome PC 浏览器进行调试,那么启动的时候,可以通过传递参数来开启 Chro ...

  8. zabbix3.0 agent安装配置

    zabbix3.0 agent安装配置wget http://repo.zabbix.com/zabbix/3.0/rhel/6/x86_64/zabbix-agent-3.0.0-2.el6.x86 ...

  9. 【玩转开源】BananaPi R2 —— 第一篇 Openwrt安装

    最近手上拿到一块香蕉派的R2,这块板子可以用作路由器,所以决定在板子上面跑一下Openwrt. R2的外观长这个样子,看起来还是比较酷的: 硬件介绍 CPU 是MTK的4核芯片mt7623n,搭配mt ...

  10. sql 语句中count()有条件的时候为什么要加上or null

    参考:https://blog.csdn.net/qq_32719287/article/details/79513164 1.sql 语句中count()有条件的时候为什么要加上or null. 如 ...