一:

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. 【bzoj3813】: 奇数国 数论-线段树-欧拉函数

    [bzoj3813]: 奇数国 题意:给定一个序列,每个元素可以分解为最小的60个素数的形式.(x=p1^k1*p2^k2*......p60^k60)(p1=2,p2=3,…,p60=281) 支持 ...

  2. server2008 IIS7配置全过程(包括发布ASP网站)

    一.安装IIS 1.打开服务器管理器->角色->添加角色,选中WEB服务器(IIS),记得要把包括之后出现的ASP相关的东西都选中并安装,安装成功后,打开http://localhost/ ...

  3. redis之常用Set和ZSet命令

    redis五种数据类型:String,Hash,List,Set,ZSet.五种数据类型各有优点,各司其职.最后两种数据类型Set和ZSet基本上是一种,ZSet就是给Set加了个排序而已(相当于加了 ...

  4. sublime 显示 python 错误信息

    默认的编译器可以直接按Ctrl+B编译运行Python,并在控制台输出结果.美中不足的是无法输出中文,需要自己手动配置一番. 在Sublime Text 3 中依次点击菜单Tools->buil ...

  5. Luogu P1120 小木棍 [数据加强版] 来来来我们一起来剪枝,剪枝,剪枝、、、

    好啊...太棒了... dfs(拼到第几根木棍,这根木棍剩余长度,上一根木棍的位置) len是木棍的长度,cnt是木棍的个数 震撼人心的剪枝: 1.枚举长度从最大的木棍开始,直到sum/2,因为之后只 ...

  6. Luogu P5122 [USACO18DEC]Fine Dining 最短路

    先跑一遍n为起点最短路,再新开一个点,向有干草垛的点连一根边权为d[u]-w的有向边(很重要..我当时连的无向边,然后我死了.),相当于用价值抵消一部分边权, 然后以这个新的点为起点跑最短路就好了.. ...

  7. HDU计算机学院大学生程序设计竞赛(2015’12)The Country List

    Problem Description As the 2010 World Expo hosted by Shanghai is coming, CC is very honorable to be ...

  8. Photoshop入门教程(三):图层

    学习心得:图层可以说是Photoshop的核心,看似简单,但是对于图像的各种编辑都是基于图层.他就像一层透明的.没有厚度的玻璃纸,每张玻璃纸设置不同的效果,层层叠加,最后显现出绚烂的效果. 在进行图像 ...

  9. layer 弹出对话框 子父页面相互参数传递

    转载:https://blog.csdn.net/flybridy/article/details/78610737

  10. Problem09 求完数

    题目:一个数如果恰好等于它的因子之和,这个数就称为"完数". 分析:例如6=1+2+3. 编程找出1000以内的所有完数. 假如整数n除以m,结果是无余数的整数,那么我们称m就是n ...