Async 拦截器

作用: 在方法上加上@Async()注解就能使该方法变为异步方法,就会开启新的线程去执行

注意:  适用于非controller层

Async注解定义:

package com.future.common.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;


@Retention(RUNTIME)
@Target(METHOD)
public @interface Async {}

拦截器代码:

package com.future.common.interceptor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.future.common.annotation.Async;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;

public class AsyncInterceptor implements Interceptor {
	private static ExecutorService cachedPool = Executors.newCachedThreadPool();

	@Override
	public void intercept(Invocation inv) {
		if (inv.getMethod().getAnnotation(Async.class) != null) {
		        //无返回值方法
			if (inv.getMethod().getReturnType().equals(Void.TYPE)) {
				cachedPool.submit(() -> {
					inv.invoke();
				});
                        //需要返回值方法
			} else {
				@SuppressWarnings("unchecked")
				Future<Object> future = cachedPool.submit(() -> {
					inv.invoke();
					return ((Future<Object>) inv.getReturnValue()).get();
				});
				inv.setReturnValue(future);
			}
			return;
		} else {
			inv.invoke();
		}
	}

}

无返回值方法使用:

	@Async()
	public void validateUser(String usernameOrPhone, String password) {
		SysUser user = this.sysUserDao.findUserByUsernameOrPhone(usernameOrPhone);
		if (user == null) {
			throw new AuthenticationException("不存在用户名为[" + usernameOrPhone + "]的用户!");
		}
		if("0".equals(user.getSfyxdl()) || StrKit.isBlank(user.getSfyxdl())){
			throw new AuthenticationException("该员工不允许登录系统!");
		}
		String passwordDb = user.getPassword();
		if (passwordDb == null || !passwordDb.equals(SecureUtil.md5(password))) {
			throw new AuthenticationException("密码错误!");
		}
	}

有返回值方法使用:

注意返回值要是Futrue类型的

	@Async()
	public Future<SysUser> findUserById(String userid) {
		if(StrKit.isBlank(userid)){
			throw new AppException("用户信息为空!");
		}
		return CompletableFuture.completedFuture(this.sysUserDao.findById(userid));
	}

调用者获取返回值:

Future<SysUser> user = this.sysUserService.findUserById(userid);
SysUser u = user.get();


评论区

JFinal

2019-09-08 18:19

这里实现异步的意义是什么呢? 是为了尽快让客户端得到响应?

如果是为了让客户端尽快得到响应, inv.invoke() 后面应该还要添加一句:
inv.getController().renderNull();

因为在新原线程响应客户端以后, response 已经有 render 动作向其输出信息了,如果异步线程中再次对 response 输出信息会出问题

快乐的蹦豆子

2019-09-08 18:23

忘了加一句话了: 适用于非controller层

快乐的蹦豆子

2019-09-08 18:24

@JFinal 意义就是把耗时比较长的方法可以用异步的方式去处理,不阻塞主线程

山东小木

2019-09-08 18:34

可以具体举例说明一下

快乐的蹦豆子

2019-09-08 19:40

举个简单的例子 serviceA 需要调用serviceB,C,D
B耗时2s C耗时3秒 D耗时5秒
如果同步执行BCD那么耗时会是2+3+5=10s
如果改造成异步方法
耗时应该是5s多点
这在服务执行慢慢的时候是一种优化的有效方法

chcode

2019-09-08 20:11

应该是模仿spring的异步注解的功能

快乐的蹦豆子

2019-09-08 20:33

@chcode 是的,看着挺方便的

JFinal

2019-09-08 22:29

@快乐的蹦豆子 有个改进建议,返回值应该是不需要关注的,因为 inv.invoke() 这个方法内部已经处理好了返回值并且将返回值放入了 returnValue 变量之中

此外,如果上面的建议如果测试后有问题,也只需要改成只保留处理返回支持的那个 if 分去,因为无返回值的时候得到的是 null 值,而调用 inv.setReturnValue(future) 正好也是 null 值

记得验证后再回来分享一下

我前面的建议就是只留下面的代码:
cachedPool.submit(() -> {
inv.invoke();
});

万一有问题,只留下面的代码:
Future《Object》 future = cachedPool.submit(() -》 {
inv.invoke();
return ((Future《Object》) inv.getReturnValue()).get();
});
inv.setReturnValue(future);

要输就输给追求

2019-09-09 10:48

想法挺好,不过已经有做得很完善的JFinal-event了

快乐的蹦豆子

2019-09-09 12:24

@JFinal 好的,波总

快乐的蹦豆子

2019-09-09 13:37

@要输就输给追求 根本就不是一回事

快乐的蹦豆子

2019-09-09 15:39

@JFinal
cachedPool.submit(() -> {
inv.invoke();
});
在外部获取值的时候会报null pointer,因为异步线程还没有执行完,主线程就去取return value了

Future《Object》 future = cachedPool.submit(() -》 {
inv.invoke();
return ((Future《Object》) inv.getReturnValue()).get();
});
inv.setReturnValue(future);

这种情况针对void方法, return ((Future《Object》) inv.getReturnValue()).get(); 这句话会报null pointer