delegate模式
委派模式,一种动态选择执行类的模式。
类图
以spring的应用监听器为例
ApplicationListener接口定义了应用事件
FileEncodingApplicationListener和DelegatingApplicationListener 实现了这个接口
类图:
下面是接口代码和实现类代码,实现类部分细节已省略
// 接口
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
// 委派模式实现类
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
// 略
}
// 略
// 略的两部分就是将Listenr遍历执行的详细代码
}
@SuppressWarnings("unchecked")
// 就是遍历配置参数取监听器
private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
if (environment == null) { return Collections.emptyList();}
String classNames = environment.getProperty(PROPERTY_NAME);
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
try {
Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationListener.class, clazz,
"class [" + className + "] must implement ApplicationListener");
listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
}
catch (Exception ex) {
throw new ApplicationContextException("Failed to load context listener class [" + className + "]",
ex);
}
}
}
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}
}
// 一般实现类
// 看类名和代码可以明显看出是做和文件编码相关的工作
public class FileEncodingApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
private static final Log logger = LogFactory.getLog(FileEncodingApplicationListener.class);
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
if (!environment.containsProperty("spring.mandatory-file-encoding")) {
return;
}
String encoding = System.getProperty("file.encoding");
String desired = environment.getProperty("spring.mandatory-file-encoding");
if (encoding != null && !desired.equalsIgnoreCase(encoding)) {
logger.error("System property 'file.encoding' is currently '" + encoding + "'. It should be '" + desired
+ "' (as defined in 'spring.mandatoryFileEncoding').");
logger.error("Environment variable LANG is '" + System.getenv("LANG")
+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL")
+ "'. You could use a locale setting that matches encoding='" + desired + "'.");
throw new IllegalStateException("The Java Virtual Machine has not been configured to use the "
+ "desired default character encoding (" + desired + ").");
}
}
}
首先delegate是一种模式,他本身不会作为原始的父类接口,通常是实现某个接口的实现类。
此例中ApplicationListener是一个应用监听器,定义了一个接口是对应用事件的监听。
委派实现类相对其他实现类的几个特点
- 委派类的类名看不出实际职责,但是像FileEncodingApplicationListener可以一眼看出来与文件编码有关
- 委派类的实际代码没有做实际的业务,业务是交给某些其他实现类去做的,而FileEncodingApplicationListener的职责是固定的,不管环境怎么变化,他做的都是文件编码的工作
- 如果使用委派模式的话,他里面的list可以既包含FileEncodingApplicationListener,又包含其他监听器,一个委派执行了多个监听逻辑
通常spring中使用委派模式的时候都会从入参或者其他配置参数中取一个list<? extends ApplicationListener>,然后遍历执行这个list,每个迭代项在执行一遍onApplicationEvent方法。
这样整个项目的动态配置性就很高。
其他类似的场景
spring security 的 DelegatingAccessDeniedHandler,DelegatingAuthenticationFailureHandler
实际应用
考虑集团系统现有一个登录机制。但是出现一个需求需要把所有的集团应用都整合到某个单点登录系统中。
考虑到系统的平滑迁移,用户希望能逐步的适应新的登录机制。
系统使用的验证框架是shiro,shiro没有委派验证模式(spring security有)。
于是新增一个简单的委派密码校验类:
public class DelegatingCredentialsMatcher extends SimpleCredentialsMatcher{
private List<CredentialsMatcher> credentialsMatchers ;
public DelegatingCredentialsMatcher(List<CredentialsMatcher> matchers) {
this.credentialsMatchers = matchers;
}
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
for(CredentialsMatcher matcher : this.credentialsMatchers) {
if(matcher.doCredentialsMatch(token, info)) {
return true;
}
}
return false;
}
}
// 初始化shiroRelam
public NewShiroRealm() {
super();
ACredentialsMatcher aCredentialsMatcher = new ACredentialsMatcher();
BCredentialsMatcher bCredentialsMatcher = new BCredentialsMatcher();
setCredentialsMatcher(new DelegatingCredentialsMatcher(Lists.newArrayList(aCredentialsMatcher , bCredentialsMatcher )));
}
即可满足双重校验