aop及事物目标方法内部调用失效解决

表格引用 https://blog.csdn.net/erica_1230/article/details/51441419

序号 动态代理策略 不能被事务增强的方法
1 基于接口的动态代理 除public外的其他所有的方法,此外public static也不能被增强
2 基于CGLib的动态代理 private、static、final的方法

service代码

package com.xyy.springboot.services;

import com.xyy.springboot.domain.BaseUser;

public interface BaseService {
    /**
     * 切面日志
     */
    void aspectLog();
    void aspectLogArgs(BaseUser baseUser, int i);
}

实现类代码

该类使用@BaseTypeLog作为切点

package com.xyy.springboot.servicesImpl;

import com.xyy.springboot.configuration.BaseMethodLog;
import com.xyy.springboot.configuration.BaseTypeLog;
import com.xyy.springboot.domain.BaseUser;
import com.xyy.springboot.repository.BaseUserRepository;
import com.xyy.springboot.services.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service("baseService")
//自己写的日志注解,整个类的方法都应用
@BaseTypeLog
public class BaseServiceImpl implements BaseService{
    @Autowired
    private BaseUserRepository baseUserRepository;

    @Autowired
    private BaseService baseService;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    @BaseMethodLog
    public void aspectLog() {
        int i = 0;
        BaseUser baseUser = new BaseUser();
        baseUser.setAge(18);
        baseUser.setName("xyy");
        //如果类实现了接口会使用jdk的代理方式,否则使用cglib的方式
        //事物的实现原理同aop一样
        //spring的动态代理机制,在执行目标方法的时候,先获取目标方法的代理类,在将目标方法和增强方法转化为方法执行链,
        //在进行执行,其中目标方法的执行使用的是this.XXX这样就是直接调用方法,不会再获取调用方法的代理类,执行方法执行链,所以
        //就造成目标方法内部调用的方法无法应用aop,可以在从容器中获取代理类,在执行代理类的目标方法,当然也可以把动态代理转化为编译时
        //代理(即在编译的时候就把需要切入的方法直接编译到.class文件中)<目前不知道怎么实现[尴尬]>
        //可以实现切面方法
        baseService.aspectLogArgs(baseUser, i);
        //可以实现切面方法
        BaseServiceImpl baseServiceImpl = (BaseServiceImpl)applicationContext.getBean("baseService");
        baseServiceImpl.aspectLogArgs2(baseUser, i);
        //不可以实现切面方法
        aspectLogArgs(baseUser, i);
        //不可以实现切面方法
        aspectLogArgs2(baseUser, i);
        //不可以实现切面方法
        this.aspectLogArgs2(baseUser, i);
    }
    @Override
//    @BaseMethodLog
    public void aspectLogArgs(BaseUser baseUser, int i) {
    }
//    @BaseMethodLog
    public void aspectLogArgs2(BaseUser baseUser, int i) {
        throw new NullPointerException();
    }
}

切面类代码

package com.xyy.springboot.configuration;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * springboot自动开启aop
 */
@Aspect
@Component
public class BaseLogAspect {
    private static final Logger log = LoggerFactory.getLogger(BaseLogAspect.class);

    /**
     *  execution:用于匹配方法执行的连接点;
     *      *:匹配任何数量字符;
            ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
            +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
                 java.lang.String    匹配String类型;
                 java.*.String       匹配java包下的任何“一级子包”下的String类型;
                 如匹配java.lang.String,但不匹配java.lang.ss.String
                 java..*            匹配java包及任何子包下的任何类型;
                 如匹配java.lang.String、java.lang.annotation.Annotation
                 java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型;
                 java.lang.Number+  匹配java.lang包下的任何Number的自类型;
                 如匹配java.lang.Integer,也匹配java.math.BigInteger
        within:用于匹配指定类型内的方法执行;
        this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
        target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
        args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
        @within:用于匹配所有持有指定注解类型内的方法;
        @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
        @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
        @annotation:用于匹配当前执行方法持有指定注解的方法;
        bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
        reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
     */
//    只能注释在方法上
//    @Pointcut("@annotation(com.xyy.springboot.configuration.BaseMethodLog)")
//    注释在类上,使方法都应用
    @Pointcut("@within(com.xyy.springboot.configuration.BaseTypeLog)")
    public void pointcut(){}

    @Before("pointcut()")
    public void logStart(JoinPoint joinPoint){
        // 方法名称
        String methodName = joinPoint.getSignature().getName();
        // 方法参数
        Object[] args = joinPoint.getArgs();
        List<Object> list = Arrays.asList(args);
        log.debug(methodName + "方法运行前,参数" + list.toString());
    }

    //如果是别的类调用这个切点,需要写全类名
    //无论目标方法是否出现异常都会执行
    @After("com.xyy.springboot.configuration.BaseLogAspect.pointcut()")
    public void logEnd(JoinPoint joinPoint){
        // 方法名称
        String methodName = joinPoint.getSignature().getName();
        log.debug(methodName + "方法运行后");
    }

    @AfterReturning(value = "pointcut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result){
        // 方法名称
        String methodName = joinPoint.getSignature().getName();
        if (null != result)
            log.debug(methodName + "方法运行结束,结果" + result.toString());
        else
            log.debug(methodName + "方法运行结束");
    }

    @AfterThrowing(value = "pointcut()", throwing = "exception")
    public void logReturn(JoinPoint joinPoint, Exception exception){
        // 方法名称
        String methodName = joinPoint.getSignature().getName();
        log.debug(methodName + "方法运行异常", exception);
    }

}

猜你喜欢

转载自blog.csdn.net/u012562117/article/details/80255847