1、基本概念
AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待
1.1 相关概念
横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
Aspect(切面): 通常是一个类,里面可以定义切入点和通知
JointPoint(连接点): 程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)
Pointcut(切入点): 就是带有通知的连接点,在程序中主要体现为书写切入点表达式
**weave(织入)**:将切面应用到目标对象并导致代理对象创建的过程
introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
**AOP代理(AOP Proxy)**:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。
1.2 AOP使用场景
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
2、AOP实现记录操作日志
2.1 引入依赖
在Spring框架中,使用AOP配合自定义注解可以方便的实现用户操作的监控。首先搭建一个基本的Spring Boot Web环境开启Spring Boot,然后引入必要依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency>
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency>
|
2.2 自定义注解
定义一个方法级别的@Log注解,用于标注需要监控的方法:
1 2 3 4 5
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; }
|
2.3 创建表和实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| create table sys_log( id int(10) not null AUTO_INCREMENT, username varchar(50), operation varchar(50), time int(11), method varchar(200), params varchar(500), ip varchar(64), create_time datetime, primary key(id) using btree )ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; public class SysLog implements Serializable { private int id; private String username; private String operation; private int time; private String method; private String params; private String ip; private Date createTime; //此处省略setter和getter方法 }
|
2.4 保存日志的方法
1 2 3 4
| @Mapper public interface SysLogDao { public void saveSysLog(SysLog sysLog); }
|
SysLogMapper.xml
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lyq.dao.SysLogDao"> <insert id="saveSysLog"> insert into sys_log (username,operation,time,method,params,ip,create_time) values (#{username},#{operation},#{time},#{method},#{params},#{ip},#{createTime}) </insert> </mapper>
|
2.5 切面和切点
定义一个LogAspect类,使用@Aspect标注让其成为一个切面,切点为使用@Log注解标注的方法,使用@Around环绕通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| @Aspect @Component public class LogAspect { @Autowired private SysLogDao sysLogDao; @Pointcut("@annotation(com.lyq.annotation.Log)") public void pointCut(){}
@Around("pointCut()") public Object around(ProceedingJoinPoint point) { Object result = null; long beginTime = System.currentTimeMillis(); try { result = point.proceed(); } catch (Throwable e) { e.printStackTrace(); } long time = System.currentTimeMillis() - beginTime; saveLog(point, time); return result; }
private void saveLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog sysLog = new SysLog(); Log logAnnotation = method.getAnnotation(Log.class); if (logAnnotation != null) { sysLog.setOperation(logAnnotation.value()); } String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); sysLog.setMethod(className + "." + methodName + "()"); Object[] args = joinPoint.getArgs(); LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null) { String params = ""; for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } sysLog.setParams(params); } HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); sysLog.setIp(IPUtils.getIpAddr(request)); sysLog.setUsername("testUser"); sysLog.setTime((int) time); sysLog.setCreateTime(new Date()); sysLogDao.saveSysLog(sysLog); } }
|
点击这里查看项目源码