一:

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自定义注解配置切面实现日志记录的更多相关文章

  1. springboot aop 自定义注解方式实现完善日志记录(完整源码)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...

  2. 010-Spring aop 001-核心说明-拦截指定类与方法、基于自定义注解的切面

    一.概述 面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志.安全.事务.性能监控等等.SpringAOP允许将公共行为从业务 ...

  3. SpringAop切面实现日志记录

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

  4. 自定义注解结合切面和spel表达式

    在我们的实际开发中可能存在这么一种情况,当方法参数中的某些条件成立的时候,需要执行一些逻辑处理,比如输出日志.而这些代码可能都是差不多的,那么这个时候就可以结合自定义注解加上切面加上spel表达式进行 ...

  5. JavaWeb_(Spring框架)注解配置

    系列博文 JavaWeb_(Spring框架)xml配置文件  传送门 JavaWeb_(Spring框架)注解配置 传送门 Spring注解配置 a)导包和约束:基本包.aop包+context约束 ...

  6. spring自定义注解实现登陆拦截器

    1.spring自定义注解实现登陆拦截器 原理:定义一个注解和一个拦截器,拦截器拦截所有方法请求,判断该方法有没有该注解.没有,放行:有,要进行验证.从而实现方法加注解就需要验证是否登陆. 2.自定义 ...

  7. Spring 自定义注解,配置简单日志注解

    java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...

  8. Spring 自定义注解,结合AOP,配置简单日志注解 (转)

    java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...

  9. Spring AOP中使用@Aspect注解 面向切面实现日志横切功能详解

    引言: AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一 ...

随机推荐

  1. [hdu 2089] 不要62 数位dp|dfs 入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求[n, m]区间内不含4和62的数字个数. 这题有两种思路,直接数位dp和dfs 数位d ...

  2. 关于Unity中的UGUI优化,你可能遇到这些问题

    https://blog.uwa4d.com/archives/QA_UGUI-1.html 关于Unity中的UGUI优化,你可能遇到这些问题 作者:admin / 时间:2016年11月08日 / ...

  3. MPI编程指南

    MPI编程指南 一.     MPI概述 1.1  MPI的发展史 MPI标准化涉及到大约60个国家的人们,他们主要来自于美国和欧洲的40个组织,这包括并行计算机的多数主要生产商,还有来自大学.政府实 ...

  4. springboot结合swagger生成接口文档

    原文链接:https://www.cnblogs.com/xu-lei/p/7423883.html https://www.jianshu.com/p/b9ae3136b292 前后台分离的开发渐渐 ...

  5. 浅谈python web框架django2.x

    1.Django简介 Python下有多款不同的 Web 框架,Django是最有代表性的一种.许多成功的网站和APP都基于Django. Django是一个开源的Web应用框架,由Python写成. ...

  6. PHPExcel类库的使用

    首先下载PHPEXCEL 下载地址:https://github.com/PHPOffice/PHPExcel 一.生成Excel <?php require "PHPExcel-1. ...

  7. [Groovy]转:Groovy 通过 isCase 方法进行分类

    闭包实现了 isCase 方法,这样闭包可以在 grep 和 sw itch 中作为分类器使用,在这种情况下,各自的参数传递给闭包,然后调用闭包进行计算得到一个 Boo lean 值(参考 6.1 节 ...

  8. Android 远程连接数据库。。。。。

    本来是 6.0.6  换成mysql 5.1.14 驱动ok.... 将方法 放在 new Thread() 解决..... 只能在主线程绘制ui.... 解决办法...子Thread 获取数据后,将 ...

  9. 六度分离 (folyd算法)

    1967年,美国著名的社会学家斯坦利·米尔格兰姆提出了一个名为"小世界现象(small world phenomenon)"的著名假说,大意是说,任何2个素不相识的人中间最多只隔着 ...

  10. sqlserver 常用语法

    sqlserver查找 table, view, column select * from information_schema.tables where table_schema='bk' sele ...