Spring自定义注解配置切面实现日志记录
一:
spring-mvc.xml:
<!--配置日志切面 start,必须与mvc配置在同一个配置文件,否则无法切入Controller层-->
<!-- 声明自动为spring容器中配置@aspectj切面的bean创建代理 ,织入切面 -->
<context:component-scan base-package="org.jeecgframework.core.aop" />
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true"></aop:config>
<!--配置日志切面 end--> 二:
LoginLog.java:
import java.lang.annotation.*; /**
* <ul>
* <li>使用运行时参数值(用法:1-->#p{下标} 2-->#{形参名称.属性名} 3-->${形参名称.属性名})</li>
* <li>第一种适合参数为基本类型或String</li>
* <li>第二种适合参数为实体类</li>
* <li>第三种适合参数为实体类List集合,会将属性按照","隔开再返回</li>
* <li>备注:</li>
* <li>需要多个值用","隔开,前后可加说明性文字,举例:</li>
* <li>@AdminLog(module = "更新#p{0}模块",content = "更新地址:#{bcContact.address}完毕,电话:${bcContactSubList.phone}完毕")
* </li>
* <li>
public void updateMain(String test,BcContactEntity bcContact,
List<BcContactSubEntity> bcContactSubList)</li>
<li>module结果-->更新测试模块,content结果-->更新地址:****完毕,电话:***,***完毕</li>
* <ul/>
*/
@Retention(RetentionPolicy.RUNTIME) //注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD) //注解到方法
@Documented //注解包含在javadoc中
@Inherited //注解可以被继承
public @interface LoginLog {
/** 登录时长 */
String time() default ""; /** 登录类型 */
String type() default ""; } 三:
LoginLogAspect.java:
import com.aiitec.log.entity.BcLoginLogEntity;
import com.aiitec.log.service.BcLoginLogServiceI;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecgframework.core.annotation.LoginLog;
import org.jeecgframework.core.util.AspectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Aspect
@Component
public class LoginLogAspect {
@Autowired
private BcLoginLogServiceI bcLoginLogServiceI;
private static final Logger log = LoggerFactory.getLogger(LoginLogAspect.class); // 配置织入点
@Pointcut("@annotation(org.jeecgframework.core.annotation.LoginLog)")
public void logPointCut() {
} /**
* 前置通知 用于拦截操作,在方法返回后执行
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()")
public void doAfter(JoinPoint joinPoint) {
handleLog(joinPoint,null);
} /**
* 拦截异常操作,有异常时执行
*
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e);
} private void handleLog(JoinPoint joinPoint,Exception e) {
try {
// 获得注解
LoginLog controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null) {
return;
}
// 获得方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
String type = controllerLog.type();
String time = controllerLog.time();
//打印日志,如有需要还可以存入数据库
log.info(">>>>>>>>>>>>>模块名称:{}",time);
log.info(">>>>>>>>>>>>>操作名称:{}",type);
log.info(">>>>>>>>>>>>>类名:{}",className);
log.info(">>>>>>>>>>>>>方法名:{}",methodName);
//获取所有的参数
Object[] args = joinPoint.getArgs();
String resultTime = AspectUtil.getAttributeValue(className,methodName,args,time);
String resultType = AspectUtil.getAttributeValue(className,methodName,args,type); log.info(">>>>>>>>>>>>>属性值:{}",resultTime);
//保存登录日志信息到数据库
BcLoginLogEntity bcLoginLogEntity=new BcLoginLogEntity();
bcLoginLogEntity.setContent(resultType+","+resultTime);
bcLoginLogEntity.setLoginType(0);
bcLoginLogEntity.setLoginTime(3600);
bcLoginLogServiceI.save(bcLoginLogEntity);
} catch (Exception exp) {
// 记录本地异常日志
log.error("==登录日志后置通知异常==");
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
} /**
* 是否存在注解,如果存在就获取
*/
private static LoginLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(LoginLog.class);
}
return null;
} } 四:
AspectUtil.java
import org.apache.commons.lang.StringUtils;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; public class AspectUtil {
/**
* 判断是否为实体类使用
*/
public final static HashMap<String, Class> IS_ENTITY_MAP = new HashMap<String, Class>() {
{
put("java.lang.Integer", int.class);
put("java.lang.Double", double.class);
put("java.lang.Float", float.class);
put("java.lang.Long", long.class);
put("java.lang.Short", short.class);
put("java.lang.Boolean", boolean.class);
put("java.lang.Char", char.class);
}
};
/**
* 解析实体类,获取实体类中的属性
* @param obj
* @param arg
* @return
*/
public static String getFieldsValue(Object obj,String arg) {
//通过反射获取所有的字段,getFileds()获取public的修饰的字段
//getDeclaredFields获取private protected public修饰的字段
Field[] fields = obj.getClass().getDeclaredFields();
StringBuilder sb = new StringBuilder();
for (Field f : fields) {
//在反射时能访问私有变量
f.setAccessible(true);
try {
if (f.get(obj)!=null){
if(arg.equals(f.getName())){//找到对应属性
sb.append(f.get(obj) + "");//取值
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return sb.toString();
} /**
* 获取指定参数的值
* @param className 类名
* @param methodName 方法名
* @param args 参数数组
* @param key 自定义字段(用法:1-->#p{下标} 2-->#{形参名称.属性名} 3-->${形参名称.属性名})
* <ul>
* <li>第一种适合参数为基本类型或String</li>
* <li>第二种适合参数为实体类</li>
* <li>第三种适合参数为实体类List集合,会将属性按照","隔开再返回</li>
* <ul/>
* @return
* @throws ClassNotFoundException
* @throws NoSuchMethodException
*/
public static String getAttributeValue(String className,String methodName,Object[] args,String key) throws ClassNotFoundException, NoSuchMethodException {
Class<?>[] classes = new Class[args.length];
for (int k = 0; k < args.length; k++) {
if (!args[k].getClass().isPrimitive()) {
//获取的是封装类型而不是基础类型
String result = args[k].getClass().getName();
Class s = IS_ENTITY_MAP.get(result);
if (result.equals("java.util.ArrayList")){//如果是ArrayList替换为List,否则找不到该方法(因为方法声明为List,传入的是ArrayList)
classes[k] = Class.forName("java.util.List");
}else{
classes[k] = s == null ? args[k].getClass() : s;
}
}
}
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
//获取指定的方法,第二个参数可以不传,但是为了防止有重载的现象,还是需要传入参数的类型
Method method = Class.forName(className).getMethod(methodName, classes);
String[] parameterNames = pnd.getParameterNames(method); StringBuffer sb=new StringBuffer();
if (StringUtils.isNotBlank(key)){
String[] vars=key.split(",");
for(String var:vars){
if(var.indexOf("#p")>-1){//包含#p
String index = var.substring(var.indexOf("{") + 1,var.indexOf("}"));
sb.append(var.replaceAll("#p\\{.*\\}",args[Integer.valueOf(index)]+"")+",");//通过注解属性值中的"#p[n]"n的值,确定参数下标,获取参数值 }else if(var.indexOf("#")>-1){//包含# String paramName= var.substring(var.indexOf("{")+1,var.indexOf("."));//参数名
int index= Arrays.asList(parameterNames).indexOf(paramName);//参数下标
String temp=getFieldsValue(args[index],var.substring(var.indexOf(".")+1,var.indexOf("}")));//传入参数对象和属性名
sb.append(var.replaceAll("#\\{.*\\}",temp)+",");
}else if(var.indexOf("$")>-1){//包含$
String paramName= var.substring(var.indexOf("{")+1,var.indexOf("."));//参数名
String attrName=var.substring(var.indexOf(".")+1,var.indexOf("}"));//属性名
int index=Arrays.asList(parameterNames).indexOf(paramName);//参数下标
final ArrayList argList = (ArrayList<?>) args[index];
StringBuffer temp=new StringBuffer();
for (Object o : argList) {
String keys=getFieldsValue(o,attrName);
temp.append(keys+",");
}
if(temp.length()>0){
if (',' == temp.charAt(temp.length() - 1)){
temp.deleteCharAt(temp.length() - 1);//去掉最后一个","
}
}
sb.append(var.replaceAll("\\$\\{.*\\}",temp.toString())+",");
}else{//普通字符串
sb.append(var+",");
}
}
if(sb.length()>0){
if (',' == sb.charAt(sb.length() - 1)){
sb.deleteCharAt(sb.length() - 1);//去掉最后一个","
}
}
} return sb.toString();
}
}
Spring自定义注解配置切面实现日志记录的更多相关文章
- springboot aop 自定义注解方式实现完善日志记录(完整源码)
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...
- 010-Spring aop 001-核心说明-拦截指定类与方法、基于自定义注解的切面
一.概述 面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志.安全.事务.性能监控等等.SpringAOP允许将公共行为从业务 ...
- SpringAop切面实现日志记录
SpringAop切面实现日志记录代码实现:https://www.cnblogs.com/wenjunwei/p/9639909.html 问题记录 1.signature.getMethod(). ...
- 自定义注解结合切面和spel表达式
在我们的实际开发中可能存在这么一种情况,当方法参数中的某些条件成立的时候,需要执行一些逻辑处理,比如输出日志.而这些代码可能都是差不多的,那么这个时候就可以结合自定义注解加上切面加上spel表达式进行 ...
- JavaWeb_(Spring框架)注解配置
系列博文 JavaWeb_(Spring框架)xml配置文件 传送门 JavaWeb_(Spring框架)注解配置 传送门 Spring注解配置 a)导包和约束:基本包.aop包+context约束 ...
- spring自定义注解实现登陆拦截器
1.spring自定义注解实现登陆拦截器 原理:定义一个注解和一个拦截器,拦截器拦截所有方法请求,判断该方法有没有该注解.没有,放行:有,要进行验证.从而实现方法加注解就需要验证是否登陆. 2.自定义 ...
- Spring 自定义注解,配置简单日志注解
java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...
- Spring 自定义注解,结合AOP,配置简单日志注解 (转)
java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...
- Spring AOP中使用@Aspect注解 面向切面实现日志横切功能详解
引言: AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一 ...
随机推荐
- Info.plist文件配置及注意事项
1.Info.plist文件配置 常见配置 2.注意事项 Info.plist文件移动路径修改编译报错:could not read data from '/Users/lelight/Desktop ...
- 图像特征提取之Haar特征
1.Haar-like特征 Haar-like特征最早是由Papageorgiou等应用于人脸表示,Viola和Jones在此基础上,使用3种类型4种形式的特征. Haar特征分为三类:边缘特征.线性 ...
- HTML5应用——生日快乐动画之星星
在讲述绘制星星动画之前,先介绍一点javascript知识. 面向对象: javascript本质上不是面向对象语言,而是脚本语言,一般只适合简单.代码量少的程序,因为脚本过于复杂会直接导致浏览器出现 ...
- 为asp.net core 自定义路由动态修改
根据IApplicationModelConvention 接口 实现相应的方法 /// <summary> /// 定义个类RouteConvention,private 来实现 IAp ...
- Linux系统之ssh命令
ssh命令用于远程登录上Linux主机. 常用格式:ssh [-l login_name] [-p port] [user@]hostname更详细的可以用ssh -h查看. 不指定用户: ssh 1 ...
- Codeforces Round #520 (Div. 2) B math(素数因子的应用)
题意: 给出一个n ; 有两个操作: 1,mul A , n=n*A : 2,sqrt() , n=sqrt(n) 开更出来必须是整数 : 求出经过这些操作后得出的最小 n , 和 ...
- Experimental Educational Round: VolBIT Formulas Blitz D
Description After a probationary period in the game development company of IT City Petya was include ...
- 服务器查看外网IP地址和方法
返回IP地址 curl ip..com/ip.aspx curl whatismyip.akamai.com wget -qO - ifconfig.co curl icanhazip.com dig ...
- 分布式事务框架-fescar
https://github.com/alibaba/fescar/wiki/%E6%A6%82%E8%A7%88?spm=5176.11156381.0.0.b9f85ceegUXvCC
- JMeter 正则表达式提取器(二)
引用名: 引用此数据-别名 正则表达式: 取值 模板: 表示使用提取到的第几个值:$-1$:表示取所有值$0$:表示随机取值$1$:表示取第1个$2$:表示取第二个以此类推:$n$:表示取第n个 ...