AOP复习
实现原理
CGLIB继承 父子关系实现is a
JDK耦合 组合 兄弟关系实现 has a
开始AOP拓展
将日志文件信息写入到数据库
有目标 如下图
和数据库打交道的还是数据层
得把数据层写一些
dao层
mapper
回到切面
先写无参 后面需要什么再定义什么
回到切面上面
doAround
上面log日志的才是aop
自己想到的问题
不涉及到aop 所以自动依赖注入和手动 都不一样的
自己去复现
1.模仿上面的概念点
@Autowired
private SysLogService sysLogService;
/**记录用户行为的正常信息*/
private void saveUserLog(ProceedingJoinPoint jp,long time) {
//1.获取用户行为日志
String ip=IPUtils.getIpAddr();
String username="cgb2005";//将来是登录用户的用户名
Date createdTime=new Date();
//获取操作名
String operation="operation";
String method=getTargetClassMethod(jp);
String params=Arrays.toString(jp.getArgs());
//2.封装用户行为日志(在内容中通过对象去实现)
SysLog log=new SysLog();
log.setIp(ip);
log.setUsername(username);
log.setCreatedTime(createdTime);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
sysLogService.saveObject(log);
}
/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
private String getOperation(ProceedingJoinPoint jp) {
//1.获取目标对象类型
//2.获取目标方法对象
//3.获取方法上的reqiredLog注解对象
//4.获取注解中的operation的值.
return null;
}
2.dao层
3.service层
如下图 去补充注解
ip
5.controller
不需要
6.html 客户端
获取注解对象方法
如下图 拿到更多
如下图 只取到了接口的注解方法 需要的是接口的实现类
改了配置文件之后 true之后
想一个稳定的实现
为了实现
改成细密度
然后想要一个json格式的字符串
json读出来好处理
都得加上注解
声明式侵入式
key:value 格式
自己实现
实现类
日志管理界面记录日志信息 并且以json串格式呈现
查询都是目标方法
操作没显示注释的名称
应该是注解方面的问题
@Autowired
private SysLogService sysLogService;
/**记录用户行为的正常信息*/
private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
//1.获取用户行为日志
String ip=IPUtils.getIpAddr();
String username="cgb2005";//将来是登录用户的用户名
Date createdTime=new Date();
//获取操作名
String operation=getOperation(jp);
String method=getTargetClassMethod(jp);
//String params=Arrays.toString(jp.getArgs());//普通格式字符串
String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
//2.封装用户行为日志(在内容中通过对象去实现)
SysLog log=new SysLog();
log.setIp(ip);
log.setUsername(username);
log.setCreatedTime(createdTime);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
sysLogService.saveObject(log);
}
/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
private String getOperation(ProceedingJoinPoint jp)throws Exception {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标方法对象
//2.1获取方法签名信息
MethodSignature ms=(MethodSignature)jp.getSignature();
//2.2获取方法对象
//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
//Method method=ms.getMethod();
//假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
//3.获取方法上的reqiredLog注解对象
RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
//4.获取注解中的operation的值.
if(requiredLog==null)return "operation";
return requiredLog.operation();
}
晚上回看``
//@Pointcut("bean(sysUserServiceImpl)")
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
不懂怎么区分
晚上分析错误
错误代码
package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
/**
* @Aspect 注解描述的类为spring容器中的一个切面对象类型(此类型中封装切入点与通知方法)
* 1)切入点:(要执行拓展业务的方法的集合)
* 2)通知方法:封装了在切入点方法上要执行的拓展业务方法.
*/
@Order(1)
@Slf4j
@Aspect
@Component
public class SysLogAspect {
//private static final Logger log=LoggerFactory.getLogger(SysLogAspect.class);
/**
* @Pointcut 注解用于描述切入点(在哪些点上执行拓展业务)
* bean(bean对象名字):为一种切入点表达式(这个表达式中定义了哪个或哪些bean对象的方法要进行功能扩展).
* 例如,bean(sysUserServiceImpl)表达式表示名字为sysUserServiceImpl的bean对象中所有方法的集合为切入点,
* 也就是说这个sysUserServiceImpl对象中的任意方法执行时都要进行功能扩展.
*/
//@Pointcut("bean(sysUserServiceImpl)")
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
public void doLogPointCut() {}//此方法内部不需要写具体实现(方法的方法名也是任意)
/**
* Around注解描述的方法为一个通知方法(服务增益方法),此方法内部可以做服务增益(拓展业务),@Around注解
* 内部要指定切入点表达式,在此切入点表达式对应的切入点方法上做功能扩展.
* @param jp 表示连接点,连接点是动态确定的,用于封装正在执行的切入点方法(目标方法)信息.
* @return 目标方法的执行结果
* @throws Throwable 通知方法执行过程中出现的异常
*/
@Around("doLogPointCut()")
public Object doLogAround(ProceedingJoinPoint jp)throws Throwable {
//1.记录方法开始执行时间
try {
long t1=System.currentTimeMillis();
log.info("start:{}",t1);
//2.执行目标方法
Object result=jp.proceed();//最终(中间还可以调用本类其它通知或其它切面的通知)会调用目标方法
//3.记录方法结束执行时间
long t2=System.currentTimeMillis();
log.info("after:{}",t2);
String targetClassMethod=getTargetClassMethod(jp);
log.info("{}目标方法执行耗时:{}",targetClassMethod,(t2-t1));
//4.将正常行为日志信息写入到数据库
saveUserLog(jp,(t2-t1));
//5.返回目标方法执行结果
return result;//目标方法的返回结果
}catch(Throwable e) {
log.error("目标方法执行时出现了异常:{}",e.getMessage());
throw e;
}
}
@Autowired
private SysLogService sysLogService;
/**记录用户行为的正常信息*/
private void saveUserLog(ProceedingJoinPoint jp,long time) {
//1.获取用户行为日志
String ip=IPUtils.getIpAddr();
String username="cgb2005";//将来是登录用户的用户名
Date createdTime=new Date();
//获取操作名
String operation="operation";
String method=getTargetClassMethod(jp);
String params=Arrays.toString(jp.getArgs());
//2.封装用户行为日志(在内容中通过对象去实现)
SysLog log=new SysLog();
log.setIp(ip);
log.setUsername(username);
log.setCreatedTime(createdTime);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
sysLogService.saveObject(log);
}
/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
private String getOperation(ProceedingJoinPoint jp)throws Exception {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标方法对象
//2.1获取方法签名信息
MethodSignature ms=(MethodSignature)jp.getSignature();
//2.2获取方法对象
//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
//Method method=ms.getMethod();
//假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
//3.获取方法上的reqiredLog注解对象
RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
//4.获取注解中的operation的值.
if(requiredLog==null)return "operation";
return requiredLog.operation();
}
/**获取目标方法的全限定名(目标类全名+方法名)*/
private String getTargetClassMethod(ProceedingJoinPoint jp) {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标对象类型的类全名
String targetClsName=targetCls.getName();
//3.获取目标方法名
//3.1获取方法签名(方法签名对象中封装了方法相关信息)
MethodSignature ms=(MethodSignature)jp.getSignature();
//3.2基于方法签名获取方法名
String methodName=ms.getName();
//4.构建方法的全限定名并返回
return targetClsName+"."+methodName;
}
}
正确代码
package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
/**
* @Aspect 注解描述的类为spring容器中的一个切面对象类型(此类型中封装切入点与通知方法)
* 1)切入点:(要执行拓展业务的方法的集合)
* 2)通知方法:封装了在切入点方法上要执行的拓展业务方法.
*/
@Order(1)
@Slf4j
@Aspect
@Component
public class SysLogAspect {
//private static final Logger log=LoggerFactory.getLogger(SysLogAspect.class);
/**
* @Pointcut 注解用于描述切入点(在哪些点上执行拓展业务)
* bean(bean对象名字):为一种切入点表达式(这个表达式中定义了哪个或哪些bean对象的方法要进行功能扩展).
* 例如,bean(sysUserServiceImpl)表达式表示名字为sysUserServiceImpl的bean对象中所有方法的集合为切入点,
* 也就是说这个sysUserServiceImpl对象中的任意方法执行时都要进行功能扩展.
*/
//@Pointcut("bean(sysUserServiceImpl)")
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
public void doLogPointCut() {}//此方法内部不需要写具体实现(方法的方法名也是任意)
/**
* Around注解描述的方法为一个通知方法(服务增益方法),此方法内部可以做服务增益(拓展业务),@Around注解
* 内部要指定切入点表达式,在此切入点表达式对应的切入点方法上做功能扩展.
* @param jp 表示连接点,连接点是动态确定的,用于封装正在执行的切入点方法(目标方法)信息.
* @return 目标方法的执行结果
* @throws Throwable 通知方法执行过程中出现的异常
*/
@Around("doLogPointCut()")
public Object doLogAround(ProceedingJoinPoint jp)throws Throwable {
//1.记录方法开始执行时间
try {
long t1=System.currentTimeMillis();
log.info("start:{}",t1);
//2.执行目标方法
Object result=jp.proceed();//最终(中间还可以调用本类其它通知或其它切面的通知)会调用目标方法
//3.记录方法结束执行时间
long t2=System.currentTimeMillis();
log.info("after:{}",t2);
String targetClassMethod=getTargetClassMethod(jp);
log.info("{}目标方法执行耗时:{}",targetClassMethod,(t2-t1));
//4.将正常行为日志信息写入到数据库
saveUserLog(jp,(t2-t1));
//5.返回目标方法执行结果
return result;//目标方法的返回结果
}catch(Throwable e) {
log.error("目标方法执行时出现了异常:{}",e.getMessage());
throw e;
}
}
@Autowired
private SysLogService sysLogService;
/**记录用户行为的正常信息*/
private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
//1.获取用户行为日志
String ip=IPUtils.getIpAddr();
String username="cgb2005";//将来是登录用户的用户名
Date createdTime=new Date();
//获取操作名
String operation=getOperation(jp);
String method=getTargetClassMethod(jp);
//String params=Arrays.toString(jp.getArgs());//普通格式字符串
String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
//2.封装用户行为日志(在内容中通过对象去实现)
SysLog log=new SysLog();
log.setIp(ip);
log.setUsername(username);
log.setCreatedTime(createdTime);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
sysLogService.saveObject(log);
}
/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
private String getOperation(ProceedingJoinPoint jp)throws Exception {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标方法对象
//2.1获取方法签名信息
MethodSignature ms=(MethodSignature)jp.getSignature();
//2.2获取方法对象
//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
//Method method=ms.getMethod();
//假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
//3.获取方法上的reqiredLog注解对象
RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
//4.获取注解中的operation的值.
if(requiredLog==null)return "operation";
return requiredLog.operation();
}
/**获取目标方法的全限定名(目标类全名+方法名)*/
private String getTargetClassMethod(ProceedingJoinPoint jp) {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标对象类型的类全名
String targetClsName=targetCls.getName();
//3.获取目标方法名
//3.1获取方法签名(方法签名对象中封装了方法相关信息)
//Signature s=jp.getSignature();
MethodSignature ms=(MethodSignature)jp.getSignature();
//3.2基于方法签名获取方法名
//String methodName=s.getName();
String methodName=ms.getName();
//4.构建方法的全限定名并返回
return targetClsName+"."+methodName;
}
}
事务管理
数据层控制事务 mybatis
两个操作放在一个事务中执行
想法就是 加一个注解
如上图 作用 在方法执行之前开始事务 方法结束之后提交事务
晚上11.25分 回看事务
示例的错误点
加入错误的点
if(rows>0)
throw new ServiceException("关系数据保存失败 检验事务开没开启 然后查询验证");
尝试自我实现回归和事务
模拟
添加错误代码 先不加事务
错误经过不显示 拦截
回看代码
加了事务注解
加了事务处理注解
结束
类上定义了的注解 方法上也定义了注解
方法上的优先级更高
类的注解 更详细
@Transactional注解,则方法上的注解一般用于事务特性的定义。
@Transactional 常用属性应用说明:
timeout:事务的超时时间,默认值为-1,表示没有超时显示。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务开启以后到sql语句执行之前。
read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true。对添加,修改,删除业务read-only的值应该为false。
rollback-for:用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for: 抛出no-rollback-for 指定的异常类型,不回滚事务。
isolation事务的隔离级别,默认值采用 DEFAULT。当多个事务并发执行时,可能会出现脏读,不可重复读,幻读等现象时,但假如不希望出现这些现象可考虑修改事务的隔离级别(但隔离级别越高并发就会越小,性能就会越差)
方法上的优先级更高
ps 这个注解表表明出现任何错误都会回滚
@Transactional(timeout =60,rollbackFor = Throwable.class,
noRollbackFor = ServiceException.class //service层出现的异常
)
这里则是 遇到ServiceException类异常 不会滚
晚上 阿里文档 看异常规范
http://blog.itpub.net/26736162/viewspace-2638951/
事务的隔离级别
没写事务管理代码 却有事务管理逻辑 就是因为有AOP
环绕通知
如果没有事务创建新事务, 如果当前有事务参与当前事务, Spring 默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。
事务传播特性测试
切面
接口实现类
事务传播特性 当前会被挂起
如上图只读不可看了
可以在这样做
必须要两个impl
事务加注解=声明式事务
Spring 声明式事务是 Spring 最核心,最常用的功能
。由于 Spring 通过 IOC 和 AOP的功能非常透明地实现了声明式事务的功能,对于一般的开发者基本上无须了解 Spring声明式事务的内部细节,仅需要懂得如何配置就可以了。但对于中高端开发者还需要了解其内部机制。
通过代码去解决业务
代码会变 业务 逻辑不会变
初学者 业务驱动业务
AOP异步操作 串行改并行
线程 被阻塞了 效率肯定比较慢 同一线程去写
写日志依然有阻塞(5s) 但是不影响显示
一万个并发量 10g内存没了
底层必须不停地 gc 还要会内存溢出
线程对象得到重用
项目实践
问题:内存溢出
模拟并发 雷哥电脑无了
自我实现 模拟延时操作
1.
如上二图 日志会有延迟(5s)
讲解
并发量高 租用阿里云的平台
线程切换也很耗时
异步就是两个线程同时操作
1.启动异步
log日志面
启动类就是通知方法
说起“并行”自然离不开“异步”
晚上再看一次
缓存
如下图 删除之后还要数据 是cache的数据
需要在方法中清除
如上图 和day19号同理
0821 晚总结
注解和获取日志对应操作方法名 @RequiredLog(operation = “xxxxxx”)
切入点(Pointcut):
@Pointcut(“bean(sysUserServiceImpl)”)
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
理解用户管理页面的切面方法 回看切面理解的图
切入点 遗忘
首先了解什么是切入点
图
拿实际切面类去校对学习
bean表达式
一般应用于类级别,实现粗粒度的切入点定义,案例分析:
bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法。
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中某个bean的name。
最重要@annotation表达式(重点)
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析
@annotation(anno.RequiredLog) 匹配有此注解描述的方法。
@annotation(anno.RequiredCache) 匹配有此注解描述的方法。
其中:RequiredLog为我们自己定义的注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作。
课堂练习:定义一Cache相关切面,使用注解表达式定义切入点,并使用此注解对需要使用cache的业务方法进行描述,代码分析如下:
package com.cy.pj.common.aspect;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.cy.pj.common.IPUtils;
import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
/**
* @Aspect 注解描述的类为spring容器中的一个切面对象类型(此类型中封装切入点与通知方法)
* 1)切入点:(要执行拓展业务的方法的集合)
* 2)通知方法:封装了在切入点方法上要执行的拓展业务方法.
*/
/*order的值越小,优先级越高
order如果不标注数字,默认最低优先级,因为其默认值是int最大值
该注解等同于实现Ordered接口getOrder方法,并返回数字。
*/
//@Order(1) //默认是最低优先级,值越小优先级越高
@Slf4j
@Aspect
@Component
public class SysLogAspect {
//private static final Logger log=LoggerFactory.getLogger(SysLogAspect.class);
/**
* @Pointcut 注解用于描述切入点(在哪些点上执行拓展业务)
* bean(bean对象名字):为一种切入点表达式(这个表达式中定义了哪个或哪些bean对象的方法要进行功能扩展).
* 例如,bean(sysUserServiceImpl)表达式表示名字为sysUserServiceImpl的bean对象中所有方法的集合为切入点,
* 也就是说这个sysUserServiceImpl对象中的任意方法执行时都要进行功能扩展.
*/
//@Pointcut("bean(sysUserServiceImpl)")
@Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
public void doLogPointCut() {}//此方法内部不需要写具体实现(方法的方法名也是任意)
/**
* Around注解描述的方法为一个通知方法(服务增益方法),此方法内部可以做服务增益(拓展业务),@Around注解
* 内部要指定切入点表达式,在此切入点表达式对应的切入点方法上做功能扩展.
* @param jp 表示连接点,连接点是动态确定的,用于封装正在执行的切入点方法(目标方法)信息.
* @return 目标方法的执行结果
* @throws Throwable 通知方法执行过程中出现的异常
*/
@Around("doLogPointCut()")
public Object doLogAround(ProceedingJoinPoint jp)throws Throwable {
//1.记录方法开始执行时间
try {
long t1=System.currentTimeMillis();
log.info("start:{}",t1);
//2.执行目标方法
Object result=jp.proceed();//最终(中间还可以调用本类其它通知或其它切面的通知)会调用目标方法
//3.记录方法结束执行时间
long t2=System.currentTimeMillis();
log.info("after:{}",t2);
String targetClassMethod=getTargetClassMethod(jp);
log.info("{}目标方法执行耗时:{}",targetClassMethod,(t2-t1));
//4.将正常行为日志信息写入到数据库
saveUserLog(jp,(t2-t1));
//5.返回目标方法执行结果
return result;//目标方法的返回结果
}catch(Throwable e) {
log.error("目标方法执行时出现了异常:{}",e.getMessage());
throw e;
}
}
@Autowired
private SysLogService sysLogService;
/**记录用户行为的正常信息*/
private void saveUserLog(ProceedingJoinPoint jp,long time)throws Exception {
//1.获取用户行为日志
String ip=IPUtils.getIpAddr();
String username="cgb2005";//将来是登录用户的用户名
Date createdTime=new Date();
//获取操作名
String operation=getOperation(jp);
String method=getTargetClassMethod(jp);
//String params=Arrays.toString(jp.getArgs());//普通格式字符串
String params=new ObjectMapper().writeValueAsString(jp.getArgs());//json格式的参数
//2.封装用户行为日志(在内容中通过对象去实现)
SysLog log=new SysLog();
log.setIp(ip);
log.setUsername(username);
log.setCreatedTime(createdTime);
log.setOperation(operation);
log.setMethod(method);
log.setParams(params);
log.setTime(time);
//3.将用户行为日志持久化(将内存中对象信息写入到数据库)
//sysLogService.saveObject(log);
new Thread() {
public void run() {
sysLogService.saveObject(log);
}
}.start();
}
/**获取目标方法上@ReqiredLog注解中定义的operation名字*/
private String getOperation(ProceedingJoinPoint jp)throws Exception {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标方法对象
//2.1获取方法签名信息
MethodSignature ms=(MethodSignature)jp.getSignature();
//2.2获取方法对象
//假如底层配置为jdk代理,则method指向接口中的抽象方法对象.
//假如底层配置为CGLIB代理,则这个method指向具体目标对象中的方法对象
//Method method=ms.getMethod();
//假如希望无论是jdk代理还是cglib代理,我们让method变量指向的都是目标对象中的方法对象,那如何实现?
Method method=targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
//3.获取方法上的reqiredLog注解对象
RequiredLog requiredLog=method.getAnnotation(RequiredLog.class);
//4.获取注解中的operation的值.
if(requiredLog==null)return "operation";
return requiredLog.operation();
}
/**获取目标方法的全限定名(目标类全名+方法名)*/
private String getTargetClassMethod(ProceedingJoinPoint jp) {
//1.获取目标对象类型
Class<?> targetCls=jp.getTarget().getClass();
//2.获取目标对象类型的类全名
String targetClsName=targetCls.getName();
//3.获取目标方法名
//3.1获取方法签名(方法签名对象中封装了方法相关信息)
//Signature s=jp.getSignature();
MethodSignature ms=(MethodSignature)jp.getSignature();
//3.2基于方法签名获取方法名
//String methodName=s.getName();
String methodName=ms.getName();
//4.构建方法的全限定名并返回
return targetClsName+"."+methodName;
}
}
周日下午 看多一次异步和自我实现一次注解cache