美文网首页
Spring AOP学习笔记

Spring AOP学习笔记

作者: wch853 | 来源:发表于2018-01-22 21:31 被阅读32次

    AOP

    AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,与OOP互为补充,初衷是为了解决代码重复问题以及将功能性代码与非功能性代码分离。使用AOP可以集中处理某一关注点,方便地添加/删除关注点。AOP的侵入性也很小。

    AOP的应用场景

    • 权限控制
    • 缓存控制
    • 事务控制
    • 审计日志
    • 性能监控
    • 分布式追踪
    • 异常处理

    AOP实例

    以下模拟新增商品操作,该操作的前提是当前用户为"admin"。

    模拟用户切换
    public class CurrentUserHolder {
    
        private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
    
        public static String get() {
            return HOLDER.get() == null ? "unknown" : HOLDER.get();
        }
    
        public static void set(String user) {
            HOLDER.set(user);
        }
    }
    
    验证用户身份
    @Component
    public class AuthUtil {
    
        /**
         * 验证用户
         */
        public void checkAccess() {
            String user = CurrentUserHolder.get();
            if (!"admin".equals(user)) {
                System.out.println("check access failed.");
                throw new RuntimeException("operation not allow.");
            }
            System.out.println("check access success.");
        }
    }
    
    编制切面

    @Aspect注解用于标注切面,@Pointcut注解表示切入点,通过切入点表达式标注在哪些类的哪些方法织入,@Before等注解标注了通知(Advice)执行的时机。

    @Aspect
    @Component
    public class AnnotationAspect {
    
        @Resource
        private AuthUtil authUtil;
    
        /**
         * 切入点
         */
        @Pointcut("@annotation(com.wch.test.aop.aspectj.AdminOnly)")
        public void adminOnly() {
        }
    
        @Before("adminOnly()")
        public void check() {
            authUtil.checkAccess();
        }
    }
    
    自定义注解

    用于标注在需要有"admin"身份才能访问的方法上。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface AdminOnly {
    }
    
    商品操作
    @Service
    public class ProductService {
    
        // @Resource
        // private AuthService authService;
    
        @AdminOnly
        public void insertProduct(Product product) {
            // 不使用aop:在每次需要验证时都手动调用checkAccess()方法
            // authService.checkAccess();
            System.out.println("insert product.");
        }
    }
    

    切入点表达式

    • 通配符
      *:匹配任意数量的字符
      +:匹配指定类及其子类
      ..:匹配子包和参数
    • 运算符
      &&:与操作符
      ||:或操作符
      !:非操作符
    • 指示器
      匹配方法:excution()
      匹配注解:@annotation()
      匹配包/类型:within()
      匹配对象:this() / target() / bean()
      匹配参数:args()
    使用within()匹配包/类型
    @Aspect
    @Component
    public class WithinAspect {
    
        /**
         * 匹配类型
         */
        @Pointcut("within(com.wch.test.aop.aspectj.service.impl.AopProductServiceImpl)")
        public void matchType() {
        }
    
        @Before("matchType()")
        public void before() {
            System.out.println("### before type.");
        }
    
        /**
         * 匹配包(通过..扩展匹配子包)
         */
        @Pointcut("within(com.wch.test.aop.aspectj.service..*)")
        public void matchPackage() {
        }
    
        @After("matchPackage()")
        public void after() {
            System.out.println("### after package.");
        }
    }
    
    使用this() / target() / bean()匹配对象
    @Aspect
    @Component
    public class ObjectAspect {
    
        /**
         * 匹配对象
         */
        @Pointcut("this(com.wch.test.aop.aspectj.service.impl.AopProductServiceImpl)")
        public void matchThis() {
        }
    
        @Before("matchThis()")
        public void before() {
            System.out.println("### before this.");
        }
    
        /**
         * 匹配实现指定接口的对象
         */
        @Pointcut("target(com.wch.test.aop.aspectj.service.ProductService)")
        public void matchTarget() {
        }
    
        @After("matchTarget()")
        public void after() {
            System.out.println("### after target.");
        }
    
        /**
         * 匹配以指定字符串为bean名称结尾的bean
         */
        @Pointcut("bean(*aopProductServiceImpl)")
        public void matchBean() {
        }
    
        @After("matchBean()")
        public void around() {
            System.out.println("### after bean.");
        }
    }
    
    使用args() 匹配指定参数的方法
    @Aspect
    @Component
    public class ArgsAspect {
    
        /**
         * 匹配含有指定参数的方法
         */
        @Pointcut("args(Long, ..) && within(com.wch.test.aop.aspectj.service..*)")
        public void matchArgs() {
        }
    
        @Before("matchArgs()")
        public void before() {
            System.out.println("### before args.");
        }
    
        /**
         * 通过args()获取参数,与before结合可用于检验参数
         *
         * @param id id
         */
        @Before("matchArgs() && args(id)")
        public void getArgsBefore(Long id) {
            System.out.println("### before get args, id = " + id + ".");
        }
    }
    
    使用注解匹配
    @Aspect
    @Component
    public class AnnotationAspect {
    
        @Resource
        private AuthUtil authUtil;
    
        /**
         * 匹配有指定注解的方法
         */
        @Pointcut("@annotation(com.wch.test.aop.aspectj.AdminOnly)")
        public void adminOnly() {
        }
    
        @Before("adminOnly()")
        public void before() {
            authUtil.checkAccess();
        }
    
        /**
         * 匹配有指定注解的类,且注解的RetentionPolicy为CLASS
         */
        @Pointcut("@within(com.wch.test.annotation.custom.Beta)")
        public void within() {
        }
    
        /**
         * 匹配有指定注解的类,且注解的RetentionPolicy为RUNTIME
         */
        @Pointcut("@target(org.springframework.jmx.export.annotation.ManagedResource)")
        public void target() {
        }
    
        /**
         * 匹配传入参数含指定注解的方法
         */
        @Pointcut("@args(org.springframework.stereotype.Repository)")
        public void args() {
        }
    }
    
    使用execution()匹配指定返回值、方法名、参数的方法
    @Aspect
    @Component
    public class ExecutionAspect {
    
        /**
         * 匹配指定返回值、方法名、参数的方法
         */
        @Pointcut("execution(public void *..delete*(Long)) throws java.lang.RuntimeException")
        public void matchExecution() {
        }
    
        @After("matchExecution()")
        public void after() {
            System.out.println("### after execution.");
        }
    }
    
    五种Advice注解
    • @Before:前置通知
    • @After:后置通知(方法执行完毕)
    • @AfterReturning:返回通知(成功执行后)
    • @AfterThrowing:异常通知(抛出异常后)
    • @Around:环绕通知
    @AfterReturning和@Around的特殊用法
    @Pointcut("execution(public String *..AopProduct*..query*(Long))")
        public void matchQueryMethod() {
        }
    
        /**
         * after returning
         *
         * @param result 获取的指定方法的返回值
         */
        @AfterReturning(value = "matchQueryMethod()", returning = "result")
        public void afterReturning(Object result) {
            System.out.println("### after returning, result: " + result + ".");
        }
    
        @Pointcut("execution(public boolean *..AopProduct*..update*(Long))")
        public void matchUpdateMethod() {
        }
    
        /**
         * around
         */
        @Around("matchUpdateMethod()")
        public Object around(ProceedingJoinPoint joinPoint) {
            System.out.println("### before.");
            Object result = null;
            try {
                result = joinPoint.proceed(joinPoint.getArgs());
                System.out.println("### after returning");
            } catch (Throwable e) {
                System.out.println("### after throwing, " + e.getMessage());
            } finally {
                System.out.println("### after");
            }
            return result;
        }
    }
    

    Spring AOP实现原理

    织入的时机
    • 编译期(AspectJ)
    • 类加载时(AspectJ 5+)
    • 运行时(Spring AOP)
    代理模式

    调用方通过代理对象来调用目标对象,在代理的过程中,代理对象可以在执行目标对象的过程中额外增加一些操作。

    代理模式中代理对象和目标对象都需要实现的接口
    public interface Subject {
    
        void request();
    }
    
    目标对象
    public class RealSubject implements Subject {
    
        @Override
        public void request() {
            System.out.println("real subject execute request.");
        }
    }
    
    代理对象
    public class Proxy implements Subject {
    
        /**
         * 将目标对象委托给代理对象
         */
        private RealSubject realSubject;
    
        public Proxy(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        /**
         * 代理类实现相同方法来对目标对象进行增强
         */
        @Override
        public void request() {
            System.out.println("### before.");
            try {
                realSubject.request();
            } catch (Exception e) {
                System.out.println("ex: " + e.getMessage());
                throw e;
            } finally {
                System.out.println("### after.");
            }
        }
    }
    
    调用方
    public class Client {
    
        public static void main(String[] args) {
            Subject subject = new Proxy(new RealSubject());
            subject.request();
        }
    }
    

    动态代理

    动态代理相对于静态代理的优势在于,当接口中新增方法时,动态代理不必像静态代理需要在目标对象和代理对象中实现该方法,从而提升效率。

    JDK动态代理
    动态代理类,相当于AOP的aspect
    public class JDKProxySubject implements InvocationHandler {
    
        private RealSubject realSubject;
    
        public JDKProxySubject(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        /**
         * 基于反射的动态代理
         *
         * @param proxy  方法反射的代理对象
         * @param method 对象方法
         * @param args   方法参数
         * @return 经过额外操作的方法返回值
         * @throws Throwable Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("### before.");
            Object result;
            try {
                result = method.invoke(realSubject, args);
            } catch (Exception e) {
                System.out.println("ex: " + e.getMessage());
                throw e;
            } finally {
                System.out.println("### after.");
            }
            return result;
        }
    }
    
    调用方
    public class JDKProxyClient {
    
        public static void main(String[] args) {
            Subject subject = (Subject) Proxy.newProxyInstance(JDKProxyClient.class.getClassLoader(),
                    new Class[]{Subject.class}, new JDKProxySubject(new RealSubject()));
            subject.request();
        }
    }
    
    CGLIB动态代理
    基于CGLIB的动态代理
    public class CglibMethodInterceptor implements MethodInterceptor {
    
        /**
         * 通过继承来实现代理
         *
         * @param o           目标对象
         * @param method      目标对象方法
         * @param args        方法参数
         * @param methodProxy 方法代理
         * @return 经过额外操作的方法返回值
         * @throws Throwable Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("### before.");
            Object result;
            try {
                result = methodProxy.invokeSuper(o, args);
            } catch (Exception e) {
                System.out.println("ex: " + e.getMessage() + ".");
                throw e;
            } finally {
                System.out.println("### after.");
            }
            return result;
        }
    }
    
    调用方
    public class CglibProxyClient {
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(RealSubject.class);
            enhancer.setCallback(new CglibMethodInterceptor());
            Subject subject = (Subject) enhancer.create();
            subject.request();
        }
    }
    
    JDK动态代理与CGLIB动态代理的区别
    • JDK动态代理只针对有接口的类的方法进行动态代理,无法对private方法代理
    • CGLIB基于继承来实现代理,无法对static、final类代理,无法对private、static方法进行代理
    Spring AOP对两种代理的选择方式
    • 如果目标对象实现了接口,则默认采用JDK动态代理
    • 如果目标对象没有实现接口,则采用CGLIB进行动态代理
    • 如果DefaultAopProxyFactory中配置proxyTargetClass = true,则采用CGLIB进行动态代理

    AOP的链式调用

    责任链模式
    链式处理器抽象类
    public abstract class ChainHandler {
    
        /**
         * 继任者
         */
        private ChainHandler successor;
    
        public ChainHandler getSuccessor() {
            return successor;
        }
    
        public void setSuccessor(ChainHandler successor) {
            this.successor = successor;
        }
    
        public void execute() {
            this.handleProcess();
            if (null != successor) {
                // 存在继任者,接替执行execute()方法,实现链式调用
                successor.execute();
            }
        }
    
        protected abstract void handleProcess();
    }
    
    调用方
    public class ChainClient {
    
        static class ChainHandlerA extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle a.");
            }
        }
    
        static class ChainHandlerB extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle b.");
            }
        }
    
        static class ChainHandlerC extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle c.");
            }
        }
    
        public static void main(String[] args) {
            ChainHandlerA handlerA = new ChainHandlerA();
            ChainHandlerB handlerB = new ChainHandlerB();
            ChainHandlerC handlerC = new ChainHandlerC();
    
            // 将处理器B设置为处理器A的继任者,在A执行后接替执行
            handlerA.setSuccessor(handlerB);
            // 将处理器C设置为处理器B的继任者,在B执行后接替执行
            handlerB.setSuccessor(handlerC);
    
            // 启动执行A
            handlerA.execute();
        }
    }
    
    改进的责任链模式

    可以看出以上的责任链模式需要不断去指定处理器的“继任者”,需要进行改良。

    链式处理器抽象类
    public abstract class ChainHandler {
    
        /**
         * 链式处理器执行方法
         *
         * @param chain 链式处理器所在处理器链
         */
        public void execute(ProcessChain chain) {
            // 执行处理器本身的逻辑
            this.handleProcess();
            // 通知处理器所在的处理器链继续执行下一个处理器
            chain.proceed();
        }
    
        protected abstract void handleProcess();
    }
    
    执行链
    public class ProcessChain {
    
        /**
         * 链式处理器集合
         */
        private List<ChainHandler> chainHandlers;
    
        /**
         * 当前执行顺序
         */
        private int index = 0;
    
        public ProcessChain(List<ChainHandler> chainHandlers) {
            this.chainHandlers = chainHandlers;
        }
    
        /**
         * 执行链核心处理方法
         */
        public void proceed() {
            if (index < chainHandlers.size()) {
                // 按序处理链式处理器
                chainHandlers.get(index++).execute(this);
            }
        }
    }
    
    调用方
    public class ChainClient {
    
        static class ChainHandlerA extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle a.");
            }
        }
    
        static class ChainHandlerB extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle b.");
            }
        }
    
        static class ChainHandlerC extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle c.");
            }
        }
    
        public static void main(String[] args) {
            ChainHandlerA handlerA = new ChainHandlerA();
            ChainHandlerB handlerB = new ChainHandlerB();
            ChainHandlerC handlerC = new ChainHandlerC();
    
            ProcessChain chain = new ProcessChain(Arrays.asList(handlerA, handlerB, handlerC));
            chain.proceed();
        }
    }
    

    AOP应用 —— 实现日志记录功能

    将JPA的save(增加或修改)、delete(删除)方法作为切入点,比较增删改操作的前后变更项,将变更记录插入数据库,实现日志功能。

    产品订单
    @Entity
    @Table(name = "product_order")
    public class ProductOrder {
    
        @Id
        @GeneratedValue
        private Long id;
    
        /**
         * 产品名称
         */
        private String productName;
    
        /**
         * 订单价格
         */
        private BigDecimal orderPrice;
    
        /**
         * 下单时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date createTime;
    
        /**
         * 修改时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date updateTime;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getProductName() {
            return productName;
        }
    
        public void setProductName(String productName) {
            this.productName = productName;
        }
    
        public BigDecimal getOrderPrice() {
            return orderPrice;
        }
    
        public void setOrderPrice(BigDecimal orderPrice) {
            this.orderPrice = orderPrice;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        @Override
        public String toString() {
            return "ProductOrder{" +
                    "id=" + id +
                    ", productName='" + productName + '\'' +
                    ", orderPrice=" + orderPrice +
                    ", createTime=" + createTime +
                    ", updateTime=" + updateTime +
                    '}';
        }
    }
    
    变更项
    public class ChangeItem {
    
        /**
         * 变更属性名
         */
        private String field;
    
        /**
         * 变更旧值
         */
        private String oldValue;
    
        /**
         * 变更新值
         */
        private String newValue;
    
        public String getField() {
            return field;
        }
    
        public void setField(String field) {
            this.field = field;
        }
    
        public String getOldValue() {
            return oldValue;
        }
    
        public void setOldValue(String oldValue) {
            this.oldValue = oldValue;
        }
    
        public String getNewValue() {
            return newValue;
        }
    
        public void setNewValue(String newValue) {
            this.newValue = newValue;
        }
    }
    
    数据操作记录类型
    public enum DataOperationLogType {
    
        INSERT(1, "新增"),
    
        UPDATE(2, "修改"),
    
        DELETE(3, "删除");
    
        private int code;
    
        private String info;
    
        DataOperationLogType(int code, String info) {
            this.code = code;
            this.info = info;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getInfo() {
            return info;
        }
    }
    
    日志记录
    @Entity
    @Table(name = "data_operation_log")
    public class DataOperationLog {
    
        @Id
        @GeneratedValue
        private Long id;
    
        /**
         * 数据操作记录来源
         */
        private String operateMethod;
    
        /**
         * 被操作的数据记录id
         */
        private Long dataId;
    
        /**
         * 数据操作时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date operateTime;
    
        /**
         * 数据操作类型
         */
        private int dataOperationLogType;
    
        /**
         * 数据操作变更项集合
         */
        private String changeItems;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getOperateMethod() {
            return operateMethod;
        }
    
        public void setOperateMethod(String operateMethod) {
            this.operateMethod = operateMethod;
        }
    
        public Long getDataId() {
            return dataId;
        }
    
        public void setDataId(Long dataId) {
            this.dataId = dataId;
        }
    
        public Date getOperateTime() {
            return operateTime;
        }
    
        public void setOperateTime(Date operateTime) {
            this.operateTime = operateTime;
        }
    
        public int getDataOperationLogType() {
            return dataOperationLogType;
        }
    
        public void setDataOperationLogType(DataOperationLogType dataOperationLogType) {
            this.dataOperationLogType = dataOperationLogType.getCode();
        }
    
        public String getChangeItems() {
            return changeItems;
        }
    
        public void setChangeItems(List<ChangeItem> changeItems) {
            this.changeItems = JsonUtils.toJsonString(changeItems);
        }
    }
    
    对象属性值变更比较工具
    public class FieldValueDiffUtils {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(FieldValueDiffUtils.class);
    
        /**
         * 自定义JpaRepository中根据id获取对象的方法为 findOne(需要重写)
         * (继承自org.springframework.data.repository.CrudRepository)
         */
        private static final String ATTAIN_OLD_OBJECT_METHOD = "findOne";
    
        /**
         * getter方法前缀
         */
        private static final String GETTER_METHOD_PREFIX = "get";
    
        /**
         * 变更项默认值
         */
        private static final String DEFAULT_ITEM_VALUE = "";
    
        /**
         * 是否过滤Bean中属性值为null的情况
         */
        private static final boolean IS_FILTER_NULL_BEAN_VALUE = Boolean.TRUE;
    
        /**
         * 通过反射执行自定义JpaRepository#findOne方法,获取对应id的记录
         *
         * @param target 目标对象
         * @param id     记录id
         * @return 记录
         */
        public static Object getOldObject(Object target, Long id) {
            Object obj = null;
            try {
                Method method = target.getClass().getMethod(ATTAIN_OLD_OBJECT_METHOD, Long.class);
                obj = method.invoke(target, id);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                LOGGER.error("get old object exception: {}", e.getMessage());
            }
            return obj;
        }
    
        /**
         * 获取指定属性的属性值
         *
         * @param object    目标Bean
         * @param fieldName 属性名称
         * @return 属性值
         */
        private static Object getFieldValue(Object object, String fieldName) {
            Object fieldValue = null;
            if (null != object) {
                Class<?> clazz = object.getClass();
                try {
                    // 获取该属性getter方法
                    Method method = clazz.getMethod(GETTER_METHOD_PREFIX.
                            concat(fieldName.substring(0, 1).toUpperCase()).
                            concat(fieldName.substring(1)));
                    // 通过反射获取属性值
                    fieldValue = method.invoke(object);
                } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                    LOGGER.error("get field value exception: {}", e.getMessage());
                }
            }
            return fieldValue;
        }
    
        /**
         * 获取Bean中的属性和值
         *
         * @param object       目标Bean
         * @param isFilterNull {@code true} 过滤值为null的属性,{@code false} 不过滤
         * @return field-value map
         */
        private static Map<String, String> getBeanFieldValueMap(Object object, boolean isFilterNull) {
            Map<String, String> fieldValueMap = new HashMap<>();
            if (null != object) {
                // 获取该Bean的类对象
                Class<?> clazz = object.getClass();
                // 该类的属性集合
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    // 属性名
                    String fieldName = field.getName();
                    // 属性值
                    Object fieldValue = getFieldValue(object, fieldName);
                    if (!isFilterNull || null != fieldValue) {
                        fieldValueMap.put(fieldName, JsonUtils.toJsonString(fieldValue));
                    }
                }
            }
            return fieldValueMap;
        }
    
        /**
         * 新增操作,新值整体作为变更项
         *
         * @param object object
         * @return 变更项集合
         */
        public static List<ChangeItem> getInsertChangeItems(Object object) {
            ChangeItem changeItem = new ChangeItem();
            changeItem.setField(DEFAULT_ITEM_VALUE);
            changeItem.setOldValue(DEFAULT_ITEM_VALUE);
            changeItem.setNewValue(JsonUtils.toJsonString(object));
            return Collections.singletonList(changeItem);
        }
    
        /**
         * 修改操作,比较新原值,获取变更项集合
         *
         * @param oldObject 原值
         * @param newObject 新值
         * @return 变更项集合
         */
        public static List<ChangeItem> getUpdateChangeItems(Object oldObject, Object newObject) {
            List<ChangeItem> changeItems = new ArrayList<>();
            // 获取类对象
            Map<String, String> oldFieldValueMap = getBeanFieldValueMap(oldObject, IS_FILTER_NULL_BEAN_VALUE);
            Map<String, String> newFieldValueMap = getBeanFieldValueMap(newObject, IS_FILTER_NULL_BEAN_VALUE);
    
            ChangeItem changeItem;
            for (Map.Entry<String, String> newFieldValueEntry : newFieldValueMap.entrySet()) {
                String fieldName = newFieldValueEntry.getKey();
                String newFieldValue = newFieldValueEntry.getValue();
                String oldFieldValue = oldFieldValueMap.get(fieldName);
                if (null == oldFieldValue || !oldFieldValue.equals(newFieldValue)) {
                    // 原值不包含此属性或属性值不同,认定为变更项
                    changeItem = new ChangeItem();
                    changeItem.setField(fieldName);
                    changeItem.setOldValue(oldFieldValue);
                    changeItem.setNewValue(newFieldValue);
                    changeItems.add(changeItem);
                }
            }
            return changeItems;
        }
    
        /**
         * 删除操作,原值整体作为变更项
         *
         * @param object 原值
         * @return 变更项集合
         */
        public static List<ChangeItem> getDeleteChangeItems(Object object) {
            ChangeItem changeItem = new ChangeItem();
            changeItem.setField(DEFAULT_ITEM_VALUE);
            changeItem.setOldValue(JsonUtils.toJsonString(object));
            changeItem.setNewValue(DEFAULT_ITEM_VALUE);
            return Collections.singletonList(changeItem);
        }
    }
    
    日志切面
    @Aspect
    @Component
    public class DataLogAspect {
    
        @Resource
        private DataOperationLogRepository dataOperationLogRepository;
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DataLogAspect.class);
    
        /**
         * ProductOrderRepository#save(ProductOrder productOrder)保存方法名(新增、修改)
         * (继承自org.springframework.data.repository.CrudRepository)
         */
        private static final String SAVE_METHOD_NAME = "save";
    
        /**
         * ProductOrderRepository#delete(Long id)删除方法名
         * (继承自org.springframework.data.repository.CrudRepository)
         */
        private static final String DELETE_METHOD_NAME = "delete";
    
        /**
         * 非法ID
         */
        private static final Long INVALID_ID = 0L;
    
        /**
         * 匹配 ProductOrderRepository#save(ProductOrder productOrder)
         */
        @Pointcut("execution(* com.wch.test.aop.spring.dao.ProductOrderRepository.save(..))")
        public void saveByEntity() {
        }
    
        /**
         * 匹配 ProductOrderRepository#delete(Long id)
         */
        @Pointcut("execution(* com.wch.test.aop.spring.dao.ProductOrderRepository.delete(..))")
        public void deleteById() {
        }
    
        /**
         * 针对ProductOrderRepository中两个方法的执行做日志记录
         *
         * @param pjp 连接点
         * @return 目标方法返回值
         */
        @Around("saveByEntity() || deleteById()")
        public Object addOperation(ProceedingJoinPoint pjp) {
            LOGGER.info("enter data log aspect");
            Object returnValue = null;
            try {
                // 获取切入方法所在的目标对象(代理对象)
                Object target = pjp.getTarget();
                // 切入方法名
                String methodName = pjp.getSignature().getName();
                // 切入方法的首个参数
                Object firstArg = pjp.getArgs()[0];
                // 日志记录对象
                DataOperationLog dataOperationLog = new DataOperationLog();
                dataOperationLog.setOperateMethod(methodName);
    
                /*
                 * 组装日志记录
                 */
                if (SAVE_METHOD_NAME.equals(methodName)) {
                    // 保存方法,参数为ProductOrder对象
                    ProductOrder productOrder = (ProductOrder) firstArg;
                    Long id = productOrder.getId();
                    if (null == id || INVALID_ID.equals(id)) {
                        // 新增
                        List<ChangeItem> insertChangeItems = FieldValueDiffUtils.getInsertChangeItems(productOrder);
                        dataOperationLog.setDataOperationLogType(DataOperationLogType.INSERT);
                        dataOperationLog.setChangeItems(insertChangeItems);
                    } else {
                        // 修改
                        Object oldObject = FieldValueDiffUtils.getOldObject(target, id);
                        List<ChangeItem> updateChangeItems = FieldValueDiffUtils.getUpdateChangeItems(oldObject, productOrder);
                        dataOperationLog.setDataOperationLogType(DataOperationLogType.UPDATE);
                        dataOperationLog.setDataId(id);
                        dataOperationLog.setChangeItems(updateChangeItems);
                    }
                } else if (DELETE_METHOD_NAME.equals(methodName)) {
                    // 删除方法,参数为id
                    Long id = (Long) firstArg;
                    Object oldObject = FieldValueDiffUtils.getOldObject(target, id);
                    List<ChangeItem> deleteChangeItems = FieldValueDiffUtils.getDeleteChangeItems(oldObject);
                    dataOperationLog.setDataOperationLogType(DataOperationLogType.DELETE);
                    dataOperationLog.setDataId(id);
                    dataOperationLog.setChangeItems(deleteChangeItems);
                }
    
                // 执行目标方法
                returnValue = pjp.proceed(pjp.getArgs());
    
                if (DataOperationLogType.INSERT.getCode() == dataOperationLog.getDataOperationLogType()) {
                    // 新增操作赋新增记录id
                    ProductOrder productOrder = (ProductOrder) returnValue;
                    dataOperationLog.setDataId(productOrder.getId());
                }
    
                dataOperationLog.setOperateTime(new Date());
                // 进行日志数据保存
                dataOperationLogRepository.save(dataOperationLog);
                LOGGER.info("log data operation: {}", JsonUtils.toJsonString(dataOperationLog));
            } catch (Throwable e) {
                LOGGER.error("data log operation exception: {}", e.getMessage());
            }
            return returnValue;
        }
    }
    
    编写测试
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ProductOrderRepositoryTest {
    
        @Resource
        private ProductOrderRepository productOrderRepository;
    
        private static final Logger LOGGER = LoggerFactory.getLogger(ProductOrderRepositoryTest.class);
    
        @Test
        public void insertTest() {
            ProductOrder productOrder = new ProductOrder();
            productOrder.setProductName("Mac Pro");
            productOrder.setOrderPrice(new BigDecimal(13999));
            Date date = new Date();
            productOrder.setCreateTime(date);
            productOrder.setUpdateTime(date);
    
            ProductOrder order = productOrderRepository.save(productOrder);
            LOGGER.info("order: {}", order);
        }
    
        @Test
        public void updateTest() {
            ProductOrder productOrder = productOrderRepository.findOne(1L);
            productOrder.setOrderPrice(new BigDecimal(14999));
            productOrder.setUpdateTime(new Date());
    
            ProductOrder order = productOrderRepository.save(productOrder);
            LOGGER.info("order: {}", order);
        }
    
        @Test
        public void deleteTest() {
            productOrderRepository.delete(1L);
        }
    }
    
    日志记录效果呈现
    日志记录

    相关文章

      网友评论

          本文标题:Spring AOP学习笔记

          本文链接:https://www.haomeiwen.com/subject/spzsnxtx.html