aop三个坑:失效、注入对象为空、方法固定返回null

问题:
DataMigrateService中两个方法firstMonitor,getResult
DataMigrateService被配置了aop,但是执行发现,如果实在另一个类中分别调用firstMonitor,getResult,
两个方法都活经过aop;
但是如果job中调用firstMonitor,firstMonitor再调用getResult发现,getResult不经过aop如下:

@Service
public class DataMigrateService {

    @Datasource(datasourceType = DataSourceConstant.NULL_BI)
    public void secondMonitor() {
        // 注意 这里的方法getResult对应的aop不会生效
        ArrayList<MigrateResultDto> resultDtos = this.getResult();
    }

    @Datasource(datasourceType = DataSourceConstant.ZPBI_BI)
    public ArrayList<MigrateResultDto> getResult(List<String> tableNames){
          return new ArrayList<MigrateResultDto>() ;
    }
}

解决
三个方案分别如下:
1 理论上最佳方案:修改类,不要出现“自调用”的情况:这是Spring文档中推荐的“最佳”方案;

2 实践之最佳方案,“自调用”,:

@Service
public class DataMigrateService {
    // 注入自己
    @Autowired
    DataMigrateService service;

    @Datasource(datasourceType = DataSourceConstant.NULL_BI)
    public void secondMonitor() {
        // 注意 这里service是必须的,否则不会走aop;这里注入的service会走动态代理
        ArrayList<MigrateResultDto> resultDtos = service.getResult();
    }

    @Datasource(datasourceType = DataSourceConstant.ZPBI_BI)
    public ArrayList<MigrateResultDto> getResult(List<String> tableNames){
          return new ArrayList<MigrateResultDto>() ;
    }
}

理解原因:
为什么通过Autowired注入的类方法调用一定会使用aop,但是直接this调用的方法没有经过aop?
我们知道aop都是经过动态代理来实现的,而动态代理管理的范围也一定是spring容器中的对象所调用的方法,即autowired进来的对象调用方法时,实际调用的是代理方法,然后代理方法里面会调用DataMigrateService类自身方法
针对this直接调用的方法会直接调用别代理对象的方法,这个this就是DataMigrateService直接调用自己方法;,所以不会在经过aop
代理原理参考:https://blog.csdn.net/h2604396739/article/details/83096696

3 若一定要使用“自调用”,那么
1)spring的aop配置增加:<aop:aspectj-autoproxy expose-proxy="true" />,如:
    <aop:aspectj-autoproxy expose-proxy="true"/>
    <aop:config>
        .......
    </aop:config>
在secondMonitor方法中调用getResult:((DataMigrateService) AopContext.currentProxy()).getResult();

2 方法为private  Autowired的mapper为null?改为public就可用了

aop只能适用于public 修饰符修饰的方法

原因解释:
见 :https://www.cnblogs.com/lcngu/p/6246950.html
SpringAOP导致@Autowired依赖注入失败
SpringAOP private @Autowired失败 原因分析

此处Method[] methods = clazz.getMethods();只能拿到public方法。该方法拿不到,导致不会走代理,那直接调用本类的方法,
那为什么Autowired的对象会空呢?难道这里的对象不是

参考:https://www.jianshu.com/p/6534945eb3b5

3 切面类书写问题导致切点方法只能返回null

Job:

@Component
public class MenuSyncJob {
    private static Logger logger = LoggerFactory.getLogger(MenuSyncJob.class);

    public String getString() {
        return "a";
    }
    // 下面还有别的定时任务,这里省略。。。
}

切面类:

@Aspect
@Component
public class JobLockAspect {

    @Value("${passport.token}")
    private String token;
    static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(DataSourceAspect.class);

    @Autowired
    private ICacheService redisService;
 
    public void  cacheLockPoint(ProceedingJoinPoint joinPoint) {
        try {
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            // 被拦截的方法
            Method method = methodSignature.getMethod();
            // 获取被拦截方法上面的 @JobLock注解的内容
            if (method.getAnnotation(JobLock.class) == null){
                joinPoint.proceed();
                return;
            }
            // do something
            joinPoint.proceed();
            return;
        } catch (Exception e) {
            log.error("定时任务执行失败", e);
        } catch (Throwable e) {
            log.error("定时任务执行失败", e);
        }
    }
}

Contrller类:

@Controller
@RequestMapping(value = "/data")
public class DataController {
    private static Logger logger = LoggerFactory.getLogger(DataController.class);
    @Autowired
    MenuSyncJob menuSyncJob;

    @RequestMapping(value="/test.action")
    public String test() {
        String str = menuSyncJob.getString();
        System.out.println(str);
        List<Menu> rootMenus = menuSyncJob.getRootMenus(0);
        return "Login";
    }
}

页面访问:
http://localhost:8080/rsbi/data/test.action
会发现controller中menuSyncJob.getString()返回的一直是null,但是MenuSyncJob中的方法返回了值;
你仔细看下就会发现,因为aop的方法返回是空,问题代码如下:
                joinPoint.proceed();
                return;
第一行执行的有结果,但是aop处理的时候吧执行的结果丢掉了。

解决方案:
1 修改aop中方法有返回值,类型设置为object,并返回joinPoint.proceed()的结果即可。


 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值