一、介绍
策略模式(Strategy):针对一组算法,将每一个算法封装到具有共同接口的独立的类中,使得它们可以互换。
使用策略模式可以把行为和环境分割开来。
本质:分离算法,选择实现。
策略模式有下面几个部分:
环境(Context):有一个Strategy类的引用,和具体的策略类交互。
抽象策略(Strategy)角色:一个接口或抽象类,给出规范。
具体策略(ConcreteStrategy)角色:具体算法或行为。
二、代码
首先,先定义一个策略接口:
public interface Strategy {
public void draw(int radius, int x, int y);
}
然后我们定义具体的几个策略:
public class RedPen implements Strategy {
@Override
public void draw(int radius, int x, int y) {
System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
}
}
public class GreenPen implements Strategy {
@Override
public void draw(int radius, int x, int y) {
System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
}
}
public class BluePen implements Strategy {
@Override
public void draw(int radius, int x, int y) {
System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
}
}
使用策略的类:
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeDraw(int radius, int x, int y){
return strategy.draw(radius, x, y);
}
}
客户端演示:
public static void main(String[] args) {
Context context = new Context(new BluePen()); // 使用绿色笔来画
context.executeDraw(10, 0, 0);
}
三、源码
1、ThreadPoolExecutor
创建线程池时,会调用 ThreadPoolExecutor
的构造函数 new 一个对象,在构造函数中需要传入七个参数,其中有一个参数叫 RejectedExecutionHandler handler
也就是线程的拒绝策略。
public class ThreadPoolExecutor extends AbstractExecutorService {
// 成员变量
private volatile RejectedExecutionHandler handler;
// 构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
// 拒绝任务
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
}
传入拒绝策略之后将对象赋给 ThreadPoolExecutor
对象的成员变量 handler
,在需要对加入线程池的线程进行拒绝时,直接调用 RejectedExecutionHandler
中的 reject
方法即可,方法内部调用传入 handler 的 rejectedExecution
方法。
但是 RejectedExecutionHandler
是一个接口,也就是说我们需要传入具体的实现,这里便是使用的策略模式。RejectedExecutionHandler
接口对应 Strategy 接口,下面四种实现类对应具体策略;RejectedExecutionHandler
对应 Context 类,外部调用 RejectedExecutionHandler
的 reject 方法,再由 RejectedExecutionHandler
内部调用具体策略实现的方法。
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
2、Spring MVC中 DispatcherServlet
DispatcherServlet
在进行转发前需要进行传说中的九大件的初始化,其中去初始化时除了 initMultipartResolver
(上传文件)没有获取 Properties defaultStrategies;
默认策略,其他的八大件都会使用到策略模式。先看一下 defaultStrategies
为 java.util.Properties
类型,其实就是个 Map,定义如下:
public class Properties extends Hashtable<Object,Object> {
// ...
}
流程梳理:
1、当Web容器启动时,ServletWebServerApplicationContext
初始化会调用其refresh()方法,则会调用 DispatcherServlet
的 onRefresh
方法
2、onRefresh
方法 - > initStrategies
方法 -> 初始化九大件
3、初始化时则会调用 getDefaultStrategy
方法。
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
这几个方法的实际基本都是差不多的,这边以 initLocaleResolver
为例
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Detected " + this.localeResolver);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException var3) {
this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No LocaleResolver 'localeResolver': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
进入 getDefaultStrategy
方法
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = this.getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException("DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
} else {
return strategies.get(0);
}
}
再往下
private static final Properties defaultStrategies;
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
// 从defaultStrategies获取指定的值
String value = defaultStrategies.getProperty(key);
if (value == null) {
return new LinkedList();
} else {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList(classNames.length);
String[] var7 = classNames;
int var8 = classNames.length;
for(int var9 = 0; var9 < var8; ++var9) {
String className = var7[var9];
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = this.createDefaultStrategy(context, clazz);
strategies.add(strategy);
} catch (ClassNotFoundException var13) {
throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);
} catch (LinkageError var14) {
throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var14);
}
}
return strategies;
}
}
小结: web容器启动,ServletWebServerApplicationContext 的refresh方法 间接调用到 DispatcherServlet的初始九大件方法, 其中八大件在没有自定义实现的情况下,调用默认的 配置。 而默认配置则是在 DispatcherServlet的静态代码块中,由Spring的ClassPathResource将配置文件DispatcherServlet.properties中的配置加载进一个 Map容器中。只待初始化九大件时,根据不同的九大件类型作为key,调用相应的实现。
四、SpringBoot中使用策略模式
1、接口
public interface PayResultService {
public static final String BEAN_NAME_PREFIX = "payResultService_";
PayCallbackVO dealPayResult(CommandPayDTO commandPayDTO);
}
2、实现类
/**
* 支付成功
*/
@Service(PayResultService.BEAN_NAME_PREFIX + PayStatus.PAY_SUCCEED_STATUS)
public class PayResultSucceedService implements PayResultService {
}
/**
* 支付失败
*/
@Service(PayResultService.BEAN_NAME_PREFIX + PayStatus.PAY_FAIL_STATUS)
public class PayResultFailService implements PayResultService {
}
3、使用
@Autowired
private Map<String, PayResultService> payResultServiceMap;