Spring框架下使用AOP实现操作记录日志功能
1 首先定义一个注解类:
/** * 类里的module方法和method方法就是注解的参数名称 * default是声明注解参数的默认值 * 使用的时候是这样的@LogAnnotation(module = "xxx", method = "xxx") */ public @interface LogAnnotation { String module() default ""; //模块 String method() default ""; //功能
String info() default ""; //业务信息 }
然后在自定义注解上添加元注解(元注解的作用就是注解其他注解)
@Target({ElementType.PARAMETER, ElementType.METHOD}) //定义注解的作用目标(范围)
@Retention(RetentionPolicy.RUNTIME) //定义注解的保留策略
@Documented //定义该注解会加入到Javadoc中
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { String module() default ""; String method() default "";
String info() default ""; }
2 定义一个切面类(这里的是日志切面类)
新建一个类
/* * 类里可以定义自己需要的通知 */ public class LogAspect{ //前置通知before 目标类的方法执行之前执行 public void before(JoinPoint joinPoint){ } //后置通知afterReturn目标类的方法正常结束执行 public void afterReturn(JoinPoint joinPoint){ } //异常抛出通知afterThrowing目标类的方法抛出异常执行 public void afterThrowing(JoinPoint joinPoint){ } //最终通知after目标类的方法不管怎样都会执行 public void after(JoinPoint joinPoint){ } //环绕通知doAround目标类的方法执行前后执行 public Object doAround(ProceedingJoinPoint pjp){ } }
通知方法里的参数JoinPoint对象和ProceedingJoinPoint对象都可以用来获取目标类的方法的方法名,参数,返回值等等数据信息
仅供参考的日志切面类代码:
/** * 日志的切面类 * */ public class LogAspect { @Autowired private LogInfoMapper logInfoMapper; //使用环绕通知 public Object around(ProceedingJoinPoint pjp) throws Throwable{ //日志实体类对象 LogInfo logInfo = new LogInfo(); //获取当前的controller Object target = pjp.getTarget(); //获取当前的方法名 String methodName = pjp.getSignature().getName(); Signature sig = pjp.getSignature(); //判断注解是否用在方法上 if(!(sig instanceof MethodSignature )){ throw new IllegalArgumentException("该注解只能用于方法"); } //获取方法上的参数 MethodSignature msig = null; msig = (MethodSignature) sig; Class[] parameterTypes = msig.getMethod().getParameterTypes(); //方法返回值 Object object = null; //方法 Method method = null; //当前方法的参数 Object[] args = pjp.getArgs(); //方法上的第一个参数里的所有方法 Method[] argsAllMethod = args[0].getClass().getDeclaredMethods(); //遍历第一个参数里的所有方法,找到getId方法,并执行该方法返回id Integer id = null; for(Method m:argsAllMethod){ if(m.getName() == "getId"){ id = (Integer)m.invoke(args[0]); } } //使用反射获取当前的Class对象的方法 try { method = target.getClass().getMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } if(method != null){ //判断方法是否包含自定义注解,就是自定义注解 if(method.isAnnotationPresent(LogAnnotation.class)){ //获取用户信息 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); WebUser user = (WebUser)request.getSession().getAttribute("user"); if(user != null){ logInfo.setUsername(user.getUsername()); } //获取当前时间 logInfo.setTime(new Date()); //获取注解信息 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); logInfo.setModule(logAnnotation.module()); logInfo.setMethod(logAnnotation.method()); //业务id logInfo.setBusinessid(String.valueOf(id)); //业务信息 logInfo.setBusinessinfo(logAnnotation.info()); try { //保存数据库 logInfoMapper.insertSelective(logInfo); object = pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); } }else{ //没有注解 object = pjp.proceed(); } }else{ object = pjp.proceed(); } return object; } }
当编写完自己定义好的切面类后就需要通过该切面类来实现Spring AOP了,实现方式有两种:
1 基于xml配置文件方式实现
把日志切面类交给Spring容器管理
<!-- 日志切面类 --> <bean id = "logAspectBean" class = "com.xxx.util.LogAspect"/> <!-- AOP配置 --> <aop:config> <aop:aspect id = "logAspect" ref = "logAspectBean"> <!-- 切入点,就是指定什么方法能使用AOP --> <aop:pointcut id="allMethod" expression="execution(* com.xxx.controller.*.*(..))"/> <!-- logAspect切面类下的通知,method的参数要与切面类下的通知名一致 --> <aop:around method = "around" pointcut-ref="allMethod" /> <aop:………可以配置其他通知 </aop:aspect> </aop:config>
2 基于注解方式实现
先把日志切面类交给Spring容器管理,并开启Spring对AspectJ注解的支持
<!-- 日志切面类 --> <bean id = "logAspectBean" class = "com.xxx.util.LogAspect"/> <!--开启Spring对AspectJ注解的支持 --> <aop:aspectj-autoproxy/>
再在日志切面类上使用@Aspect注解,并在日志切面里定义切入点,就是提供一个方法
/* * 类里可以定义自己需要的通知 */ @Aspect public class LogAspect{ //切入点 @Pointcut("execution(* com.xxx.controller.*.*(..))") private void allMethod(){} //环绕通知doAround目标类的方法执行前后执行 //添加相应的注解来确定该通知方法属于什么通知 @Around(“allMethod()”) public Object doAround(ProceedingJoinPoint pjp){ } }
3 最后在需要记录操作的方法上添加自定义注解
@LogAnnotation(module = "userLogin", method = "用户登录" , info = "用户id")
使用该注解后就能在数据库里保存用户登录操作的相关信息了。
(完)
水平有限,错误的地方请多多包涵,本文仅供参考,也欢迎指出错误的地方。
浙公网安备 33010602011771号