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")

使用该注解后就能在数据库里保存用户登录操作的相关信息了。

(完)

 

水平有限,错误的地方请多多包涵,本文仅供参考,也欢迎指出错误的地方。

posted on 2017-07-22 15:41  /***/  阅读(752)  评论(0)    收藏  举报

导航