上下文模式
概述:在方法的调用链需要一个上下文来贯穿整个调用链,主要通过它来存储数据和传递数据。通常的上下文模式如下所示(下面也给出了ThreadLocal进行上下文的改造):
1.创建ExcutionTask主要用来执行任务
public class ExcutionTask implements Runnable {
private final QueryForDBAction queryForDBAction = new QueryForDBAction();
private final HttpQueryAction httpQueryAction = new HttpQueryAction();
@Override
public void run() {
final Context context = new Context();
//查询名称
queryForDBAction.execute(context);
System.out.println(Thread.currentThread().getName()+ " 查询的姓名为: "+context.getName());
//根据名称查询身份证
httpQueryAction.execute(context);
System.out.println(Thread.currentThread().getName() + " 查询的身份证为 " + context.getCardId());
}
}
2.创建执行的上下文Context。一个完整的请求分为两步,一个向数据库查询数据,一个向http请求数据。数据库查询当前的name,将name作为参数传给http,这里不直接传参而是直接传context上下文,context里存放了name
public class Context {
private String name;
private String cardId;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getCardId() {
return this.cardId;
}
}
3.创建QueryForDBAction模拟从数据库查询数据
public class QueryForDBAction {
public void execute(Context context){
try {
String name = "zs";
Thread.sleep(2000);
context.setName(name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.创建HttpQueryAction模拟从网络上获取数据
public class HttpQueryAction {
public void execute(Context context) {
try {
String name = context.getName();
String cardId = queryCardId(name);
Thread.sleep(2000);
context.setCardId(cardId);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String queryCardId(String name) {
return "46513498765131";
}
}
5.测试类编写
public class Application {
public static void main(String[] args) {
IntStream.range(1,5).forEach(i ->{
new Thread(new ExcutionTask()).start();
});
}
}
不足与改进
在这里因为ExcutionTask都需要一个上下文context,可以不传参,考虑通过一个类直接获取即可,让每个线程都有自己的context–可以使用ThreadLocal设置默认的context即可。这里的ActionContext可以设置为单例。
这样不需要考虑线程安全性问题
public class ActionContext {
private static final ThreadLocal<Context> THREAD_LOCAL = new ThreadLocal<Context>(){
@Override
protected Context initialValue() {
return new Context();
}
};
private ActionContext(){
}
private static class Inner{
private static final ActionContext actionContext = new ActionContext();
}
public static ActionContext getInstance(){
return Inner.actionContext;
}
public Context getContext(){
return THREAD_LOCAL.get();
}
}
这样之前new Context的地方就可以换成使用ActionContext.getInstance.getContext()就可以了
分析如下
这样在方法调用时因为是同一个线程,这样不需要传递context了,只需要随时用随时getContext()皆可以了,因为ThreadLocal里面同一个线程拿到的Context是同一个。