Spring AOP使用时的一些问题

在使用AOP的时候遇到了一些问题,特此记录一下

首先写一个常用的AOP切片

切片类AopLog

package com.mantis.aop.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mantis.aop.common.util.DataUtil;
import eu.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description:执行顺序 正常顺序    Around  Before  Method.invoke   Around  After   AfterReturning
 * 异常顺序    Around  Before  Method.invoke   After   AfterThrowing
 * @author: wei.wang
 * @since: 2020/4/4 13:47
 * @history: 1.2020/4/4 created by wei.wang
 */
@Aspect
@Component
public class AopLog {

    private static Logger logger = LoggerFactory.getLogger(AopLog.class);

    /**
     * 定义切点,切点为com.smec.fin.controller包和子包里任意方法的执行和service层所有方法的执行
     */
    @Pointcut("execution(public * com.mantis.aop.controller..*.*(..))")
    public void log() {
        // 定义切点
    }

    /**
     * 定义切点,切点为com.smec.fin.controller包和子包里任意方法的执行和service层所有方法的执行
     */
    @Pointcut("execution(public * com.mantis.aop.service.impl..*.*(..))")
    public void log2() {
        // 定义切点
    }

    /**
     * 环绕通知
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("log2()")
    public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
        logger.info("开始执行环绕操作");
        Object result = point.proceed();
        logger.info("执行环绕操作结束,返回值:{}", result);
        return result;
    }
}

服务类AopServiceImpl

package com.mantis.aop.service.impl;

import com.mantis.aop.service.AopService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Description:
 * @author: wei.wang
 * @since: 2020/10/24 12:45
 * @history: 1.2020/10/24 created by wei.wang
 */
@Service
public class AopServiceImpl implements AopService {


    @Override
    public void testAop(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testAop" + str);
        this.testAop2("testFinalMethod");
    }

    @Override
    public void testAop2(String str) {
        //this.testFinalMethod("testFinalMethod");
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl." + str);
    }

    public final void testFinalMethod(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod" + str);
    }

    public void testFinalMethod2(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod" + str);
    }
}

1.同方法运行问题

在使用AOP时我们发现存在同类中调用时切点失效问题,在执行时我们发现只执行了testAop的切点,testAop2的切点没有执行,这是因为经过AOP代理后的对象都已经不是原来的对象了,而是加入了增强方法的代理对象,使用代理对象调用时可以执行增强方法,但是这里是使用this调用的,也就是AopServiceImpl,因为不是代理对象就没有增强方法。

2020-10-24 13:31:06.261  INFO 13344 --- [nio-9000-exec-1] com.mantis.aop.controller.AopController  : 方法开始执行
2020-10-24 13:31:06.264  INFO 13344 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testAoptest2
com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod

2020-10-24 13:31:06.274  INFO 13344 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

用@Autowired或Resource引入自身依赖

使用注解方法引入自身代理依赖,注意使用构造的方式会有循环依赖问题

    @Autowired
    AopServiceImpl aopService;

    @Override
    public void testAop(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testAop" + str);
        aopService.testAop2("testFinalMethod");
        System.out.println();
    }
2020-10-24 13:36:33.477  INFO 12528 --- [nio-9000-exec-1] com.mantis.aop.controller.AopController  : 方法开始执行
2020-10-24 13:36:33.480  INFO 12528 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testAoptest2
2020-10-24 13:36:33.488  INFO 12528 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod
2020-10-24 13:36:33.488  INFO 12528 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

2020-10-24 13:36:33.490  INFO 12528 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

开启暴露代理类,AopContext.currentProxy()方式获取代理类

通过暴露代理类方式,实际原理是把代理类添加到当前请求的ThreadLocal里面,然后在使用时从ThreadLocal中获取代理类,再调用对应的方法

在主方法上加入@EnableAspectJAutoProxy(exposeProxy=true),然后从AOP上下文中获取代理

    @Override
    public void testAop(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testAop" + str);
        AopServiceImpl service = AopContext.currentProxy() != null ? (AopServiceImpl) AopContext.currentProxy() : this;
        service.testAop2("testFinalMethod");
        System.out.println();
    }
2020-10-24 13:38:31.031  INFO 18828 --- [nio-9000-exec-1] com.mantis.aop.controller.AopController  : 方法开始执行
2020-10-24 13:38:31.035  INFO 18828 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testAoptest2
2020-10-24 13:38:31.047  INFO 18828 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod
2020-10-24 13:38:31.048  INFO 18828 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

2020-10-24 13:38:31.050  INFO 18828 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

2.final关键字问题

Spring AOP默认使用cglib,会生成目标对象的子类代理对象。调用目标对象的方法,实际上是调用代理对象的方法。由于子类能够继承父类的方法,因此一般情况下目标类的方法,代理对象都会有。但是当目标类中某个方法带有final关键字时,这个方法不能被重写,因此代理对象中没有这个方法,因此会调用目标对象的方法。

带final关键字

因为testFinalMethod方法中存在final关键字,导致无法重写,结果代理对象就无法生成这个方法,因此调用目标对象方法,导致没有被增强

    @Override
    public void testAop(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testAop" + str);
        AopServiceImpl service = AopContext.currentProxy() != null ? (AopServiceImpl) AopContext.currentProxy() : this;
        service.testFinalMethod("testFinalMethod");
        System.out.println();
    }
    
    public final void testFinalMethod(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod" + str);
    }
2020-10-24 13:47:46.907  INFO 15204 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testAoptest2
com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethodtestFinalMethod

2020-10-24 13:47:46.916  INFO 15204 --- [nio-9000-exec-1] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

不带final关键字

因为testFinalMethod方法中不存在final关键字,所以正常执行增强。

    @Override
    public void testAop(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testAop" + str);
        AopServiceImpl service = AopContext.currentProxy() != null ? (AopServiceImpl) AopContext.currentProxy() : this;
        service.testFinalMethod2("testFinalMethod");
        System.out.println();
    }
    
    public final void testFinalMethod2(String str) {
        System.out.println("com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethod" + str);
    }
2020-10-24 13:50:51.018  INFO 13532 --- [nio-9000-exec-2] com.mantis.aop.controller.AopController  : 方法开始执行
2020-10-24 13:50:51.021  INFO 13532 --- [nio-9000-exec-2] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testAoptest2
2020-10-24 13:50:51.029  INFO 13532 --- [nio-9000-exec-2] com.mantis.aop.aspect.AopLog             : 开始执行环绕操作
com.mantis.aop.service.AopService.AopServiceImpl.testFinalMethodtestFinalMethod
2020-10-24 13:50:51.030  INFO 13532 --- [nio-9000-exec-2] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

2020-10-24 13:50:51.031  INFO 13532 --- [nio-9000-exec-2] com.mantis.aop.aspect.AopLog             : 执行环绕操作结束,返回值:null

你可能感兴趣的:(aop,spring,springboot)