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();
}
}
 

猜你喜欢

转载自www.cnblogs.com/zou-rong/p/10366736.html