问题:
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()的结果即可。