使用Spring AOP 来记录用户操作日志并存储到数据库中

之前要做一个记录用户操作的日志记录,找了很多方法,最后选择使用spring AOP来实现。由于是要记录用户操作的日志,所以我使用的是返回通知(@AfterReturning),只有在前端调用了我后端的接口并成功返回,才调用我的切面方法记录用户的操作存储到数据库中。

LogAnnotation.java

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;

import org.springframework.data.mongodb.core.mapping.Document; 

/**
 * 日志
 * @author **
 *
 */
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Document
public @interface LogAnnotation {
	
    /** 日志描述 */
    String description() default "";
 
    /** 业务类型 */
    int bizID();
} 

切面类

LogAspect.java

@Aspect
@Component
@SuppressWarnings({ "unchecked", "unused", "rawtypes" })
public class LogAspect {
	// 注入service,用来将日志信息保存在数据库 这是我的service,你只需要注入你自己的service就行
	@Resource
	private OprLogDaoService oprLogDaoService;

    //在**处填入你的LogAnnotation所在的包
    //此代码是拦截所有使用了LogAnnotation注解的接口
	@Pointcut("@annotation(**.LogAnnotation)")
	// 定义一个切点
	private void accAspect() {
	}

    //返回通知
    @AfterReturning(pointcut= "accAspect()",returning="result")
	public void around(JoinPoint joinPoint, Object result) throws Throwable {

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
				.getRequest();
		String classType = joinPoint.getTarget().getClass().getName();
		Class<?> clazz = Class.forName(classType);
		String clazzName = clazz.getName();
		// 拦截的方法名称。当前正在执行的方法
		String methodName = joinPoint.getSignature().getName();
		// 获取方法的参数
		Object[] args = joinPoint.getArgs();
		

		// 获取传入参数的键值对
		Map<String, Object> map = (Map<String, Object>)         
        LogUtil.getFieldsName(this.getClass(), clazzName, methodName,
				args);
		// 将request中的参数转化为键值对,方便取出
		Map<String, Object> map2 = (Map<String, Object>) map.get("request");
		String resultArgs = result.toString();
		// 获取返回方法的参数
		JSONObject jasonObject = JSONObject.fromObject(resultArgs);
		Map mapResult = (Map) jasonObject;
		//将返回的result参数取出
		Map<String, Object> res = (Map<String, Object>) mapResult.get("result");
        //这样传入的参数和返回的参数都已变成Map对象
        //通过直接 .get("字段名")的方式将对应的字段值取出
        //比如取出用户的用户名ID
        Integer userID = (Integer) mapResult.get("userID");
		
			// 常见日志实体对象
			OprLog oprLog = new OprLog();
            
            //根据自己定义的实体类的属性将数据填入
            OprLog.setUserID(userID);
            OprLog.set...();        
			// 保存进数据库
            //开头定义的service,这边使用你自己的service就行了
			oprLogDaoService.addLog(oprLog);
		
	}

接下来就是在controller上添加注解了

只需要在接口上添加注解@LogAnnotation(description = "日志记录", bizID = 1)  bizID是我自己定义的你也可以定义成String类型,写上方法名。

@LogAnnotation(description = "日志记录", bizID = 1)
@RequestMapping(value = "/Test", method = RequestMethod.POST)

获取自定义注解内容的方法

可以直接在注解上写用户的操作内容(修改、添加之类的)

/**
	 * 获取注解内容
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 */
	public static Integer getMthodRemark(JoinPoint joinPoint)  
            throws Exception {  
        String targetName = joinPoint.getTarget().getClass().getName();  
        String methodName = joinPoint.getSignature().getName();  
        Object[] arguments = joinPoint.getArgs();  
  
		Class targetClass = Class.forName(targetName);
		Method[] method = targetClass.getMethods();
		Integer bizId = null;//此处可以根据你想要的类型来修改
		for (Method m : method) {  
            if (m.getName().equals(methodName)) {  
                Class[] tmpCs = m.getParameterTypes();  
                if (tmpCs.length == arguments.length) {  
                    ControllerLogAnnotation cla = m.getAnnotation(ControllerLogAnnotation.class);  
                    if (cla != null) {  
                        bizId = cla.bizID();  
                    }  
                    break;  
                }  
            }  
        }  
        return bizId;  
    }  
}

另外还有一些工具类的方法

这个是转换成map对象的方法

	public static Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args)throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();

		ClassPool pool = ClassPool.getDefault();
		ClassClassPath classPath = new ClassClassPath(cls);
		pool.insertClassPath(classPath);

		CtClass cc = pool.get(clazzName);
		CtMethod cm = cc.getDeclaredMethod(methodName);
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		if (attr == null) {
			// exception
		}
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < cm.getParameterTypes().length; i++) {
			map.put(attr.variableName(i + pos), args[i]);// paramNames即参数名
		}
		return map;
	}

最后在springMVCxml中加入

<context:component-scan
        //LogAspect 在aop中
		base-package="切面所在的包例如(a.b.aop)"></context:component-scan>

	<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>

另外还有一些依赖的jar包,需要的jar包都可以在百度中搜索到,用的时候没有可以在百度查询jar包依赖,放在maven项目中的pom.xml文件中。

 

 

PS:后续发现使用事物对接口进行处理更加方便,直接在操作的时候添加日志,接口报错事物自动回滚即可。

  • 6
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 使用SpringAOP可以实现缓存功能。具体实现方式是,在需要缓存的方法上添加@Cacheable注解,指定缓存的key和缓存的名称。当方法被调用时,Spring会先检查缓存是否存在该key对应的缓存数据,如果存在则直接返回缓存数据,否则执行方法并将返回结果缓存起来。同时,还可以使用@CachePut注解更新缓存数据,或者使用@CacheEvict注解清除缓存数据。这样可以有效地提高系统性能和响应速度。 ### 回答2: 使用SpringAOP实现缓存功能的步骤如下: 1. 首先,需要在Spring配置文件启用AOP功能,可以通过添加`<aop:aspectj-autoproxy/>`来实现。 2. 然后,创建一个用于缓存方法调用结果的类,该类需要实现`org.springframework.cache.Cache`接口,并提供对缓存的读取、写入、删除等操作方法。 3. 还需要创建一个切面类,该类需要使用`@Aspect`注解进行标记,并在需要缓存的方法上添加`@Cacheable`注解。在切面类使用`@Before`和`@After`等注解来定义缓存操作的切点和通知。 4. 在Spring配置文件,将切面类声明为一个bean,并在`<aop:config>`指定要应用缓存的方法和切面。 5. 最后,配置`ehcache.xml`(或其他缓存配置文件),并将其指定为Spring配置文件缓存管理器的实现类,例如`<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"/>`。 这样,当被缓存的方法被调用时,AOP将切入切面类的通知方法,首先查询缓存是否存在该方法的结果,如果存在,则直接返回缓存的结果;如果不存在,则调用原始方法,并将结果存入缓存。在后续的调用,如果参数相同,则直接从缓存获取结果,从而减少了对原始方法的调用,提高了系统的性能和响应速度。 使用SpringAOP实现缓存功能可以大大简化代码,提高项目的可维护性和可扩展性,同时还能通过缓存数据减少对数据库等资源的访问,提升系统整体的性能。 ### 回答3: 使用SpringAOP可以很方便地实现缓存功能。AOP(面向切面编程)是一种编程范式,通过在程序运行时动态地将横切逻辑(如日志记录、事务管理、异常处理等)插入到应用程序的特定位置,以提供更好的代码结构和模块化。 在使用SpringAOP实现缓存功能时,我们可以通过以下步骤来实现: 1. 定义一个缓存注解:可以使用Spring提供的@Cacheable注解来定义缓存的方法。这个注解可以应用在方法上,用于标记被缓存的方法。 2. 配置缓存切面:通过AOP切面配置,将缓存注解和具体的缓存实现关联起来。可以使用Spring的@Aspect注解来定义一个切面类,该类可以包含多个增强方法用于处理缓存操作。 3. 配置缓存策略:在切面类,可以通过使用Spring的缓存管理器(如Ehcache、Redis等)来定义缓存的具体策略。可以配置缓存的过期时间、缓存的存储位置等。 4. 在目标方法使用缓存注解:在需要被缓存的方法上添加之前定义的缓存注解。当方法被调用时,AOP切面会先检查缓存是否存在对应的缓存数据,如果存在则直接返回缓存数据,否则执行方法逻辑并将结果存入缓存。 5. 测试缓存功能:执行目标方法,观察是否从缓存获取数据以及方法执行的时间。 通过这种方式,我们可以很方便地在应用加入缓存功能,提高系统性能和响应速度。同时,由于使用AOP的方式,可以很好地解耦和复用缓存相关的代码逻辑。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值