spring中的切面和异步执行
1、首先理解异步和多线程的概念,怎么实现接口的异步调用呢?多线程,这是很多人第一眼想到的关键词,没错,多线程就是一种实现异步调用的方式!
2、下面介绍怎么实现异步调用方式
3、首先如果你的项目是springboot项目那么只需要在想要调用的方法上加上异步注解@Async就可以了(前提启动类上要加上启用异步注解的注解@EnableAsync如果不开启那么@Async是无效的)还有一点需要注意:那就是在同一个类里面调用加@Async注解的方法是无效的,举个栗子:在类Test里面有a和b两个发放,b方法加了开启异步注解,这样如果a方法调用b方法异步并没有执行,那么为什么呢?
难道是代码写错了?想起spring对@Transactional注解时也有类似问题,spring扫描时具有@Transactional注解方法的类时,是生成一个代理类,由代理类去开启关闭事务,而在同一个类中,方法调用是在类体内执行的,spring无法截获这个方法调用。
针对这个切面问题要重点聊聊了:
1、首先要知道spring的aop切面的原理是什么?
答:Spring是采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑,也就是切面才可以切进去,才能执行切面里编写的代码。
而在同一个class中,方法B调用方法A,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用。
2、所以想必你大致也明白了为什么在同一个类里面一个方法调用另外一个带有@Async的方法不起作用了,不仅仅是这个,同一个类里面一个方法调用另外一个开启事物(@Transactional)的的方法,事物也是不起作用的。
3、自定义的注解在同一个类里面方法调用也是不起作用的,总的来说同一个类里面总的来说只要是同一个类里面的方法间调用,那么切面都是无效的,aop都无法切入进入,
4、解决办法用AopContext.currentProxy()获取一个代理类,然后用代理类再去调用就好了
代码示例:
@Service
public class CmpActivityServiceImpl implements CmpActivityService {
@Autowired
private ActivityRepository activityRepository;
@Async
@TargetDataSource(DataSourceEnum.CMP_DB)
@Override
public void dynamicDb(Activity activity){
/**
* 获取本对象的代理对象,再进行调用这个类里面的其他方法,那么被调用的那个方法上面如果有切面
* 那么切面就可以生效了,本代码示例就是在开启事物之前先动态切换数据源,
* 这样下面的addCpmActivity上的@Transactional开启事物方法就可以生效了,
* 动态切换数据源要注意一个问题,就是在开启事物之前要先切换成需要的数据源,不要在开启事物
* 之后在切换数据源不然会切换失败,因为一个事物的开启是建立在与一个数据源建立连接的基础上开启的
* 所以如果先开启事物然后再切换数据源会报错,切换会失败
*/
((CmpActivityServiceImpl)AopContext.currentProxy()).addCpmActivity(activity);
}
/**
* 要注意 @Transactional是否是生效的
* @param activity
*/
@Transactional
@Override
public void addCpmActivity(Activity activity) {
}
}
注意:在类中使用AopContext.currentProxy()时会报如下错误:
Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to
解决办法需要再启动类或者使用切面的类上加上注解
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)即可解决
这里简单介绍一下两个参数,一个是控制aop的具体实现方式,为true 的话使用cglib,为false的话使用java的Proxy,默认为false,第二个参数控制代理的暴露方式,解决内部调用不能使用代理的场景,默认为false.
4、如果不是springboot项目,所用的框架中没有@Async注解,那么只能自己写多线程的方式来实现异步执行了,下面提供两种常用的方法
/**
* 第一种方式
*/
Thread t = new Thread() {
@Override
public void run(){
/**
* 这里面写要异步执行的代码,或者想要异步调用的方法
*/
};
};
t.start();//启动该线程
/**
* 第二种方式
*/
Runnable runnable = new Runnable(){
@Override
public void run() {
/**
* 这里面写要异步执行的代码,或者想要异步调用的方法
*/
}
};
new Thread(runnable).start();//线程启动
/**
* 或者这样写
*/
Runnable runnable2=()->{
/**
* 这里面写要异步执行的代码,或者想要异步调用的方法
*/
};
new Thread(runnable2).start();
还没有评论,来说两句吧...