策略模式是一種比較簡單的模式。一般來說,我們可以根據不同的任務類型,來選擇不同的執行策略。
一般策略模式
對於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~