委派模式的应用

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 )));
	}

即可满足双重校验

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值