策略模式是一种比较简单的模式。一般来说,我们可以根据不同的任务类型,来选择不同的执行策略。
一般策略模式
对于Java语言来说,一般来说可以简化如下:
if ("01".equals(type)) {
firstStrategy.execute();
} else if ("02".equals(type)) {
secondStrategy.execute();
}
firstStrategy和secondStrategy为策略类,其execute方法封装了处理逻辑。
在策略模式的处理逻辑中,可以根据type来选择不同的策略类。但是一旦策略类比较多,调用处的if…else…就会特别多,也特别不优雅。
对此,如果使用Spring,利用IOC,可以比较优雅的解决这个问题。
Spring+策略模式
先来看看spring策略模式的套路是如何实现的吧~
类结构如下
定义策略的接口
- StrategyService
public interface StrategyService {
/**
* 定位策略的key
*
* @return key
*/
String fetchkey();
/**
* 具体策略执行的key
*/
void execute();
}
定义两个策略实现类
- FirstStrategyImpl
@Component
public class FirstStrategyImpl implements StrategyService {
@Override
public String fetchkey() {
return "01";
}
@Override
public void execute() {
System.out.println("FirstStrategy executed!");
}
}
- SecondStrategyImpl
@Component
public class SecondStrategyImpl implements StrategyService {
@Override
public String fetchkey() {
return "02";
}
@Override
public void execute() {
System.out.println("SecondStrategy executed!");
}
}
最后再来一个策略处理类
- StrategyHandler
/**
* 策略统筹类
*
* @author :hewie
* @date :Created in 2020/3/21 17:35
*/
@Component
public class StrategyHandler implements InitializingBean, ApplicationContextAware {
private Map<String, StrategyService> strategyServiceMap = new ConcurrentHashMap<>(8);
private ApplicationContext applicationContext;
/**
* 将StrategyService的类都按照定义好的规则(fetchKey),放入strategyServiceMap中
*/
@Override
public void afterPropertiesSet() {
Map<String, StrategyService> matchBeans = applicationContext.getBeansOfType(StrategyService.class);
matchBeans.forEach((key, value) -> strategyServiceMap.put(value.fetchkey(), value));
}
/**
* 注入applicationContext
*
* @param applicationContext ac
* @throws BeansException e
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 通过key获取对应的策略实现
*
* @param key key
* @return strategyService
*/
public StrategyService getStrategy(String key) {
return strategyServiceMap.get(key);
}
}
好了,这样就完成了。
测试如下:
import cn.hewie.mapi.DemoMapiWebApplication;
import cn.hewie.mapi.util.strategy.StrategyHandler;
import cn.hewie.mapi.util.strategy.StrategyService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author :hewie
* @date :Created in 2020/3/21 17:48
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {DemoMapiWebApplication.class})
public class StrategyTest {
@Autowired
private StrategyHandler strategyhandler;
@Test
public void test() {
String type = "02";
StrategyService strategy = strategyhandler.getStrategy(type);
strategy.execute();
}
}
结果,策略二调用成功!
SecondStrategy executed!
以上就是spring策略模式的一般套路了。
可以发现,使用spring版的策略模式,扩展起来也非常容易:只需在impl目录下添加策略类,定义好对应的策略key,就可以了
原理
以上只是现象,是皮毛,现在让我们抽丝剥茧,看看spring到底帮我们做了什么,为什么我们可以这么玩?
关键在于 StrategyHandler 这个类!它实现了InitializingBean和ApplicationContextAware两个接口。那么这两个接口有什么用呢?
- 实现了ApplicationContextAware接口的类,可以注入applicationContext实例
具体来说,实现了ApplicationContextAware接口的类,会重写方法
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
可以通过此方法,让当前类拿到applicationContext实例。
当然,在Spring中,你也可以通过注解来获取,结果都一样~
@Autowired
private ApplicationContext applicationContext;
- 实现了InitializingBean的类,会在实例化Bean的时候,调用其afterPropertiesSet方法。
So,在实例化此Bean的时候,我们调用了afterPropertiesSet方法,方法里通过applicationContext对象,拿到所有StrategyService类型的类。然后将每个策略类fetchKey结果作为key,策略类本身作为value,存储到变量strategyServiceMap中了。然后当要使用的时候,直接调用getStrategy方法,根据key来获取策略类。就是这么简单~
撸一下源码
那么spring源码是如何实现的呢?实例化bean和注入applicationContext是什么时候完成的呢?下面具体看一下
ps:spring源码版本(5.0.x)
- 容器启动阶段,进行了ApplicationContextAwareProcessor的注册
ApplicationContextAwareProcessor你可能比较陌生,但是它和applicationContext的注入息息相关。applicationContext就是靠ApplicationContextAwareProcessor注入我们的StrategyHandler的。这里我们先不看Bean实例化的过程,先看看这个ApplicationContextAwareProcessor是如何注入的吧。
在spring启动的refresh()方法中,对BeanFactory进行各种功能填充(prepareBeanFactory())时,代码中硬编码添加了一个ApplicationContextAwareProcessor类
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 对BeanFactory进行各种功能填充
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 以下省略...
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 硬编码注册了ApplicationContextAwareProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 以下代码省略...
另外,注意ApplicationContextAwareProcessor实现了BeanPostProcessor接口
- Bean实例化时,会进行Bean的初始化,完成StrategyHandler的初始化
最外层方法在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,整理流程如下
下面结合代码具体来看看
在doCreateBean方法中,属性填充完成后,调用了initializeBean方法
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
追踪进入到initializeBean方法,可以看到如下代码
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// $-- 对特殊的bean处理:Aware、BeanClassLoaderAware、BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// $-- 应用后处理器applyBeanPostProcessorsBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// $-- 调用用户定义的init-method方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// $-- 应用后处理器applyBeanPostProcessorsAfterInitialization
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
在应用后处理器applyBeanPostProcessorsBeforeInitialization方法中,会获取到所有的BeanPostProcessor类,然后调用其postProcessBeforeInitialization方法
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
我们上面说到,spring容器启动的时候,手动注入了一个ApplicationContextAwareProcessor类,它也是BeanPostProcessor的实现类。我们的StrategyHandler调用到此处时,会调用ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,可以看到如下代码
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
好吧,它又调用了一个invokeAwareInterfaces方法,再去看一看
private void invokeAwareInterfaces(Object bean) {
// $-- 实现了Aware接口的bean,可以拿到相应的信息
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
抽丝剥茧,终于看到了调用方法了!因为我们的StrategyHander实现了ApplicationContextAware方法,所以类的实例化进程执行到此处时,会将applicationContext注入
那么afterPropertiesSet()又是什么时候调用的呢?
别急,回头看看上面的initializeBean方法,在应用了后处理器applyBeanPostProcessorsBeforeInitialization后,会调用invokeInitMethods方法,该方法如下:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// $-- 如果是InitializingBean,则需要调用afterPropertiesSet方法
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// $-- 调用自定义初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
从方法里,我们终于可以看到熟悉的afterPropertiesSet方法了。在注入了applicationContext之后,我们紧接着在调用初始化方法时,就调用了afterPropertiesSet方法。
至此,spring策略模式的这一套就清清楚楚的展现在我们眼前了!
spring+策略模式套路get~