AOP使用场景——mybatis之mapper执行A表删除前,保存到B表

需求:每次删除A表的数据,我们都希望去保留数据,记录到B表。

很多时候我们是这么做的

public void delete(id) {
    UserLogMapper.insert(UserBean);//记录日志
    UserMapper.delete(id);//删除记录
}

这样还得要加上事务,什么的。但是别忘记了,UserMapper里的方法如果在别的地方调用了,别人也不知道你的业务逻辑,直接调用UserMapper.delete(id);而没有记录日志。那么就破坏了原有的业务逻辑。

所以我们是在别人调用UserMapper.delete(id);之前调用UserLogMapper.insert(UserBean);。我们想到了AOP

现在我们用AOP,无侵入方式解决这个问题。

思路:用SpringAOP

我们已经有一个mapper类(mybatis)如下:

public interface UserMapper{

    int delete(@Param("id") Long id);

}

超级普通的,mybatis这样的接口,用代理去实现。我们也能对这个接口的方法进行拦截。mybatis xml 如下:

    
        delete from user
        where  id=#{id,jdbcType=BIGINT}
    

1、建一个注释

import java.lang.annotation.*;

@Target(ElementType.METHOD)                    //支持注解在方法上
@Retention(RetentionPolicy.RUNTIME)            //支持运行时可用
@Documented                                    //DOC文档可见
@Inherited
public @interface DatabaseAfterAspects {

    Class processorClass();//调用方法前先调用这个类

    String method();//调用这个类的这个方法
}

2、AOP切面,这个切面拦截以上的注释的方法后,调用注释传进来的类和方法

package com.ea.utils.aop;

import com.ea.utils.SpringContextUtils;
import org.apache.ibatis.annotations.Param;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
public class DatabaseAspectsAop {

    @Pointcut("@annotation(com.ea.utils.aop.DatabaseAspects)")
    private void databaseCalculate() {
    }

    /**
     * 拦截方法,并调用processor相同的方法
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("databaseCalculate()")
    public Object databaseCalculate(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class[] type = signature.getMethod().getParameterTypes();
        Annotation[] annotations = signature.getMethod().getAnnotations();
//        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        //拦截方法参数
//        Map paraMap = this.getParamMap(paramAnnotations, args);
        for (Annotation annotation : annotations) {
            if (annotation instanceof DatabaseAspects) {
                DatabaseAspects databaseCalculate = (DatabaseAspects) annotation;
                Class processorName = databaseCalculate.processorClass();
                String processMethod = databaseCalculate.method();
                Object object = SpringContextUtils.getApplicationContext().getBean(processorName);
                springInvokeMethod(object, processMethod, args, type);
            }
        }
        return joinPoint.proceed();
    }

    /**
     * @param service
     *            服务
     * @param methodName
     *            方法名称
     * @param params
     *            参数
     * @return
     * @throws Exception
     */
    public static Object springInvokeMethod(Object service, String methodName, Object[] params, Class[] type) {
        // 找到方法
        Method method = ReflectionUtils.findMethod(service.getClass(), methodName, type);
        // 执行方法
        return ReflectionUtils.invokeMethod(method, service, params);
    }

    /**
     * 获取需要的参数(@PARAM)
     * @param paramAnnotations
     * @param args
     * @return
     */
    private Map getParamMap(Annotation[][] paramAnnotations, Object[] args) {
        // 该集合用于记录参数索引与参数名称的对应关系
        Map map = new HashMap<>();
        int paramCount = paramAnnotations.length;
        // 遍历所有参数
        for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
            String name = null;
            // 遍历该参数上的注解集合
            for (Annotation annotation : paramAnnotations[paramIndex]) {
                if (annotation instanceof Param) {
                    // 获取@Param注解指定的参数名称
                    name = ((Param) annotation).value();
                    break;
                }
            }
            if(null == name) {
                name = String.valueOf(paramIndex);
            }
            map.put(name, args[paramIndex]);
        }
        return map;
    }
}

Application获取类

package com.ea.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtils implements ApplicationContextAware {

	 /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param 
     * @return
     */
    public static  T getBean(Class clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param 
     * @return
     */
    public static  T getBean(String name, Class clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

	
}

  

 

3、建记录日志类

@Service
public class UserLogService {

    private UserMapper userMapper;

    private UserLogMapper userLogMapper;

    int insert(long id) {
        userLogMapper.insert(userMapper.select(id));
    }

}

XML(略)

4、配置

public interface UserMapper{

    @DatabaseAspects(processorClass=UserLogService.class, method="insert")
    int delete(@Param("id") Long id);

}

总结:

1、mybatis的接口类也可以进行拦截。

2、其实以上思路很简单,就是A类执行时候调用B类。其实B类可以也是mapper,只要入参一样就行。那么B类的SQL写成

insert B select A 这种形式就行。这样就可以省去service类的麻烦。

以上仅提供思路,代码可能有点瑕疵!

 

 

 

你可能感兴趣的:(Spring,JAVA)